Reputation: 2549
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
.
How can I get the dependent RoleEntity
data just modifying the generic repository's get method?
Secondly, the line commented in service code (_roleRepository.GetAll()
), when it is also executed, the RoleEntity
is immediately filled with its proper value. How is it happening?
Upvotes: 1
Views: 1960
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