Mapping composite keys using EF code first
You definitely need to put in the column order, otherwise how is SQL Server supposed to know which one goes first? Here's what you would need to do in your code:
public class MyTable
{
[Key, Column(Order = 0)]
public string SomeId { get; set; }
[Key, Column(Order = 1)]
public int OtherId { get; set; }
}
You can also look at this SO question. If you want official documentation, I would recommend looking at the official EF website. Hope this helps.
EDIT: I just found a blog post from Julie Lerman with links to all kinds of EF 6 goodness. You can find whatever you need here.
How do I map a composite primary key in Entity Framework 4 code first?
You could also use
HasKey(u => new { u.SubscriptionID, u.UserName });
Edit:
One limitation I have found is that the following do not work:
public ProjectAssignmentConfiguration()
{
HasKey(u => u.Employee.EmployeeId);
HasKey(u => u.Project.ProjectId);
}
or
public ProjectAssignmentConfiguration()
{
HasKey(u => new { u.Employee.EmployeeId, u.Project.ProjectId });
}
So how do you set up an entity where the join table has a primary key that is composed of foreign keys?
Composite primary keys and foreign keys Code First
You have to use the ReverseProperty annotation.
public class UserReview
{
[Key, Column(Order = 0), ForeignKey("Reviewer")]
public string ReviewerId { get; set; } //The user that wrote the review
[Key, Column(Order = 1), ForeignKey("ReviewedUser")]
public string ReviewedUserId { get; set; } //The user who was reviewed
[Required]
[Range(1, 5)]
public int Rating { get; set; }
[Required]
public DateTimeOffset DateCreated { get; set; }
[Required]
public virtual User Reviewer { get; set; }
[Required)]
public virtual User ReviewedUser { get; set; }
}
public class User
{
[Key]
[Required]
public string SubjectId { get; set; }
public User()
{
Reviews = new List<UserReview>();
}
[InverseProperty("ReviewedUser")] // or Reviewer.
public virtual ICollection<UserReview> Reviews { get; set; }
}
In my opinon, that's the kind of thing that are easier to configure when using fluent mapping.
Using Composite Keys with Entity Framework Core and Using part of them as foreign keys
Answering your concrete question
Is there anyway to get efcore to do something like this using the code first approach?
Sure there is. Just the conventional foreign key names apparently don't work, so you have to configure the FK properties explicitly (via HasForeignKey
fluent API).
e.g. either
builder.Entity<PurchaseOrderLine>()
.DefaultConfigure(p => new { p.Company, p.PONum, p.LineNum })
.HasOne(e => e.PurchaseOrder)
.WithMany(e => e.PurchaseOrderLines)
.HasForeignKey(e => { e.Company, e.PONum }); // <--
or
builder.Entity<PurchaseOrder>()
.DefaultConfigure(p => new { p.Company, p.PONum })
.HasMany(e => e.PurchaseOrderLines)
.WithOne(e => e.PurchaseOrder)
.HasForeignKey(e => { e.Company, e.PONum }); // <--
Note that both Has
/ With
pairs represent one and the same relationship, so it's better to do it only in one place in order to avoid conflicting configurations.
Mapping composite foreign key to composite primary key where the foreign key is also a primary key
The main difference between your question and the one I suggested as duplicate is that your ForeignKey
attributes don't refer -
- from a primitive property to a navigation property
- from a navigation property to a primitive property
In your case, the reference is from a primitive property to another primitive property, in another type. Also, little detail, VirtualMachine.Datetime
should be a property, not a member. But I have to admit that the "duplicate" didn't cover your case.
So let's try to make this into a comprehensive answer how to handle this situation in Entity Framework 6. I'll use an abstracted model to explain the various options:
public class Parent
{
public int Id1 { get; set; } // Key
public int Id2 { get; set; } // Key
public string Name { get; set; }
public virtual List<Child> Children { get; set; }
}
public class Child
{
public int Id1 { get; set; } // Key
public int Id2 { get; set; } // Key
public int Id3 { get; set; } // Key
public string Name { get; set; }
public virtual Parent Parent { get; set; }
}
There are three options to setup the mappings.
Option 1
Data annotations, ForeignKey
attribute:
public class Parent
{
[Key]
[Column(Order = 1)]
public int Id1 { get; set; }
[Key]
[Column(Order = 2)]
public int Id2 { get; set; }
public string Name { get; set; }
public virtual List<Child> Children { get; set; }
}
public class Child
{
[Key]
[Column(Order = 0)]
public int Id1 { get; set; }
[Key]
[Column(Order = 1)]
public int Id2 { get; set; }
[Key]
[Column(Order = 2)]
public int Id3 { get; set; }
public string Name { get; set; }
[ForeignKey("Id1,Id2")]
public virtual Parent Parent { get; set; }
}
As you see, here the ForeignKey
attribute refers from a navigation property to primitive properties. Also, the absolute numbers in the column order don't matter, only their sequence.
Option 2
Data annotations, InverseProperty
attribute:
public class Parent
{
[Key]
[Column(Order = 1)]
public int Id1 { get; set; }
[Key]
[Column(Order = 2)]
public int Id2 { get; set; }
public string Name { get; set; }
public virtual List<Child> Children { get; set; }
}
public class Child
{
[Key]
[Column(Order = 0)]
[InverseProperty("Children")]
public int Id1 { get; set; }
[Key]
[Column(Order = 1)]
[InverseProperty("Children")]
public int Id2 { get; set; }
[Key]
[Column(Order = 2)]
public int Id3 { get; set; }
public string Name { get; set; }
public virtual Parent Parent { get; set; }
}
InverseProperty
points from one or more properties in a type at one end of a relationship to a navigation property in the type on the other end of the relationship. Another way to achieve the same mapping is to apply [InverseProperty("Parent")]
on both key properties of Parent
.
Option 3
Fluent mapping:
modelBuilder.Entity<Parent>().HasKey(p => new { p.Id1, p.Id2 });
modelBuilder.Entity<Child>().HasKey(p => new { p.Id1, p.Id2, p.Id3 });
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithRequired(c => c.Parent)
.HasForeignKey(c => new { c.Id1, c.Id2 });
As said in the comments, fluent mapping is less error-prone than data annotations. Data annotations offer too many options to configure mappings and it's not always easy to see which parts are connected. That's why fluent mapping is my favorite.
Entity Framework Core
In EF-core (current version 3.1.6) composite primary keys can't be modeled by data annotations. It throws a run-time exception:
Entity type 'Parent' has composite primary key defined with data annotations. To set composite primary key, use fluent API.
So for EF-core only option 3 is feasible. The mapping is almost identical:
modelBuilder.Entity<Parent>().HasKey(p => new { p.Id1, p.Id2 });
modelBuilder.Entity<Child>().HasKey(p => new { p.Id1, p.Id2, p.Id3 });
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithOne(c => c.Parent) // Different here
.HasForeignKey(c => new { c.Id1, c.Id2 });
Entity Framework Core Composite Key In One To Many Mapping
So, to close this out -
This is something not currently possible in EF Core (2.1) and remains to be seen if it will be added in future versions - as present it only supports mapping through a single property
Entity Framework Code First mapping composite key in join table where foreign keys are not exposed in model
Each column of the composite primary key has to be mapped to a scalar property in the entity. Your ideal solution will not work.
In Option 2 you would have to create a unique key also and check for unique key violations. EF does not have built in support for unique keys. Hence I would pick option 1.
Creating Composite Key Entity Framework
If Device table has composite primary key, then you need same composite foreign key on your NotificationMessageDevice table. How would SQL find Device without full primary key? Also you should make these fields to be part of NotificationMessageDevice table primary key. Otherwise you can't guarantee primary key will be unique:
public class NotificationMessageDevice
{
[Column(Order = 0), Key, ForeignKey("NotificationMessage")]
public int NotificationMessage_ID { get; set; }
[Column(Order = 1), Key, ForeignKey("Device")]
public int Device_ID { get; set; }
[Column(Order = 2), Key, ForeignKey("Device")]
public string Device_UDID { get; set; }
[Column(Order = 3), Key, ForeignKey("Device")]
public string Device_ApplicationKey { get; set; }
public virtual Device Device { get; set; }
public virtual NotificationMessage NotificationMessage { get; set; }
}
Related Topics
Why Appdomain.Currentdomain.Basedirectory Not Contains "Bin" in ASP.NET App
Capture Multiple Key Downs in C#
How to Change the Background Color of All Other Forms from One Form
Differences Between Iqueryable, List, Ienumerator
How to Discover the "Path" of an Embedded Resource
Illustrating Usage of the Volatile Keyword in C#
How to Check All Properties of an Object Whether Null or Empty
Newtonsoft Add JSONignore at Runtime
Attach Debugger in C# to Another Process
C# - Fill a Combo Box with a Datatable
How to Pass Parameters to the Custom Action
What Is the Use for Task.Fromresult<Tresult> in C#