Reputation: 16239
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?
Upvotes: 1
Views: 745
Reputation: 205849
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.
Upvotes: 2
Reputation: 3024
Many-to-Many table in EF isn't available as physically accessible entity, rather you have to perform operations on it using either of the involved entities, as the related entity is available as ICollection
in both of the models. EF doesn't expose this entity rather manages it internally.
i.e, You must have ICollection<User> Users
*as navigation property in *Role
and vice versa.
Let's say, you have an object of User
, and you need to get/set role against it. You will have to do it as:
User user = GetSomeUser();
Role adminRole = GetAdminRole();
user.Roles.Add(adminRole);
Role role = user.Roles.Where(x => x.Id == adminRoleId).firstOrDefault();
Above example is just give you understanding of how to work with Many-to-Many
relationship in EF
Upvotes: 2