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);
Related
So i want to add RelatedProducts to my products. So i applied the same relationship type as i did for category. The only difference there is no category class, but we target the same entity. (product). Because the regular many2many works fine, i removed that from my example.
Goal:
Any suggestions?
Exception:
"Exception occured: Cannot create a relationship between 'Product.RelatedProducts' and 'RelatedProduct.Related' because a relationship already exists between 'Product.RelatedProducts' and 'RelatedProduct.Product'. Navigation properties can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation 'RelatedProduct.Related' first in 'OnModelCreating'."
Product.cs:
public class Product : IExportable, IEntityBase
{
public int Id { get; set; }
public string Name { get; set;}
public ICollection<RelatedProduct> RelatedProducts { get; set; }
}
ProductEntityTypeConfiguration.cs
internal class ProductEntityTypeConfiguration : IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> builder)
{
config.HasKey(p => p.Id);
}
}
RelatedProducts.cs
public class RelatedProduct
{
public int Id { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public int RelatedId { get; set; }
public virtual Product Related { get; set; }
}
RelatedProductEntityTypeConfiguration.cs
public class RelatedProductsEntityConfiguration : IEntityTypeConfiguration<RelatedProduct>
{
public void Configure(EntityTypeBuilder<RelatedProduct> builder)
{
builder.HasKey(rp => rp.Id);
builder.ToTable("RelatedProducts");
builder
.HasOne(rp => rp.Product)
.WithMany(p => p.RelatedProducts)
.HasForeignKey(rp => rp.ProductId)
.OnDelete(DeleteBehavior.Restrict);
builder
.HasOne(rp => rp.Related)
.WithMany(p => p.RelatedProducts)
.HasForeignKey(rp => rp.RelatedId)
.OnDelete(DeleteBehavior.Restrict);
}
}
I am unable to test this suggestion but I am sure it might work. Why don't you just define your RelatedProduct as:
public class RelatedProduct
{
public int Id { get; set; }
public virtual ICollection<Product> Related { get; set; }
}
I also suggest you comment out the code in your RelatedProductsEntityConfiguration Configure method
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 am trying to create a join table that combines the Employee and Tractor table to record each time an Employee is assigned/unassigned a truck. I am able to record an initial employeeID and truckId but the code crashes once I try to record second of employeeID and truckID on a different time and Day. Thus, the Datetime object is always unique.
This is the error it shows:
SqlException: Violation of PRIMARY KEY constraint 'PK_DriverTractorsAssignmentHistory'. Cannot insert duplicate key in object 'dbo.DriverTractorsAssignmentHistory'. The duplicate key value is (1, 2).
The statement has been terminated.
Using a Many to many relationship was the one solution I could think of to capture each time an employee is assigned a truck. Pls show me a better solution if you have one
public class Employee
{
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public int? TractorID { get; set; }
public virtual Tractor Tractor { get; set; }
public virtual List<DriverTractorAssignmentHistory>DriverTractorAssignmentHistories { get; set; }
}
public class Tractor
{
public int TractorID { get; set; }
public string TruckNumber {get; set;}
public string Status { get; set; }
public virtual Employee Employee { get; set; }
public virtual List<DriverTractorAssignmentHistory> DriverTractorAssignmentHistories { get; set; }
public Tractor()
{
Status = "Available";
}
}
public class TrailerOrderDbContext:DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<DriverTractorAssignmentHistory> DriverTractorsAssignmentHistory { get; set; }
public DbSet<Tractor> Tractors { get; set; }
public TrailerOrderDbContext(DbContextOptions<TrailerOrderDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DriverTractorAssignmentHistory>().HasKey(co => new { co.EmployeeId, co.TractorId });
modelBuilder.Entity<DriverTractorAssignmentHistory>()
.HasOne(e => e.Driver)
.WithMany(c => c.DriverTractorAssignmentHistories)
.HasForeignKey(trac => trac.TractorId);
modelBuilder.Entity<DriverTractorAssignmentHistory>()
.HasOne(trac => trac.Tractor)
.WithMany(c => c.DriverTractorAssignmentHistories)
.HasForeignKey(e => e.EmployeeId);
}
}
Remove this line of code in OnModelCreating(ModelBuilder modelBuilder)beause this line of code prevents duplicate data entry and prevents many-to-many mapping:
modelBuilder.Entity<DriverTractorAssignmentHistory>()
.HasKey(co => new { co.EmployeeId, co.TractorId });
Or modify as below if in case unique key is needed:
modelBuilder.Entity<DriverTractorAssignmentHistory>()
.HasKey(co => new { co.EmployeeId, co.TractorId, co.AssignTimestamp };
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");
});
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.