Object is null when configuring one to one relationship with fluent api - c#

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; }
}

Related

Entity Framework relationship to IdentityUser not resolving

I have a class named "MyFile" that has a Many-to-One relationship to "Workspace" and then the "Workspace" has a Many-to-One relationship to "IdentityUser". This works well when I create a Workspace the relationship to the IdentityUser is configured correctly, however when I fetch the Workspace the Owner field show up as null. In the database the value is set in the Owner column.
So what I want to do is get a list of All Files and whom they belong to, but since the Owner property is null I'm not able to figure out the owner. Database wise all looks good.
(This code has been simplified to focus on the problem)
public class MyFile
{
// Base
public Guid MyFileID { get; set; }
[Column(TypeName = "nvarchar(256)")]
public string Name { get; set; }
// Workspace
public virtual Workspace Workspace { get; set; }
}
public class Workspace
{
// Base
public Guid WorkspaceID { get; set; }
public string Name { get; set; }
// Security
public virtual IdentityUser Owner { get; set; }
}
Code to get the information that ends up with Owner = Null:
var myFiles = _applicationDbContext.MyFiles
.Include(x => x.Workspace)
.ThenInclude(y => y.Owner)
.Where(x => x.Deleted == showDeleted)
.OrderByDescending(x => x.Uploaded)
.Skip(pagesize*(page-1))
.Take(pagesize);
I managed to solve the issue by extending the IdentityUser class with ApplicationUser and then change the relationship to ApplicationUser instead.
Also i added a ApplicationUserId which I populate at the same time as I Populate the ApplicationUser then I could use to lookup the correct IdentityUser in my lamdba. There is probably a better solution but this worked for me.
public class Workspace
{
// Base
public Guid WorkspaceID { get; set; }
public string Name { get; set; }
// Security
public string ApplicationUserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
}
Extension Class:
public class ApplicationUser : IdentityUser
{
}

How to Map Twitter follower/following type of relation in EF Core 5

How do you configure something similar to Twitter Following and Follower type of relationship using EF Core 5 with the Fluent API? I tried various different ways of configuring it and the only few ways I was able to get it to work is if I ignored the navigation properties on the User entity. I am currently migrating my code from EF Core 2.1 to 5. The following configuration worked earlier. (Not sure if it is misconfigured)
public class User
{
public long Id { get; set; }
public string Name { get; set; }
public ICollection<UserFollower> Followers { get; set; }
public ICollection<UserFollower> Following { get; set; }
}
public class UserFollower
{
public long UserId { get; set; }
public User User { get; set; }
public long FollowedById { get; set; }
public User FollowedBy { get; set; }
}
public class UserFollowerConfiguration : IEntityTypeConfiguration<UserFollower>
{
public void Configure(EntityTypeBuilder<UserFollower> builder)
{
builder.HasKey(p => new { p.UserId, p.FollowedById });
builder.HasOne(p => p.User)
.WithMany(i => i.Followers)
.HasForeignKey(i => i.UserId);
builder.HasOne(p => p.FollowedBy)
.WithMany(i => i.Following)
.HasForeignKey(i => i.FollowedById);
}
}
This configuration throws an error when saving to the database.
SqlException: Violation of PRIMARY KEY constraint 'PK_UserFollower'.
Cannot insert duplicate key in object 'dbo.UserFollower'. The duplicate key value is (111, 111).
Even when trying to directly add to the DbContext and calling SaveChanges() on it.
Context.Add(new UserFollower() {UserId = 222, FollowedById = 111});
What is the recommended way of mapping such a relationship with EF Core 5? Note that I do need to access the UserFollowers table without going through the Navigation properties of the User.
Edit #1
The following is the OnModelCreating() for the DbContext
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfigurations(typeof(DbContext).Assembly);
/*few configurations unrelated to UserFollower entity*/
}
User entity has the following configuration,
builder.HasKey(i => i.Id);
builder.Property(i => i.Id).ValueGeneratedOnAdd();
Try configuring it like this.
builder.Entity<User>().HasMany(s => s.Followers)
.WithOne(f => f.FollowedBy);
builder.Entity<User>().HasMany(s => s.Following)
.WithOne(f => f.);
Also, The PK is missing for the UserFollower table, I don't know if an Id is being generated somehow somewhere. If not, maybe this is why it's trying to wrongly use FollowedById as key, but define an Id for the UserFollower table and see.
public class UserFollower
{
public long Id {get;set;}
public long UserId { get; set; }
public User User { get; set; }
public long FollowedById { get; set; }
public User FollowedBy { get; set; }
}
Even if this works, I would recommend you change the structure of your model, it looks ambigous for the twitter requirements you described. If I query Userfollowers
var userFollowers = _context.UserFollowers.ToList();
For each result in the list, there is no way for me to tell if the user is following or being followed. You could change your models to these ones;
public class User
{
public long Id { get; set; }
public string Name { get; set; }
public ICollection<UserFollower> Followers { get; set; }
public ICollection<UserFollowing> Following { get; set; }
}
public class UserFollower
{
public long UserId { get; set; }
public User User { get; set; }
public long UserFollowingMeId { get; set; }
public User UserFollowingMe { get; set; }
}
public class UserFollowing
{
public long UserId { get; set; }
public User User { get; set; }
public long UserIAmFollowingId { get; set; }
public User UserIAmFollowing { get; set; }
}
This way, everybody knows when they check the UserFollowings table, the UserId is the Id of the person that is following and vice versa for the UserFollowers table. If I had an Id of 8 in the system, I can query my followers and people I follow like this;
var myFollowers = _context.UserFollowers.Where(UserId = 8);
var peopleIFollow = _context.UserFollowing.Where(UserId = 8);

