Introducing FOREIGN KEY constraint - Basic Migration - entity-framework-core

I tried to make some project in C# with entity framework core 2.1. However, there is a problem that I can't solve, since I don't see anything wrong.
I'm trying to just do a simple migrate in my database.
There is no problem until I write 'Update-Database' to Package manager console. After trying to update database, here is the error message:
Introducing FOREIGN KEY constraint 'FK_Users_Baskets_BasketId' on table 'Users' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
Basket.cs
public class Basket {
[Key]
public int BasketId { get; set; }
public List<ProductByBasket> ProductByBaskets { get; set; }
public string BasketName { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public User User { get; set; }
}
Product.cs
public class Product {
[Key]
public int ProductId { get; set; }
public List<ProductByBasket> ProductByBaskets { get; set; }
public string ProductName { get; set; }
}
ProductByBasket.cs
[Key]
public int ProductByBasketId { get; set; }
public int BasketId { get; set; }
[ForeignKey("BasketId")]
public Basket Basket { get; set; }
public int ProductId { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
}
Migration File
migrationBuilder.CreateTable(
name: "ProductByBaskets",
columns: table => new
{
BasketId = table.Column<int>(nullable: false),
ProductId = table.Column<int>(nullable: false),
ProductByBasketId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ProductByBaskets", x => new { x.ProductId, x.BasketId });
table.UniqueConstraint("AK_ProductByBaskets_ProductByBasketId", x => x.ProductByBasketId);
table.ForeignKey(
name: "FK_ProductByBaskets_Products_ProductId",
column: x => x.ProductId,
principalTable: "Products",
principalColumn: "ProductId",
onDelete: ReferentialAction.Cascade);
});
ApplicationDbContext.cs
public class ApplicationDbContext : DbContext {
public ApplicationDbContext() { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
string connectionString = #"Data Source=...\SQLEXPRESS; Initial Catalog = db; Integrated Security=true;";
optionsBuilder.UseSqlServer(connectionString);
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<ProductByBasket>()
.HasOne(u => u.Basket).WithMany(u => u.ProductByBaskets).IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<ProductByBasket>()
.HasKey(x => new { x.ProductId, x.BasketId });
modelBuilder.Entity<ProductByBasket>()
.HasOne(pt => pt.Basket)
.WithMany(p => p.ProductByBaskets)
.HasForeignKey(pt => pt.BasketId);
modelBuilder.Entity<ProductByBasket>()
.HasOne(pt => pt.Product)
.WithMany(t => t.ProductByBaskets)
.HasForeignKey(pt => pt.ProductId);
}
public DbSet<Product> Products { get; set; }
public DbSet<Basket> Baskets { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<ProductByBasket> ProductByBaskets { get; set; }
}
I tried to configure migration file to write. Then, it looks like:
migrationBuilder.CreateTable(
name: "ProductByBaskets",
columns: table => new
{
BasketId = table.Column<int>(nullable: false),
ProductId = table.Column<int>(nullable: false),
ProductByBasketId = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn)
},
constraints: table =>
{
table.PrimaryKey("PK_ProductByBaskets", x => x.ProductByBasketId);
table.ForeignKey(
name: "FK_ProductByBaskets_Baskets_BasketId",
column: x => x.BasketId,
principalTable: "Baskets",
principalColumn: "BasketId",
onDelete: ReferentialAction.Cascade);
table.PrimaryKey("PK_ProductByBaskets", x => new { x.ProductId, x.BasketId });
table.UniqueConstraint("AK_ProductByBaskets_ProductByBasketId", x => x.ProductByBasketId);
table.ForeignKey(
name: "FK_ProductByBaskets_Products_ProductId",
column: x => x.ProductId,
principalTable: "Products",
principalColumn: "ProductId",
onDelete: ReferentialAction.Cascade);
});
Then I get this error:
Foreign key references invalid table.
What am I doing wrong?

Related

Many to Many relationship Entity Framework Core 5

I created a Blazor project and I have a many-to-many relationship between these classes:
public class ItemAttribute
{
[Key]
public int ItemAttributeId { get; set; }
public string Title { get; set; }
public ICollection<Item> Items { get; set; }
public ICollection<ItemAttributeCluster> itemAttributeClusters { get; set; }
}
and
public class ItemAttributeCluster
{
[Key]
public int ItemAttributeClusterId { get; set; }
public string Titel { get; set; }
public bool IsMultiChoice { get; set; }
public ICollection<ItemAttribute> itemAttributes { get; set; }
}
So far so good, EF generates the Join table ItemAttributeItemAttributeCluster, ok.
Then I try to add a new cluster of ItemAttributes for the first time with my controller:
// Create
[HttpPost]
public async Task<IActionResult> Post(ItemAttributeCluster itemAttributeCluster)
{
_context.ItemAttributeClusters.Add(itemAttributeCluster);
await _context.SaveChangesAsync();
return Ok(itemAttributeCluster);
}
and I get this error:
Cannot insert explicit value for identity column in table 'ItemAttributes' when IDENTITY_INSERT is set to OFF.
What am I doing wrong? Why is EF trying to write something into 'ItemAttributes'? When i´m trying to create a new Cluster on 'ItemAttributesCluster' and the Join Table?
Migration Builder:
Join Table
migrationBuilder.CreateTable(
name: "ItemAttributeItemAttributeCluster",
columns: table => new
{
itemAttributeClustersItemAttributeClusterId = table.Column<int>(type: "int", nullable: false),
itemAttributesItemAttributeId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ItemAttributeItemAttributeCluster", x => new { x.itemAttributeClustersItemAttributeClusterId, x.itemAttributesItemAttributeId });
table.ForeignKey(
name: "FK_ItemAttributeItemAttributeCluster_ItemAttributeClusters_itemAttributeClustersItemAttributeClusterId",
column: x => x.itemAttributeClustersItemAttributeClusterId,
principalTable: "ItemAttributeClusters",
principalColumn: "ItemAttributeClusterId",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ItemAttributeItemAttributeCluster_ItemAttributes_itemAttributesItemAttributeId",
column: x => x.itemAttributesItemAttributeId,
principalTable: "ItemAttributes",
principalColumn: "ItemAttributeId",
onDelete: ReferentialAction.Cascade);
});
ItemAttributes
migrationBuilder.CreateTable(
name: "ItemAttributes",
columns: table => new
{
ItemAttributeId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ItemAttributes", x => x.ItemAttributeId);
});
ItemAttributeCluster
migrationBuilder.CreateTable(
name: "ItemAttributeClusters",
columns: table => new
{
ItemAttributeClusterId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Titel = table.Column<string>(type: "nvarchar(max)", nullable: true),
IsMultiChoice = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ItemAttributeClusters", x => x.ItemAttributeClusterId);
});
If this was an existing schema for the ItemAttribute / Cluster tables and their PK were defined as identity columns, you will need to tell EF to expect them using the [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] attribute alongside the Key designation.
When using a naming convention that EF recognizes like "ItemAttributeId" or "Id" I believe EF will default to assuming these are Identity columns, but with a name like "ItemAttributeCode" I believe it would assume a database generated option of "None" as default.
try to add some navigation properties
public ItemAttributeCluster()
{
AttributeClusters = new HashSet<AttributeCluster>();
}
[Key]
public int Id { get; set; }
public string Titel { get; set; }
public bool IsMultiChoice { get; set; }
[InverseProperty(nameof(AttributeCluster.ItemAttributeClaster))]
public virtual ICollection<AttributeCluster> AttributeClusters { get; set; }
}
public partial class ItemAttribute
{
public ItemAttribute()
{
AttributeClusters = new HashSet<AttributeCluster>();
}
[Key]
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Item> Items { get; set; }
[InverseProperty(nameof(AttributeCluster.ItemAttribute))]
public virtual ICollection<AttributeCluster> AttributeClusters { get; set; }
}
public partial class AttributeCluster
{
[Key]
public int Id { get; set; }
public int ItemAttributeId { get; set; }
public int ItemAttributeClasterId { get; set; }
[ForeignKey(nameof(ItemAttributeId))]
[InverseProperty("AttributeClusters")]
public virtual ItemAttribute ItemAttribute { get; set; }
[ForeignKey(nameof(ItemAttributeClasterId))]
[InverseProperty(nameof(ItemAttributeCluster.AttributeClusters))]
public virtual ItemAttributeCluster ItemAttributeClaster { get; set;
}
dbcontext (no any fluent apis at all)
public virtual DbSet<AttributeCluster> AttributeClusters { get; set; }
public virtual DbSet<ItemAttribute> ItemAttributes { get; set; }
public virtual DbSet<ItemAttributeCluster> ItemAttributeClusters { get; set; }
Test
var itemAttributeClaster = new ItemAttributeCluster { Titel="titleClaster2", IsMultiChoice=false};
var itemAttribute = new ItemAttribute{Title="attrTitle" };
var attributeClaster = new AttributeCluster { ItemAttribute = itemAttribute, ItemAttributeClaster = itemAttributeClaster };
_context.AttributeClusters.Add(attributeClaster);
_context.SaveChanges();
it created 1 record in each of 3 tables
I give up on getting this to work with ef. I run several sql`s directly to achieve the same functionality and so far it works, not a satisfactory solution but it needs to be done.

EF Core Many-To-Many Relationship Sets Both Keys to Parent

I working on building out a model that would represent a typical product that could be created in an e-commerce platform written using EF Core 2.0. See the model structure below
public class GSProduct : BaseEntity
{
[Required]
public string Name { get; set; }
public ICollection<GSProduct> BaseProducts { get; set; }
public ICollection<GSRelatedProduct> ParentProducts { get; set; }
public ICollection<GSRelatedProduct> ChildProducts { get; set; }
public ICollection<GSVendorProductInfo> VendorProductInfo { get; } = new List<GSVendorProductInfo>();
}
public class GSRelatedProduct
{
public virtual GSProduct ParentProduct { get; set; }
public Guid ParentProductId { get; set; }
public virtual GSProduct ChildProduct { get; set; }
public Guid ChildProductId { get; set; }
}
public class GSVendorProductInfo : BaseEntity
{
public GSContact Vendor { get; set; }
public Guid VendorId { get; set; }
public GSProduct Product { get; set; }
public Guid ProductId { get; set; }
public string VendorPartNumber { get; set; }
public int BaseUnits { get; set; }
public double Cost { get; set; }
public int MinOrderQty { get; set; }
public int OrderedInMultiples { get; set; }
}
This is what I have set up for the Fluent API.
modelBuilder.Entity<GSVendorProductInfo>()
.HasOne(pt => pt.Product)
.WithMany(p => p.VendorProductInfo)
.HasForeignKey(pt => pt.ProductId);
modelBuilder.Entity<GSVendorProductInfo>()
.HasOne(pt => pt.Vendor)
.WithMany(t => t.VendorProductInfo)
.HasForeignKey(pt => pt.VendorId);
modelBuilder.Entity<GSRelatedProduct>().HasKey(x => new { x.ParentProductId, x.ChildProductId });
modelBuilder.Entity<GSRelatedProduct>()
.HasOne(pt => pt.ParentProduct)
.WithMany(t => t.ParentProducts)
.HasForeignKey(pt => pt.ParentProductId);
modelBuilder.Entity<GSRelatedProduct>()
.HasOne(pt => pt.ChildProduct)
.WithMany(t => t.ChildProducts)
.HasForeignKey(pt => pt.ChildProductId);
Also including the migration
migrationBuilder.CreateTable(
name: "GSRelatedProducts",
columns: table => new
{
ParentProductId = table.Column<Guid>(nullable: false),
ChildProductId = table.Column<Guid>(nullable: false),
Optional = table.Column<bool>(nullable: false),
Quantity = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_GSRelatedProducts", x => new { x.ParentProductId, x.ChildProductId });
table.ForeignKey(
name: "FK_GSRelatedProducts_GSProducts_ChildProductId",
column: x => x.ChildProductId,
principalTable: "GSProducts",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
table.ForeignKey(
name: "FK_GSRelatedProducts_GSProducts_ParentProductId",
column: x => x.ParentProductId,
principalTable: "GSProducts",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
});
The scaffolding / migration is working fine and I can actually create products without a problem that include all of the relationships. The issue arises when I try to add a 'RelatedProduct' to the Product model.
I set the ParentProductId and the ChildProductId accordingly and when I create or update the entity it sets both the ParentProductId and the ChildProductId value to the ParentProductId.
I've followed the code through my debugger and it is correct up until the point where I call _context.Update(entity). After that both of the Ids in the RelatedProduct model are set to the same value.
I've got no idea why this is happening any suggestions would be very helpful.
I think there is a problem with the way your migration is generated. From the above code I am not sure it is clear what is the purpose of the child property in GSRelatedProduct. If you look at the migration you will see that the same constraint is set for both parent and child:
table.ForeignKey(
name: "FK_GSRelatedProducts_GSProducts_ChildProductId",
column: x => x.ChildProductId,
principalTable: "GSProducts",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
table.ForeignKey(
name: "FK_GSRelatedProducts_GSProducts_ParentProductId",
column: x => x.ParentProductId,
principalTable: "GSProducts",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
Both Parent and Child have the same principal table and principal column. They will both get the same value from GSProducts table. What your logic or business process is I cannot figure out, but you are actually getting the desired effect. Is the child supposed to point to something else? In your code you are probably assigning different values to product and child, but this seems to be somehow overwritten. Basically, your code first is thinking that both and parent should have the same value. In other words, Child seems to be redundant.
Is your GSProduct table a self-referencing table where you keep products and subproducts together? If so, then you need an additional column for this purpose like ParentId that points to the Id in order to get the desired relationship.

EF Core OnDelete restrict adds additional column

I have models in a many-to-many relationship:
User
public class User
{
public int Id { get; set; }
public int CompanyId { get; set; }
[Required]
[StringLength(100)]
public string Username { get; set; }
[Required]
public string PasswordHash { get; set; }
[ForeignKey("CompanyId")]
public Company Company { get; set; }
public ICollection<UserRole> UserRoles { get; set; }
}
Role
public class Role
{
public int Id { get; set; }
public int CompanyId { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[StringLength(500)]
public string Description { get; set; }
[ForeignKey("CompanyId")]
public Company Company { get; set; }
public ICollection<RolePrivilege> RolePrivileges { get; set; }
}
UserRole
public class UserRole
{
public int Id { get; set; }
public int UserId { get; set; }
public int RoleId { get; set; }
[ForeignKey("UserId")]
public User User { get; set; }
[ForeignKey("RoleId")]
public Role Role { get; set; }
}
When I created migrations and then tried update-database, it threw an error of multiple cascade paths. The solution to this was to make On Delete, No Action so I added this in OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserRole>()
.HasIndex(e => new { e.UserId, e.RoleId })
.IsUnique();
modelBuilder.Entity<UserRole>()
.HasOne(e => e.User)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<UserRole>().ToTable("UserRoles");
}
Now the tables are created but one thing that I wasn't expecting is its making an extra column. The migration code looks like this after generating:
migrationBuilder.CreateTable(
name: "UserRoles",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
RoleId = table.Column<int>(type: "int", nullable: false),
UserId = table.Column<int>(type: "int", nullable: false),
UserId1 = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UserRoles", x => x.Id);
table.ForeignKey(
name: "FK_UserRoles_Roles_RoleId",
column: x => x.RoleId,
principalTable: "Roles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UserRoles_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_UserRoles_Users_UserId1",
column: x => x.UserId1,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
As you can see, it added an extra column, UserId1.
What am I doing wrong or how do I prevent this from happening?
This is a result of a typical relationship fluent configuration mistake - using a parameterless overload of Has / With (effectively telling EF that there is no corresponding navigation property) while actually a navigation property exists. In that case EF will map the missing navigation property to another relationship with no navigation property at the other end and default by convention FK property/column name.
To fix the issue, make sure to use the correct overloads which represent the presence/absence of a navigation property (and update them according in case you add/remove navigation property). In your case, replace
.WithMany()
with
.WithMany(e => e.UserRoles)

Entity framework 6, same column for many foreign keys

I need help with this issue, because after many hours of investigation I am stuck.
I created a datamodel from an existing old database, using Entity Framework 6 (I am using Code First approach). This database was multi-company oriented, so most of its tables has a column "Company" that its used as a part of almost all primary keys and foreign keys.
The datamodel creation created all the foreign keys using Fluent API. But this don't helps and when I try to select data from any table I received errors "invalid columna name 'TABLE_COLUMN'. Because in this database usually the columns has different name in every table and the Entity framework can't determine the relation, so its required to map the column names.
So, I can solve the issue using DataAnnotations, and I can do, for example:
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("BLOQHOR"), InverseProperty("CODHOR")]
public int NUMHOR { get; set; }
[Key]
[Column(Order = 2)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("BLOQHOR"), InverseProperty("DISTAINIC")]
public int DISTAINIC { get; set; }
[Key]
[Column(Order = 3)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("BLOQHOR"), InverseProperty("COMPANY")]
public int COMPANY{ get; set; }
What happends now?
The table has another foreign key that also needs the column COMPANY. Because data annotations don't allow me to use the column twice, I can't make the table to work.
I repeat, in the data model, it created a fluent api definition for the second foreign key, but it don't works.
modelBuilder.Entity<CABAJUSTES>()
.HasMany(e => e.AJUSBLOQ)
.WithRequired(e => e.CABAJUSTES)
.HasForeignKey(e => new { e.NUMAJUST, e.COMPANY})
The fact its that everytime I try to get data I received errors like "Invalid column name CABAJUSTES_CODAJUSTE" and "Invalid column name CABAJUSTES_COMPANY". And I am unable to map this second foreign key.
What can I do?
Thanks in advance.
Its a bit hard to follow your table structure, so I've tried to set up a comprehensive example using some common entities anyone should be able to follow. Please comment if this does not fully describe your problem.
Note that I've deliberately used pretty shitty foreign keys to make sure the helping automapping in Entity Framework doesn't help me, and to show that this works with any legacy database design you may have.
First the expected structure in the example
One Company holds many Articles and many Invoices.
One Invoice holds many InvoiceRows.
Each InvoiceRow may optionally refer to an Article.
The actual Entities
class Company
{
public int TheCompanyKey { get; set; }
public virtual ICollection<Invoice> Its_Invoices { get; set; }
public virtual ICollection<Article> Its_Articles { get; set; }
}
class Invoice
{
public int Its_CompanyKey { get; set; }
public int TheInvoiceKey { get; set; }
public string InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public virtual Company Its_Company { get; set; }
public virtual ICollection<InvoiceRow> Its_Rows { get; set; }
}
class InvoiceRow
{
public int Rows_Company_Key { get; set; }
public int Its_InvoiceID { get; set; }
public int RowNumber { get; set; }
public int? Its_Articles_ID { get; set; }
public string Text { get; set; }
public double Price { get; set; }
public virtual Invoice Its_Invoice { get; set; }
public virtual Article Its_Article { get; set; }
}
class Article
{
public int TheArticleCompany_Key { get; set; }
public int TheArticleKey { get; set; }
public string ArticleNumber { get; set; }
public double Cost { get; set; }
public double TargetPrice { get; set; }
public virtual Company Its_Company { get; set; }
}
The DbContext with OnModelCreating()
There are multiple ways to generate the required structure, depending on if you think top-down or bottom-up. My take on modelling is to start with the base tables and the describe how children relate to them.
class MyContext : DbContext
{
public MyContext() : base("name=MyContext")
{
}
public virtual IDbSet<Company> Companies { get; set; }
public virtual IDbSet<Invoice> Invoices { get; set; }
public virtual IDbSet<InvoiceRow> InvoiceRows { get; set;}
public virtual IDbSet<Article> Articles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Company>()
.HasKey(e => e.TheCompanyKey);
modelBuilder.Entity<Article>()
.HasKey(e => new { e.TheArticleCompany_Key, e.TheArticleKey })
.HasRequired(e => e.Its_Company).WithMany(e => e.Its_Articles).HasForeignKey(e => e.TheArticleCompany_Key);
modelBuilder.Entity<Invoice>()
.HasKey(e => new { e.Its_CompanyKey, e.TheInvoiceKey })
.HasRequired(e => e.Its_Company).WithMany(e => e.Its_Invoices).HasForeignKey(e => e.Its_CompanyKey);
modelBuilder.Entity<InvoiceRow>()
.HasKey(e => new { e.Rows_Company_Key, e.Its_InvoiceID, e.RowNumber });
modelBuilder.Entity<InvoiceRow>()
.HasRequired(e => e.Its_Invoice).WithMany(e => e.Its_Rows)
.HasForeignKey(e => new { e.Rows_Company_Key, e.Its_InvoiceID }).WillCascadeOnDelete();
modelBuilder.Entity<InvoiceRow>()
.HasOptional(e => e.Its_Article)
.WithMany()
.HasForeignKey(e => new { e.Rows_Company_Key, e.Its_Articles_ID });
}
}
Finally the generated migration
Running add-migration multikeys in the Package Manager Console window results in the following migration:
public partial class multikeys : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Articles",
c => new
{
TheArticleCompany_Key = c.Int(nullable: false),
TheArticleKey = c.Int(nullable: false),
ArticleNumber = c.String(),
Cost = c.Double(nullable: false),
TargetPrice = c.Double(nullable: false),
})
.PrimaryKey(t => new { t.TheArticleCompany_Key, t.TheArticleKey })
.ForeignKey("dbo.Companies", t => t.TheArticleCompany_Key, cascadeDelete: true)
.Index(t => t.TheArticleCompany_Key);
CreateTable(
"dbo.Companies",
c => new
{
TheCompanyKey = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.TheCompanyKey);
CreateTable(
"dbo.Invoices",
c => new
{
Its_CompanyKey = c.Int(nullable: false),
TheInvoiceKey = c.Int(nullable: false),
InvoiceNumber = c.String(),
InvoiceDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => new { t.Its_CompanyKey, t.TheInvoiceKey })
.ForeignKey("dbo.Companies", t => t.Its_CompanyKey, cascadeDelete: true)
.Index(t => t.Its_CompanyKey);
CreateTable(
"dbo.InvoiceRows",
c => new
{
Rows_Company_Key = c.Int(nullable: false),
Its_InvoiceID = c.Int(nullable: false),
RowNumber = c.Int(nullable: false),
Its_Articles_ID = c.Int(),
Text = c.String(),
Price = c.Double(nullable: false),
})
.PrimaryKey(t => new { t.Rows_Company_Key, t.Its_InvoiceID, t.RowNumber })
.ForeignKey("dbo.Articles", t => new { t.Rows_Company_Key, t.Its_Articles_ID })
.ForeignKey("dbo.Invoices", t => new { t.Rows_Company_Key, t.Its_InvoiceID }, cascadeDelete: true)
.Index(t => new { t.Rows_Company_Key, t.Its_Articles_ID })
.Index(t => new { t.Rows_Company_Key, t.Its_InvoiceID });
}
public override void Down()
{
DropForeignKey("dbo.Articles", "TheArticleCompany_Key", "dbo.Companies");
DropForeignKey("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_InvoiceID" }, "dbo.Invoices");
DropForeignKey("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_Articles_ID" }, "dbo.Articles");
DropForeignKey("dbo.Invoices", "Its_CompanyKey", "dbo.Companies");
DropIndex("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_InvoiceID" });
DropIndex("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_Articles_ID" });
DropIndex("dbo.Invoices", new[] { "Its_CompanyKey" });
DropIndex("dbo.Articles", new[] { "TheArticleCompany_Key" });
DropTable("dbo.InvoiceRows");
DropTable("dbo.Invoices");
DropTable("dbo.Companies");
DropTable("dbo.Articles");
}
}
Summary
I believe this describes the OP problem and with a little study gives a good understanding of how Fluent can be used to map entities.
Good luck!

EF Code First cascade delete doesn't work

I have 4 tables:
User table
public enum SEX { Male, Female }
public abstract class User
{
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public SEX Sex { get; set; }
}
Doctor table inherites from User
[Table("Doctor")]
public class Doctor : User
{
public string Department { get; set; }
public string Occupation { get; set; }
public string CabinetNumber { get; set; }
public virtual List<Treat> Treats { get; set; }
}
Patient table inherites from User
[Table("Patient")]
public class Patient : User
{
public int InsuranceNumber { get; set; }
public int CardNumber { get; set; }
public virtual List<Treat> Treats { get; set; }
}
public class Treat
{
public int TreatId { get; set; }
public int DoctorUserId { get; set; }
public int PatientUserId { get; set; }
public virtual Doctor Doctor { get; set; }
public virtual Patient Patient { get; set; }
}
public class HospitalContext: DbContext
{
public HospitalContext() : base("DBConnectionString") {
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<HospitalContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Treat>()
.HasRequired(x => x.Doctor)
.WithMany( x => x.Treats)
.HasForeignKey( x => x.DoctorUserId)
.WillCascadeOnDelete(true);
modelBuilder.Entity<Treat>()
.HasRequired(x => x.Patient)
.WithMany( x => x.Treats)
.HasForeignKey( x => x.PatientUserId)
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
public DbSet<User> Users { get; set; }
public DbSet<Treat> Treats { get; set; }
}
I have found much answers here but no one from them works. I have spend a few hours trying to make it work. I know that Entity Framework must enable cascade delete when there is one-to-many relation, but it didn't
Entity Framework doesn't apply cascade deletion with TPT (Table Per Type) inheritance. You can solve this with Code Fist migrations:
CreateTable(
"dbo.Treats",
c => new
{
TreatId = c.Int(nullable: false, identity: true),
DoctorUserId = c.Int(nullable: false),
PatientUserId = c.Int(nullable: false),
})
.PrimaryKey(t => t.TreatId)
.ForeignKey("dbo.Doctor", t => t.DoctorUserId, cascadeDelete: true)
.ForeignKey("dbo.Patient", t => t.PatientUserId, cascadeDelete: true)
.Index(t => t.DoctorUserId)
.Index(t => t.PatientUserId);
The important part is cascadeDelete: true. You have to manually add it after migration code generation. After that you will have cascade deletion in your database:
FOREIGN KEY ([DoctorUserId]) REFERENCES [dbo].[Doctor] ([UserID]) ON DELETE CASCADE,
FOREIGN KEY ([PatientUserId]) REFERENCES [dbo].[Patient] ([UserID]) ON DELETE CASCADE