ibubi
ibubi

Reputation: 2549

Ef core many to many relationship inside a generic repository

I am trying to get work with many-to-many relationship in my web application. I am using a generic repository base code.

Here my the entities

public class UserEntity : BaseEntity<int>
{
    public string EmployeeId { get; set; }
    public string FullName { get; set; }
    public string Email { get; set; }

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

public class RoleEntity : BaseEntity<int>
{
    public string Name { get; set; }

    public virtual ICollection<UserRoleEntity> Users { get; set; }
}

public class UserRoleEntity : BaseEntity<Guid>
{
    public int UserId { get; set; }
    public int RoleId { get; set; }

    public virtual UserEntity UserEntity { get; set; }
    public virtual RoleEntity RoleEntity { get; set; }
}

This is context model configuration

private void ConfigureUserRole(EntityTypeBuilder<UserRoleEntity> builder)
{
    builder.ToTable("UserRole");

    //there is no need for a surrogate key on many-to-many mapping table
    builder.Ignore("Id");

    builder.HasKey(ur => new { ur.RoleId, ur.UserId });

    builder.HasOne(ur => ur.RoleEntity)
        .WithMany(r => r.Users)
        .HasForeignKey(ur => ur.RoleId);

    builder.HasOne(ur => ur.UserEntity)
        .WithMany(u => u.UserRoles)
        .HasForeignKey(ur => ur.UserId);
}

This is the main get method of my generic repository (in fact it is Chris Pratt's implementation) Please notice the query.Include line, the get methods of generic repository make use of EF Core's Include api to get dependent entity which comes as string parameter .

 protected virtual IQueryable<T> GetQueryable(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
                                                    string includeProperties = null,
                                                    int? skip = null,
                                                    int? take = null)
        {
            includeProperties = includeProperties ?? string.Empty;
            IQueryable<T> query = _dbContext.Set<T>();

            if (filter != null)
            {
                query = query.Where(filter);
            }

            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            if (orderBy != null)
            {
                query = orderBy(query);
            }

            if (skip.HasValue)
            {
                query = query.Skip(skip.Value);
            }

            if (take.HasValue)
            {
                query = query.Take(take.Value);
            }

            return query;
        }

And in my service, I want to get user data with its full dependents.

public class UserService : IUserService
{
    private IRepository<UserEntity> _userRepository;
    private IRepository<RoleEntity> _roleRepository;

    public UserService(IRepository<UserEntity> userRepository, IRepository<RoleEntity> roleRepository)
    {
        _userRepository = userRepository;
        _roleRepository = roleRepository;
    }
    public UserEntity GetUserData(string employeeId)
    {
        var user = _userRepository.GetFirst(u => u.EmployeeId == employeeId, includeProperties: "UserRoles");
        //var roles = _roleRepository.GetAll().ToList();

        return user;
    }
}

When the above service code executed, I get the user with its UserRole dependent, however, when I inspect UserRole's RoleEntity dependent which I need, it is null.

Upvotes: 1

Views: 1960

Answers (1)

DavidG
DavidG

Reputation: 119206

Your include path only has a single hop from UserEntity to UserRoleEntity (via the UserRoles property. You need to include the next step to ensure you also capture the RoleEntity. To do this, change your path to UserRoles.RoleEntity, for example:

var user = _userRepository.GetFirst(
    u => u.EmployeeId == employeeId, 
    includeProperties: "UserRoles.RoleEntity");

Upvotes: 1

Related Questions