How should I define a composite primary key for an

2020-03-26 06:19发布

问题:

I'm working on a Core MVC project that reads data (no writing required) from a pre-existing database. Unfortunately this database is a total mess, but I cannot change anything in it (and even if I could, I wouldn't touch it with a 10-foot pole).

Some of the relevant problems of the database are the following:

  • The tables don't have any foreign key relations to each other, and contain data that would have best been entered into a sub-table; whoever created the database seems to have used tables as if they were Excel spreadsheets.
  • There are no primary or foreign keys defined.
  • The names of tables and columns seem to be the initials of the data contained (for an imaginary example, a table that one would normally name "Customer" would instead be named "dbbc" for "Database - Business Customer").
  • The tables are not in the "dbo" schema.

Nevertheless I am forced to read data from this database and would prefer to use Entity Framework to do so.

The database is hosted in SQL Server 2008R2.

I could make it work with no issue on one of the tables I have to use, by using attributes such as [Table], [Column] and [Key] to use a class with proper property names mapped to the real column names. Another table however is proving to be a problem.

This table doesn't have a single column that could be considered a primary key, but I found that a combination of two columns is unique for each row, so they could be considered a composite primary key (even if they're not defined as such in the database).

I've defined a class like the following (I've changed the names to hide the identity of this database, as much as I'd like to name and shame):

[Table("dbbo", Schema = "schema")]
public class Order
{
    [Key]
    [Column("order", Order = 0)]
    public string OrderNo { get; set; }

    [Key]
    [Column("order_line", Order = 1)]
    public string OrderLineNo { get; set; }

    [Column("qty")]
    public double Quantity { get; set; }

    [Column("pr")]
    public double Price { get; set; }
}
  • I've added a [Key] attribute to both columns that make up the imaginary composite primary key.
  • I've added a value to the Order property of the [Column] attribute as I read that it's required for composite keys.
  • I've only mapped the columns I need to use, as the table has about 10 times more columns than are relevant to this project.

Still, trying to run the project produces an error:

InvalidOperationException: The entity type 'MyProject.Models.Order' requires a primary key to be defined.

Is there anything more I can add to make this table work with Entity Framework?

EDIT: I discovered it works if I define it in the context's OnModelCreating method, like modelBuilder.Entity<Order>().HasKey(ord => new { ord.OrderNo, ord.OrderLineNo });. However I'd still prefer to do it with attributes alone, if possible.

回答1:

Your configuration works fine with EF6. But you must be careful when using Data Annotations with EF Core (looks like Fluent API will be the preferred approach there).

The Keys(primary) section of the documentation explicitly states:

You can also use the Fluent API to configure multiple properties to be the key of an entity (known as a composite key). Composite keys can only be configured using the Fluent API - conventions will never setup a composite key and you can not use Data Annotations to configure one.

So you really need to use:

modelBuilder.Entity<Order>().HasKey(e => new { e.OrderNo, e.OrderLineNo });