I have three classes User
, Role
and UserRole
.
Using Code First I have created tables like below
DbContext.cs
rotected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<CodeFirstDBContext>(null);
modelBuilder.Entity<DomainModels.Security.User>().HasKey(x => x.Id);
modelBuilder.Entity<DomainModels.Security.User>().Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<DomainModels.Security.Role>().HasKey(x => x.Id);
modelBuilder.Entity<DomainModels.Security.Role>().Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// UserRoles table column names as UserId and RoleId instead of default created by ER as User_Id and Role_Id.
modelBuilder.Entity<DomainModels.Security.User>().HasMany(x => x.Roles).WithMany(y => y.Users).Map(
of =>
{
of.MapLeftKey("UserId");
of.MapRightKey("RoleId");
of.ToTable("UserRoles");
});
}
public virtual DbSet<DomainModels.Security.Role> Roles { get; set; }
public virtual DbSet<DomainModels.Security.User> Users { get; set; }
Not Added UserRole
DbSet as it is created by EF in many to many relations
My issue is, that when I try to perform any CRUD operation on UserRole
it throws an exception:
The entity type UserRole is not part of the model for the current context We are using
simple odata
var resul = await odataclient.For<UserRole>().Set(new { UserId = userId, RoleId = roleId }).InsertEntryAsync();
I'm not sure whether Simple OData Client
supports this or not.
What should I do?
What you have setup is so called many-to-many relationship with auto "link" table maintained for you by EF. In such configuration UserRole
entity does not exist. The model should be something like this:
public class User
{
public int Id { get; set; }
// other properties
// ...
// UserRole association
public virtual ICollection<Role> Roles { get; set; }
}
public class Role
{
public int Id { get; set; }
// other properties
// ...
// UserRole association
public virtual ICollection<User> Users { get; set; }
}
You perform CRUD on UserRoles table by using either one of the navigation properties User.Roles
or Role.Users
.
EDIT: Alternatively, you can keep the UserRole
entity and use explicit link table with two one-to-many
associations like this:
Model:
public class User
{
public int Id { get; set; }
// other properties
// ...
// UserRole association
public virtual ICollection<UserRole> UserRoles { get; set; }
}
public class Role
{
public int Id { get; set; }
// other properties
// ...
// UserRole association
public virtual ICollection<UserRole> UserRoles { get; set; }
}
public class UserRole
{
public int UserId { get; set; }
public int RoleId { get; set; }
public virtual User User { get; set; }
public virtual Role Role { get; set; }
}
DbContext:
public virtual DbSet<DomainModels.Security.Role> Roles { get; set; }
public virtual DbSet<DomainModels.Security.User> Users { get; set; }
public virtual DbSet<DomainModels.Security.UserRoles> UserRoles { get; set; }
Configuration:
modelBuilder.Entity<DomainModels.Security.UserRole>()
.HasKey(x => new { x.UserId, x.RoleId });
modelBuilder.Entity<DomainModels.Security.UserRole>()
.HasRequired(x => x.User)
.WithMany(y => y.UserRoles)
.HasForeignKey(x => x.UserId)
.WillCascadeOnDelete();
modelBuilder.Entity<DomainModels.Security.UserRole>()
.HasRequired(x => x.Role)
.WithMany(y => y.UserRoles)
.HasForeignKey(x => x.RoleId)
.WillCascadeOnDelete();
With this configuration you can use your original CRUD design.