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"。就像您发现的那样,它去除了复合键中的列顺序。但是,它确实保留了外键中的哪一列映射到主键中的哪一列。因此,即使它可能无法反映数据库中的布局,生成的模型也能正常工作您的数据库。
给定一个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 不关心数据库中的顺序。它总是按名称而不是序号引用列。重要的是
我编写了代码优先生成器。从数据库中提取信息的组件中有一个 "bug"。就像您发现的那样,它去除了复合键中的列顺序。但是,它确实保留了外键中的哪一列映射到主键中的哪一列。因此,即使它可能无法反映数据库中的布局,生成的模型也能正常工作您的数据库。