bin
bin

Reputation: 550

Entity Framework Core : how to map multiple objects of the same type

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

Answers (1)

Smit
Smit

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

Related Questions