I have the following entity:
public class ShoppingCartItem
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ShoppingCartItemId { get; set; }
public virtual Product Product { get; set; }
public int Quantity { get; set; }
public string ShoppingCartId { get; set; }
public DateTime ItemAddedToCart { get; set; }
}
There is a one to one foreign key relationship from Product to ShoppingCartItem.
My question is, how can I make entity framework core skip deleting the ShoppingCartItem entity on Product cascade delete?
You can use something like the following in your builder object
builder.Entity<ShoppingCartItem>()
.HasOne(s => s.Product)
.WithOne(p => p.ShoppingCartItem)
.Metadata.DeleteBehavior = DeleteBehavior.Restrict;
Related
This is a operation i have done many times in the past using database-first approach. I'm now trying it with code-first using EF Core and i'm failing horribly.
I have the following model:
public class DataMapping
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string Model { get; set; }
public string Property { get; set; }
public bool IgnoreProperty { get; set; }
[NotMapped] //<-- I had to add this as the migration was complaining that it did not know what the relation was
public List<DataMappingRelation> DataMappingRelations { get; set; }
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
public DateTime? DateModified { get; set; }
}
and a Bridge model that basically creates a relations between two DataMapping items in the same table:
public class DataMappingRelation
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[ForeignKey("DataMappingId")]
public long? DataMapping1Id { get; set; }
public DataMapping DataMapping1 { get; set; }
[ForeignKey("DataMappingId")]
public long? DataMapping2Id { get; set; }
public DataMapping DataMapping2 { get; set; }
}
However this call does not work:
return _context.DataMappings.Where(x => x.Model == type.FullName)
.Include(x=>x.DataMappingRelations)
.ToList();
It does not like the Include and throws a null ref exception.
All i basically need to do here is for a given "DataMapping" get all the related DataMapping items based on the relations in the "DataMappingRelations" table.
Yes i have looked at this answer but again, it is an example of two seperate tables, not a single table bridging on itself.
I suspect i have done all of this wrong. How can i get this to work? All the examples i have found are bridging two seperate tables. this would be bridging the same table.
Its many-to-many with self but your whole configuration looks messy.
So first, your DataMapping model class should contain two list navigation properties for two foreign keys in the DataMappingRelation as follows:
public class DataMapping
{
......
public List<DataMappingRelation> DataMapping1Relations { get; set; }
public List<DataMappingRelation> DataMapping2Relations { get; set; }
.........
}
Now remove [ForeignKey("DataMappingId")] attribute from both DataMapping1 and DataMapping2 foreign keys as follows:
public class DataMappingRelation
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public long? DataMapping1Id { get; set; }
public DataMapping DataMapping1 { get; set; }
public long? DataMapping2Id { get; set; }
public DataMapping DataMapping2 { get; set; }
}
Then the Fluent API configuration should be as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<DataMappingRelation>()
.HasOne(dmr => dmr.DataMapping1)
.WithMany(dm => dm.DataMapping1Relations)
.HasForeignKey(dmr => dmr.DataMapping1Id)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<DataMappingRelation>()
.HasOne(dmr => dmr.DataMapping2)
.WithMany(dm => dm.DataMapping2Relations)
.HasForeignKey(dmr => dmr.DataMapping2Id)
.OnDelete(DeleteBehavior.Restrict);
}
We have a requirement that several tables need to be referred in a one table as shown in the diagram.
Please note that the diagram is NOT the correct DB model but just represent what our requirement. Can you suggest a proper way to implement above in SQL Server DB and Entity Framework 6?
Example: A sales order (in SalesOrder table) can have multiple files and those uploaded file details will be stored in UploadedFile table. Likewise OrderTable and Invoice too.
So we need to have a proper DB model with FK relationships between FileUpload table with each other related table.
Note: All tables PKs are auto-increment int values and we may need add more entities (tables) in future
In order to provide you an alternative, as you said you are using Entity Framework, here is a sample of Code First implementation done on C#. You can create and update the schema via Package Manager Console migrations, within Visual Studio. I have used the Fluent API in order to define the relationships, as this is recommended over the alternative.
public class SampleContext : DbContext
{
public SampleContext()
: base("name=YourConnection")
{
}
public DbSet<SalesOrder> SalesOrders { get; set; }
public DbSet<CreditOrder> CreditOrders { get; set; }
public DbSet<Invoice> Invoices { get; set; }
public DbSet<UploadedFile> UploadedFiles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<SalesOrder>()
.HasKey(so => so.Id);
modelBuilder.Entity<CreditOrder>()
.HasKey(co => co.Id);
modelBuilder.Entity<Invoice>()
.HasKey(i => i.Id);
modelBuilder.Entity<UploadedFile>()
.HasKey(u => u.Id);
modelBuilder.Entity<UploadedFile>()
.HasRequired(u => u.SalesOrder)
.WithMany(s => s.UploadedFiles)
.WillCascadeOnDelete(false);
modelBuilder.Entity<UploadedFile>()
.HasRequired(u => u.CreditOrder)
.WithMany(c => c.UploadedFiles)
.WillCascadeOnDelete(false);
modelBuilder.Entity<UploadedFile>()
.HasRequired(u => u.Invoice)
.WithMany(c => c.UploadedFiles)
.WillCascadeOnDelete(false);
modelBuilder.Entity<UploadedFile>()
.Property(uf => uf.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<SalesOrder>()
.Property(so => so.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<CreditOrder>()
.Property(co => co.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Invoice>()
.Property(i => i.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
base.OnModelCreating(modelBuilder);
}
}
// Collections of navigation properties should be included in classes for a one-to-many relationship
public class SalesOrder
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class CreditOrder
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class UploadedFile
{
public int Id { get; set; }
public SalesOrder SalesOrder { get; set; }
public CreditOrder CreditOrder { get; set; }
public Invoice Invoice { get; set; }
public string FilePath { get; set; }
public string FileType { get; set; }
}
public class SalesOrder
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class CreditOrder
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class UploadedFile
{
public int Id { get; set; }
public SalesOrder SalesOrder { get; set; }
public CreditOrder CreditOrder { get; set; }
public Invoice Invoice { get; set; }
public string FilePath { get; set; }
public string FileType { get; set; }
}
Before Reading My Answer take notice in the below information:
A better answer can be found at this previous post at Foreign Key to multiple tables.
My Answer:
Refer here for more information from the Creating Tables documentation off of the MDSN page.
The design I went for gave each table there own id as the Primary Key. Then I used the UploadedFile table to add references to each of these tables in the shape of Foreign Keys.
I have created a few dummy scripts that might help you create these tables. Please let me know if this helps. Thank you!
SQL Scripts:
SalesOrder Table:
Create Table dbo.SalesOrder(
SalesOrderID int not null, identity primary key,
--enter whatever other columsn you have here
)
CreditOrder Table:
Create Table sbo.CreditOrder(
CreditOrderID int not null, identity primary key,
--enter whatever other columsn you have here
)
Invoice Table:
Create Table dbo.Invoice(
InvoiceID int not null, identity primary key,
--enter whatever other column you have here
)
UploadedFile Table:
Create table dbo.UploadedFile(
UploadFileID int not null identity primary key,
SalesOrderID int null Foreign Key References SalesOrder(SalesOrderID),
CreditOrderID int null Foreign Key References CreditOrder(CreditOrderID),
InvoiceID int null Foreign Key References CreditOrder(InvoiceID),
--enter whatever other columns you have here
)
I have an entity that excludes entities of the same type under certain conditions. In order to achieve this, I have an entity class like:
public class Entity
{
public int ID { get; set; }
public virtual ICollection<EntityExcludedEntity> ExcludedEntities { get; set; }
}
public class ExcludedEntity
{
public int ID { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public int EntityID { get; set; }
public virtual Entity Entity { get; set; }
public int ExcludedEntityID { get; set; }
public virtual Entity ExcludedEntity { get; set; }
}
//declared in the ExcludedEntity mapping class.
public ExcludedEntityMapping()
{
HasRequired(t => t.Entity).WithMany(t => t.ExcludedEntity).HasForeignKey(t => t.EntityID)
HasRequired(t => t.ExcludedEntity).WithMany(t => t.ExcludedEntity).HasForeignKey(t => t.ExcludedEntityID);
}
This causes in EF creating a third column and foreign key field called Entity_ID in my model. Seems like it thinks I have another relationship defined here but I don't understand why.
Here is the part related to foreign keys in the tables created:
.ForeignKey("dbo.Entities", t => t.EntityID)
.ForeignKey("dbo.Entities", t => t.ExcludedEntityID)
.ForeignKey("dbo.Entities", t => t.Entity_ID)
This post helped me find the answer.
Basically, EF cannot have two foreign keys to the same entity field. If you need to create two foreign key to the same entity you should bind them to different fields. So in this example:
public class Entity
{
public int ID { get; set; }
public virtual ICollection<EntityExcludedEntity> ExcludingEntities { get; set; }
public virtual ICollection<EntityExcludedEntity> ExcludedFromEntities { get; set; }
}
and this configuration:
public DBConceptAnswerExcludedAnswerMapping()
{
HasRequired(t => t.Entity).WithMany(t => t.ExcludingEntities).HasForeignKey(t => t.EntityID);
HasRequired(t => t.ExcludedEntity).WithMany(t => t.ExcludedFromEntities).HasForeignKey(t => t.ExcludedEntityID);
}
would solve the problem.
I am trying to learn how to use Entity Framework 6 with an already created database, without creating an .edmx file, i.e, using the DbContext and POCO classes.
These are my model classes:
[Table("Category")]
public class Category
{
[Key]
public long CategoryID { get; set; }
public string CategoryName { get; set; }
}
[Table("RegistrationForm")]
public class RegistrationForm
{
[Key]
public int RegistrationID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Country { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
[Table("RegistrationCategory")]
public class RegistrationCategory
{
[Key]
public long RegistrationCategory { get; set; }
public long RegistrationID { get; set; }//Foreign key to RegistrationID in RegistrationForm table in database
public long CategoryID { get; set; }//Foreign key to CategoryID in Category table in database
}
My DbContext class:
public class MyContext : DbContext
{
public virtual DbSet<RegistrationForm> RegistrationForm { get; set; }
public virtual DbSet<Category> Category { get; set; }
public virtual DbSet<RegistrationCategory> RegistrationCategory { get; set; }
}
Here I want to use the default model builder of DbContext.User can select multiple categories in the registration screen so the RegistrationCategory table will have multiple records for each registration. Therefore RegistrationForm and RegistrationCategory are in a one-to-many relationship.
How to write foreign key mappings between the above mentioned models?
How to bind data from Category table data in the mvc view(listbox) so that we can save one record in RegistrationForm table and multiple records in RegistrationCategory table without using loops (using mappings between the c# models) in Entity Framework 6?
The database schema that you have here is a Many to Many relationship between RegistrationForm and Category, with a join table. The RegistrationCategory Table is not necessary to be modeled in Entity Framework at all. You will need to use Entity Framework Fluent API to generate the correct mappings.
First, your RegistrationForm Table:
public class RegistrationForm
{
[Key]
public int RegistrationID { get; set; }
...
// add a navigation property ICollection<Category> to reference the categories
public virtual ICollection<Category> Categories { get; set; }
}
Next, the Category class:
public class Category
{
[Key]
public int CategoryID { get; set; }
public string CategoryName { get; set; }
//Navigation property to reference the RegistrationForms
public virtual ICollection<RegistrationForm> RegistrationForms { get; set; }
}
next, in your DbContext: note the change in pluralization, and the removal of the RegistrationCategory, you do not need a model class for it at all.
public class MyContext : DbContext
{
public virtual DbSet<RegistrationForm> RegistrationForms { get; set; }
public virtual DbSet<Category> Categories { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RegistrationForm>()
.HasMany(r => r.Categories)
.WithMany(c => c.RegistrationForms)
.Map(
m =>
{
m.MapLeftKey("RegistrationID");
m.MapRightKey("CategoryID");
m.ToTable("RegistrationCategory");
}
);
}
With this in place, you can now query all the Categories of a RegistrationForm or all the RegistrationForms of a Category.
foreach (var category in registrationForm.Categories)
{
//do whatever with each category
}
I'm trying to map a fairly "standard" category model using EF Code First
public class Category
{
public int ID { get; set; }
public int ParentID { get; set; }
public string Name { get; set; }
public Category ParentCategory { get; set; }
public List<Category> ChildCategories { get; set; }
}
I've got something along the lines of:
modelBuilder.Entity<Category>()
.HasOptional(t => t.ParentCategory)
.WithMany()
.HasForeignKey(t => t.ParentCategoryID)
.WillCascadeOnDelete();
But this doesn't seem to take care of ChildCategories??
Am I missing something?
To avoid the duplicate question argument, I followed the following, however didn't quite answer my specific query:
Code First Mapping for Entity Framework Hierarchy
Entity Framework CTP5 Code-First Mapping - Foreign Key in same table
Change your Entity to
public class Category
{
public int ID { get; set; }
public int? ParentID { get; set; }
public string Name { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual IList<Category> ChildCategories { get; set; }
}
Make ParentID nullable and to allow ChildCategories to be lazy loaded, make it virtual.