作为外键的复合键

我在 MVC 3应用程序中使用实体框架4.1。我有一个实体,其中主键由两列(组合键)组成。这在另一个实体中作为外键使用。如何建立关系?在正常的场景中,我们使用:

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; }
}

但是如果分类有两列键呢?

101768 次浏览

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; }
}