Entity Framework Many-Too-Many Not Populating Results

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?!
}

User has roles for systems - how to (object-)model ternary relation?

I've got the following Entities:
Privilege ( Id, Name )
Role ( Id, Name, ICollection<Privilege> )
System ( Id, Name )
User ( Id, Name, Pass, ? )
Now I want to model "A user may have for each of zero or more systems zero or more roles", e.g.:
IDictionary<System, ICollection<Role>> SystemRoles { get; set; }
Is this possible with ASP.NET EntityFramework? If yes, how? What attributes do I have to set?
Been looking around for quite some time now, however I don't find anything useful on the net for "entity framework code first ternary relation"
Can you point me to some nice link where this is covered in detail? Or maybe give me a hint how to model it / which attributes I can put on the dictionary?
Additional question: If the IDictionary solution works somehow, is there any change to get change tracking proxy performance? IDictionary is not an ICollection...
A user may have for each of zero or more systems zero or more roles
You will need an entity that describes the relationship between the three:
public class UserSystemRole
{
public int UserId { get; set; }
public int SystemId { get; set; }
public int RoleId { get; set; }
public User User { get; set; }
public System System { get; set; }
public Role Role { get; set; }
}
I would create a composite primary key from all three properties because each combination may only occur once and must the unique. Each part of the key is a foreign key for the respective navigation property User, System and Role.
Then the other entities would have collections refering to this "link entity":
public class User
{
public int UserId { get; set; }
//...
public ICollection<UserSystemRole> UserSystemRoles { get; set; }
}
public class System
{
public int SystemId { get; set; }
//...
public ICollection<UserSystemRole> UserSystemRoles { get; set; }
}
public class Role
{
public int RoleId { get; set; }
//...
public ICollection<UserSystemRole> UserSystemRoles { get; set; }
}
And then the mapping with Fluent API would look like this:
modelBuilder.Entity<UserSystemRole>()
.HasKey(usr => new { usr.UserId, usr.SystemId, usr.RoleId });
modelBuilder.Entity<UserSystemRole>()
.HasRequired(usr => usr.User)
.WithMany(u => u.UserSystemRoles)
.HasForeignKey(usr => usr.UserId);
modelBuilder.Entity<UserSystemRole>()
.HasRequired(usr => usr.System)
.WithMany(s => s.UserSystemRoles)
.HasForeignKey(usr => usr.SystemId);
modelBuilder.Entity<UserSystemRole>()
.HasRequired(usr => usr.Role)
.WithMany(r => r.UserSystemRoles)
.HasForeignKey(usr => usr.RoleId);
You can remove one of the collection properties if you don't need them (use WithMany() without parameter then).
Edit
In order to get a dictionary for a user you could introduce a helper property (readonly and not mapped to the database) like so:
public class User
{
public int UserId { get; set; }
//...
public ICollection<UserSystemRole> UserSystemRoles { get; set; }
public IDictionary<System, IEnumerable<Role>> SystemRoles
{
get
{
return UserSystemRoles
.GroupBy(usr => usr.System)
.ToDictionary(g => g.Key, g => g.Select(usr => usr.Role));
}
}
}
Note that you need to load the UserSystemRoles property with eager loading first before you can access this dictionary. Or alternatively mark the UserSystemRoles property as virtual to enable lazy loading.

