Reputation: 550
I have two classes, one for User
, and one for Warn
that a said user can receive, and I'd like to map them, but I'm having an issue with doing so.
using System;
using System.Collections.Generic;
using System.Linq;
namespace web
{
public class User
{
public User()
{
}
public ICollection<MuteWarn> Mutes { get; set; }
public ICollection<Warn> Warns { get; set; }
public ICollection<Ban> Bans { get; set; }
public ICollection<Kick> Kicks { get; set; }
public int Id { get; set; }
public ulong DiscordId { get; set; }
public bool AntiBan { get; set; }
public ICollection<string> Permissions { get; set; }
public Level Level { get; set; }
public Mute CurrentMute { get; set; }
public string Name { get; set; }
public int Discrim { get; set; }
public string AvatarUrl { get; set; }
public Guild Guild { get; set; }
public bool HasPermission(string perm)
{
var res = Permissions.Where(x => x == perm);
if(res != null) return true;
return false;
}
}
}
This is the User
class, and here is the Warn
class:
using System;
namespace web
{
public class Warn
{
public Warn()
{
Moderator = DataHelper.GetDefaultModerator();
}
public int Id { get; set; }
public string Reason { get; set; }
public DateTime Timestamp { get; set; }
public User Moderator { get; set; }
public User User { get; set; }
public Guild Guild { get; set; }
}
}
As you can see there are two objects of type User
in this class, and I'd like to access them from the User class like so:
foreach(var w in User.Warns)
{
Console.WriteLine(w.Reason);
Console.WriteLine(w.Moderator.Name);
}
and etc.
The moderator and user are two separate users (even though they can be the same person).
The Moderator is the person who issues this warn, and the User is the user who receives the warn.
Each user can have an unlimited number of warns.
For some reason this will not build because it cannot find the relationship between Moderator and User.
Please do help, thank you.
Upvotes: 3
Views: 3279
Reputation: 2459
EF Core has conventions to figure out parts of metadata based on structure of domain classes. Generally navigation property represents existence of relationship between declaring type and target type. And if there is navigation in target type of declaring type then they are considered navigation pair defining the relationship. Though there is limitation in terms of finding pair of navigations which constitute relationship. If there is ambiguity then EF Core will not create relationships. In your case User
class has one navigation Warns
which has target type Warn
but class Warn
has 2 navigations of type User
- Moderator
& User
. Now EF cannot figure out which navigations are part of single relationship. So this needs user configuration to define relationship hence build the model correctly.
Assuming User.Warns
& Warn.User
are part of 1 relationship since Warns contains all warnings user get so Warn.User
should point to User
. And Warn.Moderator
is separate relationship without inverse navigation. There are 2 ways to configure this.
Data annotations
You can use InverseProperty
attribute on your navigation to tell EF Core what is the inverse navigation for this navigation. In your case, either of this would fix the issue.
[InverseProperty("User")]
public ICollection<Warn> Warns { get; set; }
or
[InverseProperty("Warns")]
public User User { get; set; }
This would resolve the ambiguity and help EF to find out the pairs so convention will create relationships.
Fluent API
You can use fluent api to configure relationship explicitly. You need to put this code in your DbContext.OnModelCreating
method.
modelBuilder.Entity<User>().HasMany(e => e.Warns).WithOne(e => e.User);
or
modelBuilder.Entity<Warn>().HasOne(e => e.Moderator).WithMany();
Again defining any of above clears the ambiguity and model builds correctly.
For more details on how to configure relationships you can read in documentation
Upvotes: 5