Entity Framework Many-Too-Many Not Populating Results - c#

I'm trying to map a many-too-many relationship in C# EF with code first migrations.
The issue that I'm having is that no results are being populated from the related table. Take this piece of code for example, _role contains no permissions whereas it should contain 4 results:
foreach (Role _role in _dbContext.Roles) //_dbContext.Roles.Count = 2
{
foreach(Permission _permission in _role.Permissions) //_role.Permissions.Count = 0
{
//Do something with each _permission
}
}
The classes look like this:
[Table("Roles")]
public partial class Role
{
public Role()
{
Permissions = new HashSet<Permission>();
}
[Key]
public int RoleId { get; set; }
public string RoleName { get; set; }
public ICollection<Permission> Permissions { get; set; }
}
[Table("Permissions")]
public partial class Permission
{
public Permission()
{
Roles = new HashSet<Role>();
}
[Key]
public int PermissionId { get; set; }
public string PermissionName { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
And finally the Fluent API:
modelBuilder.Entity<Permission>()
.HasMany(e => e.Roles)
.WithMany(e => e.Permissions)
.Map(m => m
.ToTable("_Lnk_Role_Permission")
.MapLeftKey("PermissionId")
.MapRightKey("RoleId"));
After inspecting the database, the tables, keys and data are all in order and manually adding and querying data produces the correct results. When trying to access the data, roles are present but no permissions.
Can anybody see where it could be going wrong?

I think you are looking for the Include statement.
foreach (var role in _dbContext.Roles.Include(x => x.Permissions))
{
...
}
The virtual keyword is good practice and plays a role in how the data is loaded. If you aren't careful, you can end up doing 1+N queries (1 for the Role object + N more for each Permission). The Include statement tells EF to effectively join the tables in the DB query so that the data is available in memory for handling your nested for loops.
Apologies for linking elsewhere, but MSDN links should be here for a while. This is a pretty good read to understand the implications of virtual/non-virtual and with/without the Include statement:
https://msdn.microsoft.com/en-us/data/jj574232.aspx

Well after hours of head scratching, I missed out Virtual from the ICollection. The modified class looks like so:
public partial class Role
{
public Role()
{
Permissions = new HashSet<Permission>();
}
[Key]
public int RoleId { get; set; }
public string RoleName { get; set; }
public Virtual ICollection<Permission> Permissions { get; set; } //Notice the virtual?!
}

Related

How to tell EF core two models use the same table?

I'm migrating a classic SQL database to EF core 2.2 and I'm facing a problem.
I'm not new to EF but I don't see how to do this...
Here is the problem, I have a table Users, a table Groups, and a table Members. Members are users composing Groups. I simplified models and kept only the keys :
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
public class Member
{
[Key]
public int UserId { get; set; }
[ForeignKey(nameof(UserId))]
public virtual User User { get; set; }
[Key]
public int GroupId { get; set; }
[ForeignKey(nameof(GroupId))]
public virtual Group Group { get; set; }
}
public class Group
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<Member> Members { get; set; }
}
In classic SQL I can join tables by key, but how can I "jump" from Users to Groups like User.Groups ?
User already has a groups why do you need to include the group into Member? is not just having a user enough? If you need to read Members from DB then you can read them and use "Include" method to add User or Groups in your case. Something like this will do the job:
var result = db.Members
.Include(m => m.Groups)
.Include(m => m.User)
Ok, I resolved this by another way :
User.Groups = context.Groups.Where(g => g.Members.Any(m => m.UserId == x.Id)).Select(g => new GenericData
{
Id = g.Id,
Tag = g.Tag,
Label = g.Name,
Flag = g.Flag
}).ToList()
Where x is the db user
It works like this, but if someone could say me how to do this like I wanted at start, I'm curious to know it :)

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.
Try this:
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

Object is null when configuring one to one relationship with fluent api

I have three classes: Role, Permission and RolePermission(role permission is the third table in a many to many relationship)
public class Role : Entity
{
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<RolePermission> RolePermissions { get; set; }
}
public class Permission : Entity
{
public string Name { get; set; }
public virtual ICollection<RolePermission> RolePermissions { get; set; }
}
public class RolePermission : Entity
{
public int RoleId { get; set; }
public int PermissionId { get; set; }
public Permission Permission { get; set; }
public Role Role { get; set; }
}
Then I used fluentAPI in order to configure relationship:
For Role:
HasMany(role => role.RolePermissions)
.WithRequired(rolePermission => rolePermission.Role)
.HasForeignKey(rolePermission => rolePermission.RoleId);
For Permission:
HasMany(permission => permission.RolePermissions)
.WithRequired(rolePermission => rolePermission.Permission)
.HasForeignKey(rolePermission => rolePermission.PermissionId);
For RolePermission:
HasRequired(rolePermission => rolePermission.Permission)
.WithMany(permission => permission.RolePermissions)
.HasForeignKey(rolePermission => rolePermission.PermissionId);
HasRequired(rolePermission => rolePermission.Role)
.WithMany(role => role.RolePermissions)
.HasForeignKey(rolePermission => rolePermission.RoleId);
The problem is that only Role object is populated.
The code in this question pertains to setting up a relationship. The reported issue in this question pertains to related data not being loaded automatically. These are two different things that have little to do with one another.
Did you miss an Include somewhere? Have you accessed (and therefore lazily loaded) the Role nav prop, but not the Permission nav prop? I would like to see the code starting from where you launch the query up to where you inspect this object (as per your screenshot)
You responded with the requested code:
var user = _userRepository
.FirstOrDefaultAsync(us => us.Email == email);
var userPermissions =
user.UserRoles
.First()
.Role
.RolePermissions
.Select(rp => rp.Permission)
.ToList();
If you insert an Include() statement in your query, you will see that the Permission will actually be fetched correctly.
I am not quite sure which object you're inspecting. The screenshot tells me you're looking at a RolePermission, but the posted code suggests that you fetch a list of Permission objects.
Regardless, you seem to already have fixed it using an Include:
Mihai Alexandru-Ionut #Flater, yes, I have to use include and the problem is solved. This is the solution, so please post it as an answer in order to accept it.
Id Property is missing for both Role and Permission tables. When you say RoleId property in RolePermission table, EF looks for Id Property in Role table.
Update your Role and Permission tables like this and give a try:
public class Role : Entity
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<RolePermission> RolePermissions { get; set; }
}
public class Permission : Entity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<RolePermission> RolePermissions { get; set; }
}