Relationship troubles with Entity Framework

I need help creating the relationship in entity framework as everything I have tried gives me errors when trying to add the migration or if I get passed that then I try to update the database and get an error about indexes with the same name.
public class Profile
{
public Profile()
{
Environments = new HashSet<Environment>();
}
[Key]
public int Id { get; set; }
public string VersionCreated { get; set; }
public string DiskLocation { get; set; }
public string Name { get; set; }
public DateTime DateTime { get; set; }
public virtual Product Product { get; set; }
public virtual Instance OriginalInstance { get; set; }
public virtual ICollection<Environment> Environments { get; set; }
}
public class Instance
{
public Instance()
{
TestResults = new HashSet<TestResult>();
Environments = new HashSet<Environment>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public string UserFriendlyName { get; set; }
public virtual Product Product { get; set; }
public virtual Profile LastKnownProfile { get; set; }
public virtual Computer Computer { get; set; }
public virtual ICollection<TestResult> TestResults { get; set; }
public virtual ICollection<Environment> Environments { get; set; }
}
The problem with the above classes is that the OrginalInstance property on the Profile class and the LastKnownProfile in the Instance class are supposed to just be foreign keys to those specific tables and they probably won't be the same very often. They can also both possibly be null.
I have tried:
modelBuilder.Entity<Instance>().HasRequired(i => i.LastKnownProfile);
modelBuilder.Entity<Profile>().HasRequired(p => p.OriginalInstance);
This gave me an Unable to determine the principal end of an association between the types 'EcuWeb.Data.Entities.Instance' and 'EcuWeb.Data.Entities.Profile'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations. error.
and with:
modelBuilder.Entity<Instance>().HasRequired(i => i.LastKnownProfile).WithOptional();
modelBuilder.Entity<Profile>().HasRequired(p => p.OriginalInstance).WithOptional();
The database adds a foreign key reference back to itself.
...that the OrginalInstance property on the Profile class and the
LastKnownProfile in the Instance class are supposed to just be foreign
keys to those specific tables and they probably won't be the same very
often. They can also both possibly be null.
In this case you actually want two one-to-many relationships between Profile and Instance if I don't misunderstand your quote above. It would mean that many Profiles can have the same OriginalInstance and that many Instances can have the same LastKnownProfile. The correct mapping would look like this then:
modelBuilder.Entity<Profile>()
.HasOptional(p => p.OriginalInstance)
.WithMany()
.Map(m => m.MapKey("OriginalInstanceId"));
modelBuilder.Entity<Instance>()
.HasOptional(i => i.LastKnownProfile)
.WithMany()
.Map(m => m.MapKey("LastKnownProfileId"));
The lines with MapKey are optional. Without them EF will create a foreign key with a default name.
Also note that you must use HasOptional (instead of HasRequired) if "both can possibly be null".

Categories