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.
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; }
}
How do I create composite key which is combination of base class and derived class in Entity Framework Core
Problem is that [Not Mapped]
data annotation is not ignoring the table creation because Fluent API
always has the higher priority than the data annotations (attributes). So you can call modelBuilder.Ignore<BaseEntity>();
after the BaseEntity
configuration in the OnModelCreating
but calling modelBuilder.Ignore<BaseEntity>();
will lose the BaseEntity
configurations.
So best solution would be:
Write the configuration for BaseEntity
as follows:
public class BaseEntityConfigurations<TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : BaseEntity
{
public virtual void Configure(EntityTypeBuilder<TEntity> builder)
{
builder.Property(x => x.Key).HasDefaultValueSql("NEWID()");
//CreatedDate
builder.Property(x => x.DateCreated).HasDefaultValueSql("GETDATE()");
//Updated Date
builder.Property(x => x.DateModified).HasDefaultValueSql("GETDATE()");
}
}
Then write the configuration for Student
as follows:
public class StudentConfigurations : BaseEntityConfigurations<Student>
{
public override void Configure(EntityTypeBuilder<Student> builder)
{
base.Configure(builder); // Must call this
// composite key
builder.HasKey(c => new { c.Key, c.Id });
}
}
Then in the OnModelCreating
as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new StudentConfigurations());
}
Now everything should working fine!
Note: If you have already a database then it would not work on migration. You have to generate brand new table with initial migration because Entity Framework core cannot change primary key with migration.
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
How create composite foreign key to table with composite primary key
Composite foreign key requires ForeignKey
attribute to be applied on the navigation property specifying the comma separated list of the foreign key property names:
If you add the ForeignKey attribute to a foreign key property, you should specify the name of the associated navigation property. If you add the ForeignKey attribute to a navigation property, you should specify the name of the associated foreign key(s). If a navigation property has multiple foreign keys, use comma to separate the list of foreign key names.
Since you have no navigation property in the Event
class, you should apply it on the corresponding navigation property in the EventDocument
class:
[ForeignKey("DocID1, DocID2")]
public virtual ICollection<Event> Events { get; set; }
and the issue should be resolved.
But I personally find setting up relationships with Fluent API to be much easier to understand and less error prone. For instance, the same can be achieved by the following fluent configuration:
modelBuilder.Entity<EventDocument>()
.HasMany(e => e.Events)
.WithRequired() // or .WithOptional()
.HasForeignKey(e => new { e.DocID1, e.DocID2 });
Same btw for the composite PKs (instead of all these Key
/ Column(Order = ...)
attributes):
modelBuilder.Entity<Event>()
.HasKey(e => new { e.ID1, e.ID2 });
modelBuilder.Entity<EventDocument>()
.HasKey(e => new { e.ID1, e.ID2 });
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.
Entity Framework - Composite Primary Key formed by 3 Foreign Keys
Based on your model, Drm
should not have its own "id". So, it should look like this:
public partial class Drm
{
//remove this property
//[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
//public int DrmId { get; set; }
// one-to-one relation
[Key, Column(Order = 1), ForeignKey("CustomerSite")]
public int CustomerSiteId { get; set; }
// one-to-one relation
[Key, Column(Order = 2), ForeignKey("Provider")]
public int ProviderId { get; set; }
// one-to-one relation
[Key, Column(Order = 3), ForeignKey("DrmType")]
public int DrmTypeId { get; set; }
public virtual Provider Provider { get; set; }
public virtual CustomerSite CustomerSite { get; set; }
public virtual DrmType DrmType { get; set; }
}
Since you provided the [Key]
attribute at the class declaration, you do not need to declare HasKey
in the model binding. So, remove this line:
modelBuilder.Entity<Drm>()
.HasKey(d => new { d.ProviderId, d.CustomerSiteId, d.DrmType });
EDIT
Provider
, CustomerSite
and DrmType
must have a collection of Drm
.
public partial class Provider
{
//...
//public virtual Drm Drm { get; set; }
public virtual ICollection<Drm> Drms { get; set; }
}
public partial class CustomerSite
{
//...
//public virtual Drm Drm { get; set; }
public virtual ICollection<Drm> Drms { get; set; }
}
public partial class DrmType
{
//public virtual Drm Drm { get; set; }
public virtual ICollection<Drm> Drms { get; set; }
}
Hope this helps!
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?
Foreign key with composite key in EF Core
In order to configure the desired relationship as required and cascade delete, you can use the following inside the ProductAttribute
entity configuration block:
b.HasMany(e => e.Values)
.WithOne()
.IsRequired();
IsRequired
is enough because by convention cascade delete is on for required and off for optional relationships. Of course you can add .OnDelete(DeleteBehavior.Cascade)
if you want - it will be redundant, but won't hurt.
Please note that the relationships should be configured in a single place. So do it either in ProductAttribute
or ProductAttributeValue
, but never in both (error prone, may cause unexpected conflicting or overriding configuration issues).
For completeness, here is how you can configure the same inside ProductAttributeValue
configuration (requires explicitly providing the HasOne
generic type argument due to the lack of navigation property):
b.HasOne<ProductAttribute>()
.WithMany(e => e.Values)
.IsRequired();
Related Topics
Repository Pattern Step by Step Explanation
What Is the Fastest Way to Create a Checksum for Large Files in C#
How to Run and Interact with an Async Task from a Wpf Gui
Identityserver4 Register Userservice and Get Users from Database in ASP.NET Core
Check If Instance Is of a Type
Implementing Collectionchanged
Why Func<T,Bool> Instead of Predicate<T>
What's the Difference Between Returning Void and Returning a Task
Get File Name from Uri String in C#
What Is the "Cost" of .Net Reflection
How to Return a File Using Web API
Non-Static Method Requires a Target
Updating an Item Property Within Ienumerable But the Property Doesn't Stay Set