I am using Entity framework 4.1 in MVC 3 application. I have an entity where I have primary key consists of two columns ( composite key). And this is being used in another entity as foreign key. How to create the relationship ? In normal scnerios we use :
public class Category
{
public string CategoryId { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string CategoryId { get; set; }
public virtual Category Category { get; set; }
}
but what if category has two columns key ?
You can use either fluent API:
public class Category
{
public int CategoryId1 { get; set; }
public int CategoryId2 { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId1 { get; set; }
public int CategoryId2 { get; set; }
public virtual Category Category { get; set; }
}
public class Context : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Category>()
.HasKey(c => new {c.CategoryId1, c.CategoryId2});
modelBuilder.Entity<Product>()
.HasRequired(p => p.Category)
.WithMany(c => c.Products)
.HasForeignKey(p => new {p.CategoryId1, p.CategoryId2});
}
}
Or data annotations:
public class Category
{
[Key, Column(Order = 0)]
public int CategoryId2 { get; set; }
[Key, Column(Order = 1)]
public int CategoryId3 { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
[Key]
public int ProductId { get; set; }
public string Name { get; set; }
[ForeignKey("Category"), Column(Order = 0)]
public int CategoryId2 { get; set; }
[ForeignKey("Category"), Column(Order = 1)]
public int CategoryId3 { get; set; }
public virtual Category Category { get; set; }
}
I believe the easiest way is to use Data Annotation on the Navigation property like this:
[ForeignKey("CategoryId1, CategoryId2")]
public class Category
{
[Key, Column(Order = 0)]
public int CategoryId1 { get; set; }
[Key, Column(Order = 1)]
public int CategoryId2 { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
[Key]
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId1 { get; set; }
public int CategoryId2 { get; set; }
[ForeignKey("CategoryId1, CategoryId2")]
public virtual Category Category { get; set; }
}
In .NET Core and .NET 5 < the documentation only shows Data annotations (simple key).
https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-composite-key%2Csimple-key#foreign-key
However using the example from #LadislavMrnka you will get a error message like this:
System.InvalidOperationException: There are multiple properties with
the [ForeignKey] attribute pointing to navigation ''. To define a
composite foreign key using data annotations, use the [ForeignKey]
attribute on the navigation.
Using that error message you can write the code like this:
public class Product
{
[Key]
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId2 { get; set; }
public int CategoryId3 { get; set; }
[ForeignKey("CategoryId2,CategoryId3")]
public virtual Category Category { get; set; }
}
Fluent API (composite key) example from Microsoft:
internal class MyContext : DbContext
{
public DbSet<Car> Cars { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.HasKey(c => new { c.State, c.LicensePlate });
modelBuilder.Entity<RecordOfSale>()
.HasOne(s => s.Car)
.WithMany(c => c.SaleHistory)
.HasForeignKey(s => new { s.CarState, s.CarLicensePlate });
}
}
public class Car
{
public string State { get; set; }
public string LicensePlate { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public List<RecordOfSale> SaleHistory { get; set; }
}
public class RecordOfSale
{
public int RecordOfSaleId { get; set; }
public DateTime DateSold { get; set; }
public decimal Price { get; set; }
public string CarState { get; set; }
public string CarLicensePlate { get; set; }
public Car Car { get; set; }
}
Related
public class Message
{
[Key]
public int MeesageId { get; set; }
public int SenderId { get; set; }
[ForeignKey("PersonId")]
public virtual Person Sender { get; set; }
public int ReceiverId { get; set; }
[ForeignKey("PersonId")]
public virtual Person Receiver { get; set; }
public string Content { get; set; }
public DateTime CreatedOn { get; set; }
public bool Seen { get; set; }
}
public class Person
{
public string Username { get; set; }
[Key]
public int PersonId { get; set; }
}
I'm getting this error:
The ForeignKeyAttribute on property 'Receiver' on type 'Finder.Models.Message' is not valid. The foreign key name 'PersonId' was not found on the dependent type 'Finder.Models.Message'. The Name value should be a comma-separated list of foreign key property names.
What I think I should do is rename ReceiverId to PersonId, so it matches the foreign key, but then the property names would be too messy. Any help would be appreciated
The ForeignKey attribute specifies which int property is the foreign key for the specified navigation property. So
public class Message
{
[Key]
public int MeesageId { get; set; }
public int SenderId { get; set; }
[ForeignKey("SenderId")]
public virtual Person Sender { get; set; }
public int ReceiverId { get; set; }
[ForeignKey("ReceiverId")]
public virtual Person Receiver { get; set; }
public string Content { get; set; }
public DateTime CreatedOn { get; set; }
public bool Seen { get; set; }
}
It is better to use fluent api.
for example :
public class Message
{
public int MeesageId { get; set; }
public int SenderId { get; set; }
public virtual Person Sender { get; set; }
public int ReceiverId { get; set; }
public virtual Person Receiver { get; set; }
public string Content { get; set; }
public DateTime CreatedOn { get; set; }
public bool Seen { get; set; }
}
public class Person
{
public string Username { get; set; }
public int PersonId { get; set; }
public List<Message> SenderMessages { get; set; }
public List<Message> RecieverMessages { get; set; }
}
public class MessageConfigurations : IEntityTypeConfiguration<Message>
{
public void Configure(EntityTypeBuilder<Message> builder)
{
builder.HasKey(x => x.MeesageId);
builder.HasOne(x => x.Sender)
.WithMany(x => x.SenderMessages)
.HasForeignKey(x => x.SenderId)
.OnDelete(DeleteBehavior.NoAction);
builder.HasOne(x => x.Receiver)
.WithMany(x => x.RecieverMessages)
.HasForeignKey(x => x.ReceiverId)
.OnDelete(DeleteBehavior.NoAction);
}
}
public class PersonConfigurations : IEntityTypeConfiguration<Person>
{
public void Configure(EntityTypeBuilder<Person> builder)
{
builder.HasKey(x => x.PersonId);
}
}
I have created two tabled called Customer and destination . CustomerCode is a primary key in Customer and Foreign key is Destination .when i delete a customer the destination will be deleted.
public class tblCustomerDetails
{
[Key]
public string CustomerCode { get; set; }
public string CustomerName { get; set; }
}
public class tblDestinationDetails
{
[Key]
public string DestinationCode { get; set; }
[ForeignKey("tblCustomerDetails")]
public string CustomerCode { get; set; }
public tblCustomerDetails tblCustomerDetails { get; set; }
public string DestinationName { get; set; }
}
public class tblOrderDetails
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int SrNo { get; set; }
public int OrderNo { get; set; }
[ForeignKey("tblCustomerDetails")]
public string CustomerCode { get; set; }
public tblCustomerDetails tblCustomerDetails { get; set; }
[ForeignKey("tblDestinationDetails")]
public string DestinationCode { get; set; }
public tblDestinationDetails tblDestinationDetails { get; set; }
}
Your probable model will be
public class Customer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CustomerCode { get; set; }
public virtual Destination destination { get; set; }//relationship with Destination
}
public class Destination
{
public virtual Customer customer { get; set; }//relationship with Customer
[Key, ForeignKey("User")]
public int CustomerCode { get; set; }
}
You need to use the fluent API and add following code in DBContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.HasOptional(d => d.Destination)
.WithOptionalDependent()
.WillCascadeOnDelete(true);
}
I crete table in asp.net mvc but when i crete the migration this error message show
Introducing FOREIGN KEY constraint 'FK_dbo.DailyTransactions_dbo.Contracts_ContractId' on table 'DailyTransactions' 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. See previous errors.
this is DailyTransactions table :
public class DailyTransactions
{
[Key]
public int DailyTransactions_Id { get; set; }
public double Account { get; set; }
public string Account_Name { get; set; }
public double Debit { get; set; }
public double Credit { get; set; }
public DateTime Date { get; set; }
public string Remarks { get; set; }
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual Customers customers { get; set; }
public int ContractId { get; set; }
[ForeignKey("ContractId")]
public virtual Contracts contracts { get; set; }
}
and this contract table :
public class Contracts
{
[Key]
public int Contracts_Id { get; set; }
public int Contract_Num { get; set; }
public DateTime Contract_Start { get; set; }
public DateTime Contract_End { get; set; }
public string Status { get; set; }
public string TypeOfRent { get; set; }
public double AmountOfRent { get; set; }
public double Total { get; set; }
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual Customers customers { get; set; }
public int sectionsId { get; set; }
[ForeignKey("sectionsId")]
public virtual Sections sections { get; set; }
}
Try to turn off CascadeDelete for DailyTransactions and Contracts:
modelBuilder.Entity<DailyTransactions>()
.HasRequired(c => c.Contracts)
.WithMany()
.WillCascadeOnDelete(false);
For example:
public class YourDBContext: DbContext
{
public YourDBContext(): base()
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<DailyTransactions>()
.HasRequired(c => c.Contracts)
.WithMany()
.WillCascadeOnDelete(false);
}
}
I'm new to Entity Framework and I've run into some problems while trying to implement my ERD Code First. The situation is as follows:
ERD
A product has a group of questions (QuestionGroup). A QuestionGroup has multiple questions and can belong to multiple Questionaires. A Questionaire basically has a questiongroup and a questionorder. The questionorder is supposed to keep the position of a question within that questionaire. The Questionaire table is needed because a QuestionGroup can have multiple questionorders, and a questionorder can belong to multiple questiongroups.
Because I'm trying my best to keep this post succinct I won't post all my classes, unless you ask to see them. The Entity classes I've made look like this:
public class Question
{
[Key]
public int Id { get; set; }
[MaxLength(2000)]
[Required]
public string Text { get; set; }
public Answer Answer { get; set; }
public QuestionType Type { get; set; }
public ICollection<QuestionAnswerOption> Options { get; set; }
public ICollection<QuestionOrder> Orders { get; set; }
[ForeignKey("FollowupQuestion")]
public int QuestionId { get; set; }
public virtual Question FollowupQuestion { get; set; }
[ForeignKey("Questiongroup")]
public int QuestionGroupId { get; set; }
public virtual QuestionGroup Questiongroup { get; set; }
}
public class QuestionOrder
{
[Key]
public int Id { get; set; }
public int Position { get; set; }
[ForeignKey("Question")]
[Required]
public int QuestionId { get; set; }
public virtual Question Question { get; set; }
[ForeignKey("Questionaire")]
[Required]
public int QuestionaireId { get; set; }
public virtual Questionaire Questionaire { get; set; }
}
public class Product
{
[Key]
public int Id { get; set; }
[MaxLength(150)]
[Required]
[Index(IsUnique = true)]
public string Name { get; set; }
[MaxLength(500)]
public string Summary { get; set; }
[MaxLength(500)]
public string Examples { get; set; }
public virtual QuestionGroup QuestionGroup { get; set; }
}
public class QuestionGroup
{
[Key]
public int Id { get; set; }
public ICollection<Question> Questions { get; set; }
[ForeignKey("Product")]
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
public class Questionaire
{
[Key]
public int Id { get; set; }
public QuestionGroup Group { get; set; }
public QuestionOrder Order { get; set; }
}
The errors I'm getting look like this:
QuestionGroup_Product_Source: : Multiplicity is not valid in Role 'QuestionGroup_Product_Source' in relationship 'QuestionGroup_Product'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
QuestionOrder_Questionaire_Source: : Multiplicity is not valid in Role 'QuestionOrder_Questionaire_Source' in relationship 'QuestionOrder_Questionaire'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
QuestionType_Question_Source: : Multiplicity is not valid in Role 'QuestionType_Question_Source' in relationship 'QuestionType_Question'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
The question we've arrived at here is:
Does anyone here know of a way to fix the relations between my tables? Maybe my ERD needs some improvements too, but I think the problem is that I'm missing a few things with my Code First implementation.
what about this design for you;)
public class Customer
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CustomerId { get; set; }
public string CompanyName { get; set; }
public virtual ICollection<ContactInfo> ContactInfoes { get; set; }
public virtual ICollection<ProductQuestionaireReport> Reports { get; set; }
}
public class ContactInfo
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int ContactInfoId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Telephone { get; set; }
[ForeignKey("Customer")]
public int CustomerId { get; set; }
public virtual Customer Customer { get; set; }
}
public class ProductQuestionaireReport
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ProductQuestionaireReportId { get; set; }
[ForeignKey("Product")]
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public virtual QuestionaireResult Result { get; set; }
[ForeignKey("Customer")]
public int CustomerId { get; set; }
public virtual Customer Customer { get; set; }
public bool Haste { get; set; }
public string Status { get; set; }
public string Comment { get; set; }
}
public class QuestionaireResult
{
[Key, ForeignKey("ProductQuestionaireReport")]
public int ProductQuestionaireReportId { get; set; }
[ForeignKey("Questionaire")]
public int QuestionaireId { get; set; }
public virtual Questionaire Questionaire { get; set; }
public DateTime SurveyCreated { get; set; }
public Double Costs { get; set; }
public Double FunctionPoints { get; set; }
public virtual ProductQuestionaireReport ProductQuestionaireReport { get; set; }
public virtual ICollection<QuestionaireAnswer> Answers { get; set; }
}
public class Product
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ProductId { get; set; }
public string Name { get; set; }
public string Summary { get; set; }
public string Examples { get; set; }
public virtual ICollection<Questionaire> Questionaires { get; set; }
public virtual ICollection<ProductQuestionaireReport> Reports { get; set; }
}
public class QuestionaireAnswer
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QuestionaireAnswerId { get; set; }
[ForeignKey("QuestionaireResult")]
public int QuestionaireResultId { get; set; }
public virtual QuestionaireResult QuestionaireResult { get; set; }
[ForeignKey("QuestionaireQuestion")]
public int QuestionaireId { get; set; }
public virtual QuestionaireQuestion QuestionaireQuestion { get; set; }
[ForeignKey("Answer")]
public int AnswerId { get; set; }
public virtual QuestionOption Answer { get; set; }
public string Text { get; set; }
public virtual ICollection<string> Files { get; set; }
}
public class Questionaire
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QuestionaireId { get; set; }
[ForeignKey("Product")]
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public virtual ICollection<QuestionaireQuestion> QuestionaireQuestions { get; set; }
public virtual ICollection<QuestionaireResult> Results { get; set; }
}
public class Question
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QuestionId { get; set; }
[ForeignKey("ParentQuestion")]
public int ParentQuestionId { get; set; }
public virtual Question ParentQuestion { get; set; }
public virtual QuestionType QuestionType { get; set; }
public virtual ICollection<QuestionOption> Options { get; set; }
}
public class QuestionType
{
[Key, ForeignKey("Question")]
public int QuestionId { get; set; }
public string Text { get; set; }
public bool IsOpenQuestion { get; set; }
public virtual Question Question { get; set; }
}
public class QuestionOption
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QuestionAnswerOptionId { get; set; }
[ForeignKey("Question")]
public int QuestionId { get; set; }
public string Text { get; set; }
public virtual Question Question { get; set; }
}
public class QuestionaireQuestion
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QuestionaireQuestionId { get; set; }
[ForeignKey("Questionaire")]
public int QuestionaireId { get; set; }
public virtual Questionaire Questionaire { get; set; }
[ForeignKey("Question")]
public int QuestionId { get; set; }
public int DisplayOrder { get; set; }
public virtual Question Question { get; set; }
}
also you must add this to OnModelCreating
modelBuilder.Entity<Customer>().HasMany(c => c.ContactInfoes)
.WithRequired(i => i.Customer).HasForeignKey(i => i.CustomerId).WillCascadeOnDelete(false);
modelBuilder.Entity<Question>().HasMany(q => q.Options)
.WithRequired(o => o.Question).HasForeignKey(o => o.QuestionId).WillCascadeOnDelete(false);
modelBuilder.Entity<QuestionaireResult>().HasMany(r => r.Answers)
.WithRequired(a => a.QuestionaireResult).HasForeignKey(o => o.QuestionaireResultId).WillCascadeOnDelete(false);
check it and feedback me ...
I have few Domain Models - Address, Customer, Employee, StoreLocation. Address has many to one relationship with Customerand Employee and one to one relationship with StoreLocation.
public class Address
{
public int Id;
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Address> Addresses { get; set; }
}
public class StoreLocation
{
public int Id { get; set; }
public string ShortCode { get; set; }
public string Description { get; set; }
public Address Address { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Dob { get; set; }
public IList<Address> Addresses { get; set; }
}
How to Map this relationship?. I am using ASP.NET MVC 3.0 and Entity Framework 4.1.
If you are using code-first (I think you want this, else, you have to edit your Q), the first way is the way explained below:
Entities:
public class Address {
[Key]
public int Id { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public virtual Customer Customer { get; set; }
public virtual StoreLocation StoreLocation { get; set; }
public virtual Employee Employee { get; set; }
public int? CustomerId { get; set; }
public int? EmployeeId { get; set; }
}
public class Customer {
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class StoreLocation {
[Key]
public int Id { get; set; }
public string ShortCode { get; set; }
public string Description { get; set; }
public virtual Address Address { get; set; }
}
public class Employee {
[Key]
public int Id { get; set; }
public string Name { get; set; }
public DateTime Dob { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
DbContext inherited class:
public class ManyOneToManyContext : DbContext {
static ManyOneToManyContext() {
Database.SetInitializer<ManyOneToManyContext>(new ManyOneToManyInitializer());
}
public DbSet<Address> Addresses { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<StoreLocation> StoreLocations { get; set; }
public DbSet<Employee> Employees { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
modelBuilder.Entity<Customer>().HasMany(c => c.Addresses).WithOptional(a => a.Customer).HasForeignKey(a => a.CustomerId);
modelBuilder.Entity<StoreLocation>().HasRequired(s => s.Address).WithOptional(a => a.StoreLocation).Map(t => t.MapKey("AddressId"));
modelBuilder.Entity<Employee>().HasMany(e => e.Addresses).WithOptional(a => a.Employee).HasForeignKey(e => e.EmployeeId);
}
}
Context Initializer:
public class ManyOneToManyInitializer : DropCreateDatabaseAlways<ManyOneToManyContext> {
protected override void Seed(ManyOneToManyContext context) {
}
}
That will create the db-schema below:
Let me know if you have any questions or need clarifications on any part.