I'm using EntityFramework with a link table and I have created a OnModelCreating that creates a link table called "RolePrivileges". But I have another dbcontext that uses the same database but with the relation the other way so I get this error:
Invalid object name 'dbo.PrivilegeRoles'
My Privilege class has a public virtual ICollection<Role> Roles { get; set; } property.
How can I tell EF that it's from RolePrivileges and not PrivilegeRoles?
UPDATE
I have added some code, this a big project and I am using repository pattern and all that. This is just to show the error
I have many projects, one for only generating the database that has all entities, migrations and all that.
in the dbcontext in this project I have
public class EasyhoursDbContext : IdentityDbContext<ApplicationUser>
{
...
public DbSet<Role> AccessRoles { get; set; }
public DbSet<Privilege> Privileges { get; set; }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.Entity<Role>()
.HasMany(role => role.Privileges)
.WithMany(p => p.AccessRoles)
.Map(ap =>
{
ap.MapLeftKey("RoleId");
ap.MapRightKey("PrivilegeId");
ap.ToTable("RolePrivileges");
});
...
}
}
public class Role
{
[Key]
public string Id { get; protected set; }
public string Name { get; protected set; }
public virtual List<Privilege> Privileges { get; private set; }
}
public class Privilege
{
[Key]
public string Id { get; set; }
...
public virtual ICollection<Role> AccessRoles { get; set; }
}
And then in another project I have a dbcontext that just contains this
public class RoleDbContext : DbContext
{
public RoleDbContext()
: base("DefaultConnection")
{
}
public DbSet<Role> AccessRoles { get; set; }
public DbSet<Privilege> Privileges { get; set; }
}
public class Privilege
{
public string Id { get; set; }
...
[JsonIgnore]
[IgnoreDataMember]
public virtual ICollection<Role> AccessRoles { get; set; }
}
public class Role
{
public string Id { get; protected set; }
public string Name { get; protected set; }
public List<Privilege> Privileges { get; set; } = new List<Privilege>();
}
And here's an example where I get the error:
var db = new RoleDbContext();
var role = db.AccessRoles.FirstOrDefault(r => true);
var privilege = db.Privileges.FirstOrDefault(p => true);
role.Privileges.Add(privilege);
db.Entry(role).State = EntityState.Modified;
db.SaveChanges();
On save changes I get the error:
Running this exact same code, but with EasyhoursDbContext instead it works fine
UPDATE
I updated the RoleDbContext now to contain the exact same rule for the onmodelcreation as EasyhoursDbContext.
But now I'm getting this error:
Violation of PRIMARY KEY constraint 'PK_dbo.RolePrivileges'. Cannot insert duplicate key in object 'dbo.RolePrivileges'. The duplicate key value is (03fd67b6-277f-43f6-b276-5bafbdbe55af, A657a693-0961-Role-b86b-381261aApply).\r\nThe statement has been terminated.
I wanna thank Steve Greene for helping me with finding the problem.
All I needed was to add OnModelCreation to my RolesDbContext
modelBuilder.Entity<Role>()
.HasMany(role => role.Privileges)
.WithMany(p => p.AccessRoles)
.Map(ap =>
{
ap.MapLeftKey("RoleId");
ap.MapRightKey("PrivilegeId");
ap.ToTable("RolePrivileges");
});
Related
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);
I am creating a sqlite database to track users assigned to teams. This would mean many-to-many table relationship. I need help making the model for the migration.
users table:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public ICollection<Photo> Photos { get; set; }
public virtual ICollection<UserTeam> UserTeams { get; set; }
}
team table:
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public User User { get; set; }
public int UserId { get; set; }
public virtual ICollection<UserTeam> UserTeams{ get; set; }
}
UserTeam Table:
public class UserTeam
{
public User User { get; set; }
public int UserId{ get; set; }
public Team Team{ get; set; }
public int TeamId{ get; set; } //... I have this one for owner of the team
public string Title { get; set; }
}
DataContext:
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base (options) {}
public DbSet<Value> Values { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Photo> Photos { get; set; }
public DbSet<Team> Teams { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserTeam>()
.HasKey(ut => new { ut.UserId, ut.TeamId });
modelBuilder.Entity<UserTeam>()
.HasOne(ut => ut.User)
.WithMany(u => u.UserTeams)
.HasForeignKey(ut => ut.UserId);
modelBuilder.Entity<UserTeam>()
.HasOne(ut => ut.Team)
.WithMany(t => t.UserTeams)
.HasForeignKey(ut => ut.TeamId);
}
}
From what I read declaring I ICollection on team class and user class would force a join table creating. However when I try to migrate, I get this message: Unable to determine the relationship represented by navigation property 'User.Teams' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Any suggestions on how to correctly make join tables in sqlite would be helpful
looks like you are using EF Core 2.x. It is necessary to define the entity in the model. This means you will have to define relationship by overriding it in the OnModelCreating method.
I would further normalize your structure by creating another table to hold the reference for the user and the team the user belongs to and vice-versa. I would call this table UserTeam (for the lack of better naming)
So I will have something like:
public class User
{
//....omitted
public virtual ICollection<UserTeam> UserTeams { get; set; }
}
public class UserTeam
{
public int UserId{ get; set; }
public User User { get; set; }
public int TeamId{ get; set; }
public Team Team{ get; set; }
}
public class Team
{
//.... omitted
public virtual ICollection<UserTeam> UserTeams{ get; set; }
}
The UserTeam table will need to be configured so that EFCore can map it successfully. This is where we define the many-to-many relationship
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserTeam>()
.HasKey(ut => new { ut.UserId, ut.TeamId });
modelBuilder.Entity<UserTeam>()
.HasOne(ut => ut.User)
.WithMany(u => u.UserTeams)
.HasForeignKey(ut => ut.UserId);
modelBuilder.Entity<UserTeam>()
.HasOne(ut => ut.Team)
.WithMany(t => t.UserTeams)
.HasForeignKey(ut => ut.TeamId);
}
I've made a simple Entity Framework ASP Core Application that works but I do not know why:
I've made a context like this:
public class AstootContext : DbContext
{
public AstootContext(DbContextOptions<AstootContext> options)
: base(options)
{ }
public DbSet<Account> Accounts { get; set; }
public DbSet<User> Users { get; set; }
}
And I have two tables with models like this:
public class Account
{
public int Id { get; set; }
public string Username { get; set; }
public string PasswordHash { get; set; }
public DateTime Created { get; set; }
List<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public DateTime Birthday { get; set; }
public Account Account { get; set; }
}
The interesting thing is that when I run my application it actually can pick up the data. It just seems weird because I have not specified any table mapping.
I'm assuming this just automaps because the specified tables are the same name.
My questions are:
How do I specify Table explicit table mapping in case I do not want my model names to be exactly the same as the DB?
How do I specify Custom Column Mapping.
Is there anything special I have to specify for Primary/Foreign Keys
edit
To clarify
Say I had a table in the DB MyAccounts and I wanted to map that to an entity Accounts.
Say I had a column password and I wanted that to map to a POCO property PasswordHash
To specify the name of the database table, you can use an attribute or the fluent API:
Using Attributes:
[Table("MyAccountsTable")]
public class Account
{
public string PasswordHash { get; set; }
}
Using Fluent API:
public class YourContext : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Language>(entity => {
entity.ToTable("MyAccountsTable");
});
}
}
To name your columns manually, it's very similar and you can use an attribute or the fluent API:
Using Attributes:
public class Account
{
[Column("MyPasswordHashColumn")]
public string PasswordHash { get; set; }
}
Using Fluent API:
public class YourContext : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Language>(x => x
.ToTable("MyAccountsTable")
.Property(entity => entity.PasswordHash)
.HasColumnName("MyPasswordHashColumn")
);
}
}
I have coded a MVC5 Internet Application with cascading deletes, and am getting the following error:
Unable to cast object of type 'System.Collections.Generic.List`1[TestDeleteForeignKeyReferences.Models.Room]' to type 'TestDeleteForeignKeyReferences.Models.Room'.
Here is my context class:
public class MyDatabaseContext : DbContext
{
public DbSet<House> houses { get; set; }
public DbSet<Room> rooms { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<House>()
.HasOptional(x => x.rooms)
.WithOptionalDependent()
.WillCascadeOnDelete(true);
}
}
Here is my House class:
public class House
{
[Key]
public int Id { get; set; }
public string name { get; set; }
public virtual ICollection<Room> rooms { get; set; }
public House()
{
rooms = new List<Room>();
}
}
Here is my Room class:
public class Room
{
[Key]
public int Id { get; set; }
public int roomNumber { get; set; }
public virtual House house { get; set; }
}
The error occurs when trying to add a House to the database at the following Create code:
db.houses.Add(house);
If I have no OnModelCreating method in the context class, the error does not occur.
Can I please have some help to get this code working.
You want to create a one (house) to many (rooms) relation with optional principal (house is optional on room). The configuration should be like.
modelBuilder.Entity<House>()
.HasMany(x => x.rooms)
.WithOptional(x => x.house)
.WillCascadeOnDelete(true);
All my attempts to map the ID of SitePage to the database column ID (SitePages table, ID column of type bigint) has failed. It keeps looking for column SitePage_ID to map it.. Can you see where I am doing wrong? All related code is below;
public class Site : EntityBase<Int64>
{
public virtual string Url { get; set; }
public virtual IList<SitePage> Pages { get; set; }
}
public class SitePage : EntityBase<Int64>
{
public virtual Site Site { get; set; }
public virtual string Url { get; set; }
public virtual string Html { get; set; }
public virtual string Text { get; set; }
public virtual string Language { get; set; }
}
public abstract class EntityBase<T> : IComparable
{
public virtual T ID { get; set; }
protected EntityBase() : this(default(T))
{
}
protected EntityBase(T id)
{
this.ID = id;
if (this.ID == null)
this.ID = default(T);
}
}
public class SpellCrawlerContext : DbContext
{
public SpellCrawlerContext(){}
public DbSet<Site> Sites { get; set; }
public DbSet<SitePage> SitePages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Site>()
.HasMany(s => s.Pages)
.WithRequired(p => p.Site)
.Map(s => s.MapKey("SiteID"));
modelBuilder.Entity<SitePage>()
.HasKey(p => p.ID);
modelBuilder.Entity<SitePage>()
.Property(p => p.ID)
.HasColumnName("ID");
}
}
You are not doing anything wrong. The code you shown does everything correctly. You even don't need to explicitly define the name of ID in SitePage because it will be defined like ID anyway.
SitePage_ID is used by default naming convention for foreign keys created for independent associations. So do you have any other one-to-many relation between SitePage and any other entity? If you didn't map foreign key in dependent entity it will be defined as SitePage_ID by default.