How can I write a simple LINQ report for a many to many relationship?

My application uses ASP.NET Identity 2 with tables stored in SQL Server 2012 Relational tables. Here's a view of two classes that I would like to use for reporting:
public partial class AspNetUser
{
public AspNetUser()
{
this.AspNetRoles = new List<AspNetRole>();
}
public string Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<AspNetRole> AspNetRoles { get; set; }
}
public partial class AspNetRole
{
public AspNetRole()
{
this.AspNetUsers = new List<AspNetUser>();
}
public string Id { get; set; }
public string Name { get; set; }
public virtual ICollection<AspNetUser> AspNetUsers { get; set; }
}
I created the following LINQ to give me a report:
var users = await db.AspNetUsers
.Include(u => u.AspNetRoles)
.ToListAsync();
This is inside a WebAPI method and when I check the result it gives me a
result that I don't expect. What I see is an array of user objects and
then inside that an array of Roles. But inside the array of Roles is
another array of users!
Here's the output that I see with some fields ommitted to make it show
easily:
[{"id":"27cd003a-fb6a-4a2d-8df9-f502bc10a583"
"aspNetRoles":[
{
"id":"53724f55-af7a-448b-9ae2-1fe295b109fd",
"name":"Admin",
"aspNetUsers":[
{
"id":"527ddbd5-14d3-4fb9-a7ae-374e66f635d4",
"aspNetRoles":[]
},
{
"id":"e87c05bc-8305-45d0-ba07-3dd24438ba8b",
"aspNetRoles":[]
}
]},
{"id":"527ddbd5-14d3-4fb9-a7ae-374e66f635d4",
How can I change the LINq so that it gives me an array of AspNetUser objects and inside a simple array of AspNetRole objects? I also need the query to show me the users even if they have no roles. Something like this:
User1
Role1
Role2
User2
Role1
User3
User4
Role2
As you said, you have no control over the domain classes -- they belong to Microsoft. So, what you're really saying is that you don't want to expose your domain objects directly to the rest of the world (google that and you'll find all sorts of people with opinions on that subject). So, one option is to define a set of classes which are the ones you want to expose (data exchange classes). Something like:
public class User
{
public string Id { get; set; }
public string UserName { get; set; }
public virtual List<Role> Roles { get; set; }
}
public class Role
{
public string Id { get; set; }
public string Name { get; set; }
}
Then, when you get your domain objects back, map them into your data exchange objects for serialization and exposure to the rest of the world.

The number of elements in ICollection is zero in many-to-many relationship

I have a database made with Entity Framework. I have two tables Users and Advertisments and the relationship between them is Many-to-Many. Everything is working fine except when I want to return the number of ICollection in class Users.
[Table("Advertisments")]
public class Advertisment
{
public Advertisment()
{
Users = new HashSet<User>();
}
[Key]
public int AdvertismentID { get; set; }
public string Address { get; set; }
public string City { get; set; }
public double Rating { get; set; }
public int NumberOfRates { get; set; }
public string Title { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public ICollection<User> Users { get; set; }
}
[Table("Users")]
public class User
{
public User()
{
FavouriteAdvertisments = new HashSet<Advertisment>();
}
[Key]
public int UserID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public ICollection<Advertisment> FavouriteAdvertisments { get; set; }
}
public class GetHiredDBContext : DbContext
{
public GetHiredDBContext()
: base("GetHiredDBContext")
{ }
public DbSet<User> Users { get; set; }
public DbSet<Advertisment> Advertisments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().HasMany(a => a.FavouriteAdvertisments).WithMany(u => u.Users).Map(m =>
{
m.MapLeftKey("UserID");
m.MapRightKey("AdvertismentID");
m.ToTable("UserAdvertisment");
});
}
}
And this is what I want to do:
public ICollection<Advertisment> favouriteAdvertismentsByUser(int UserID)
{
GetHiredDBContext db = new GetHiredDBContext();
foreach (User user in db.Users)
{
if (user.UserID == UserID)
{
return user.FavouriteAdvertisments;
}
}
return null;
}
Everytime I call this method, the number of elements in ICollection for every user is 0!
public ICollection<Advertisment> favouriteAdvertismentsByUser(int UserID)
{
GetHiredDBContext db = new GetHiredDBContext();
// First of all, you probably forgot to "include" FavouriteAdvertisments
var users = db.Users.Include(u => u.FavouriteAdvertisments);
// Second of all, use linq!
return users.SingleOrDefault(u => u.UserID == UserID).FavouriteAdvertisments;
}
If your using Entity Framework you need to write your queries in Linq so the query provider can translate that into a SQL statement. As you have it now it is doing a table scan. Instead try this:
public ICollection<Advertisment> favouriteAdvertismentsByUser(int UserID)
{
return new GetHiredDbContext()
.Users
.Single(u => u.UserID = UserID)
.FavouriteAdvertisements;
}
One thing to note, this method now expects there to be exactly 1 record in your table with that UserID. It will throw an exception if it does not exist. Personally I prefer this because if I'm calling a method I expect it to work, and exception would mean I coded something wrong allowing me to find bugs earlier. You also do not have to check if your collection is null before getting the count.
The way your entites are currently set up, you will have to either use Eager, or Explicit loading, your related entites will not be loaded automatically.
To Explicitly load, I believe you can use your original query (provided you're passing an entity that can be found in the DBSet, and make an explicit call to load the related information (using Load):
E.g. (provided your entity can be found).
public ICollection<Advertisment> favouriteAdvertismentsByUser(User userEntity)
{
// Load the blog related to a given post
GetHiredDBContext db = new GetHiredDBContext();
db.Entry(userEntity).Reference(p => p.FavouriteAdvertisments).Load();
return user.FavouriteAdvertisments;
}
Although it's probably cleaner to obtain your entity from your context, and call load on that, rather than interating through your entire set.
To Eagerly load, you make your load request at the time of query, using Include:
public ICollection<Advertisment> favouriteAdvertismentsByUser(int userID)
{
GetHiredDBContext db = new GetHiredDBContext();
User myUser = db.Users
.Where(x => x.UserID = userID)
.Include(x => x.FavouriteAdvertisments)
.FirstOrDefault();
return myUser.FavouriteAdvertisments;
}
To obtain the data the third way, using Lazy-Loading, you would have to make some alterations to your classes, namely marking your ICollection navigation properties as virtual, so entity framework is able to create valid proxy types for your classes. Your data would be available as required, and loaded on demand.
Hopefully I haven't got the problem completely wrong, just about to shut down/sleep.
Good luck.
More info: http://msdn.microsoft.com/en-us/data/jj574232.aspx

Categories