Entity Framework 列序号 <> 主键序号的组合键定义

Entity Framework composite key definition with column ordinal <> primary key ordinal

给定一个table作为

CREATE TABLE pk_ordinal_test
(
 C1 INT NOT NULL,
 C2 INT NOT NULL,
 C3 INT NOT NULL
)
ALTER TABLE pk_ordinal_test ADD PRIMARY KEY (C3, C1);

注意主键已经定义为C3、C1,列顺序为C1、C3。

主键的 Entity Framework 代码优先配置是否应该在 primary key ordinal:

HasKey(x => new { x.C3, x.C1 });

或者应该在列序数:

HasKey(x => new { x.C1, x.C3 });

使用 MSI 安装的 EF Tools v6.1.3,使用 Visual Studio -> 添加 -> 新项目 -> ADO.Net 实体数据模型。选择代码优先,生成以下内容:

public partial class pk_ordinal_test
{
    [Key]
    [Column(Order = 0)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int C1 { get; set; }

    public int C2 { get; set; }

    [Key]
    [Column(Order = 1)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int C3 { get; set; }
}

上面定义的主列顺序不正确。

如果您的实体具有复合外键,那么您必须指定用于相应主键属性的相同列顺序,因此您必须select的选项是主键序号:

HasKey(x => new { x.C3, x.C1 });

如果您在 Visual Studio 2015 年从数据库生成代码优先模型,它将按照模式中列的顺序为键分配一个 Order 属性。因此,如果您执行 Find 等 LINQ 查询,您需要对 "column order" 中的主键进行排序,因为键是按架构列顺序排序的。

如果您要将数据库优先方法与 EDMX 结合使用,则指南还应根据设计器中的列顺序使用主键顺序。因此,如果 C1 在 C3 之前并且都是主键,则 find 方法将用作 Find (C1, C3)。

因此有两项证据,对我来说,当使用复合键指定主键顺序时,当在代码或设计器中生成模型时,您将使用列顺序,因为代码生成器将使用列顺序订购钥匙。

OP 的示例(这是我在 VS 扩展中查询的结果生成的)有点不同,因为模式正在生成主键排序。因此,让我们退后一步,从 "hey, I'm going to generate a code first model from your schema. I see C1 and C3 are primary keys. When I generate the code first model from your schema I'm going to iterate columns in order AND assign an Order attribute for the primary key columns." 看一下这会导致直接基于列顺序的使用模式。

因此,OP 再次从模式生成模型,EF 根据 schema/column 订单在代码中生成订单。

SET ANSI_NULLS ON
GO 

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[UserSetting](
    [userId] [INT] NOT NULL,
    [deleted] [BIT] NULL CONSTRAINT [DF_UserSetting_deleted]  DEFAULT ((0)),
    [key] [NVARCHAR](MAX) NULL,
    [lastUpdatedUtc] [DATETIME2](7) NULL CONSTRAINT [DF_UserSetting_lastUpdatedUtc]  DEFAULT (GETUTCDATE()),
    [notificationMessage] [NVARCHAR](MAX) NULL,
    [notificationType] [INT] NULL,
    [stringValue] [NVARCHAR](MAX) NULL,
    [synced] [BIT] NULL,
    [userSettingId] [NCHAR](36) NOT NULL,
    [timestamp] [DATETIME2](7) NULL CONSTRAINT [DF_UserSetting_timestamp]  DEFAULT (GETUTCDATE()),
    [modelVersion] [INT] NULL CONSTRAINT [DF_UserSetting_modelVersion]  DEFAULT ((0)),
    [preview] [BIT] NULL,
    [createdUtc] [DATETIME2](7) NULL CONSTRAINT [DF_UserSetting_createdUtc]  DEFAULT (GETUTCDATE()),
 CONSTRAINT [PK_UserSetting] PRIMARY KEY CLUSTERED 
(
    [userSettingId] ASC,
    [userId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

以下是 Visual Studio Code First 从数据库中生成的内容:

[Table("UserSetting")]
    public partial class UserSetting
    {
        [Key]
        [Column(Order = 0)]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int userId { get; set; }

        public bool? deleted { get; set; }

        public string key { get; set; }

        [Column(TypeName = "datetime2")]
        public DateTime? lastUpdatedUtc { get; set; }

        public string notificationMessage { get; set; }

        public int? notificationType { get; set; }

        public string stringValue { get; set; }

        public bool? synced { get; set; }

        [Key]
        [Column(Order = 1)]
        [StringLength(36)]
        public string userSettingId { get; set; }

        [Column(TypeName = "datetime2")]
        public DateTime? timestamp { get; set; }

        public int? modelVersion { get; set; }

        public bool? preview { get; set; }

        [Column(TypeName = "datetime2")]
        public DateTime? createdUtc { get; set; }
    }

EF 不关心数据库中的顺序。它总是按名称而不是序号引用列。重要的是 ——顺序需要在整个 EF 模型中保持一致。如果以一种方式指定主键,则引用该主键的外键必须采用相同的顺序。

我编写了代码优先生成器。从数据库中提取信息的组件中有一个 "bug"。就像您发现的那样,它去除了复合键中的列顺序。但是,它确实保留了外键中的哪一列映射到主键中的哪一列。因此,即使它可能无法反映数据库中的布局,生成的模型也能正常工作您的数据库。