Search code examples
c#entity-frameworkodata

EF Code First exception The entity type is not part of the model for the current context using Simple Odata client


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?


Solution

  • 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.