Neo
Neo

Reputation: 16239

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?

Upvotes: 1

Views: 745

Answers (2)

Ivan Stoev
Ivan Stoev

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

Zeeshan
Zeeshan

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

Related Questions