Creating Composite Key Entity Framework

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



Leave a reply



Submit