简体   繁体   中英

EF Core Many-to-Many hide pivot

I'm still learning .NET Core 2.1. I'm working on a Web API where I use EF Core. Currently I'm working on a many to many relationship between Users and Roles. I wanted to hide the pivot, but it ended out being a bit hacky i think, so I wanted to see what I could do to improve it.

I started out with something like this:

public class User
{
    public int Id { get; set; }
    public string UserName { get; set; }

    public virtual ICollection<UserRole> UserRoles { get; set; }
}

public class Role
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<UserRole> UserRoles { get; set; }
}

public class UserRole
{
    public int UserId { get; set; }
    public int RoleId { get; set; }
}

That works just fine, I then wanted to add an IEnumerable<Role> to the user for easier accessibility and for a prettier JSON output. I found an article online which did that like this:

public class User
{
    // All of the previous code
    [NotMapped]
    public virtual IEnumerable<Role> Roles => UserRoles.Select(x => x.Role);
}

I can then get users and roles:

_context.Users.Include(x => x.UserRoles).ThenInclude(y => y.Role)

The thing is, sometimes, I only wish to get the users without the roles:

_context.Users

That makes the program crash, as UserRoles is null , then .Select(x => x.Role) will fail.

My fix to the User class was the following:

public class User
{
    public virtual IEnumerable<Role> Roles
    {
        get
        {
            if (UserRoles == null) return null;
            return UserRoles.Select(x => x.Role);
        }
    }
}

But to me, that is a really hacky and ugly solution to the problem. I just don't know how I can simplify it. I tried doing something like

public virtual IEnumerable<Role> Roles => UserRoles.Select(x => x?.Role);

I want to have something just as simple as the above line, but it should actually work as intended.

尝试这个:

public virtual IEnumerable<Role> Roles => UserRoles?.Select(x => x.Role);

I always do initialize any collection in empty constructor ( for EF ) and always call it from any other. As an example:

 public class User
    {
        public int Id { get; set; }
        public string UserName { get; set; }

        public virtual ICollection<UserRole> UserRoles { get; set; }

        public IEnumerable<Role> Roles => UserRoles.Select(x => x.Role);

        public User()
        {
            this.UserRoles = new List<UserRole>();
        }

        public User(string name)
            :this()
        {

        }
    }

Whenever you will include the roles collection will be fill else you will always have empty collection so any operation wont fail.

Also you do not neet [NotMapped] attribute since the property is readonly and EF will know that

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM