【问题标题】:Entity Framework composite key definition with column ordinal <> primary key ordinal实体框架复合键定义与列序号<>主键序号
【发布时间】:2015-09-14 11:45:23
【问题描述】:

给定一个表格

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。

主键的实体框架代码优先配置是否应为主键序数

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

或者应该在列序数中:

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

使用 MSI 安装中的 EF Tools v6.1.3,使用 Visual Studio -> 添加 -> 新项目 -> ADO.Net 实体数据模型。选择 Code-first,生成如下:

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

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

【问题讨论】:

  • 您能否从我在您的扩展程序上发布的帖子中编辑/添加架构和 EF 的第一代代码到您的帖子中,以显示 VS 提供的内容?
  • 您应该在回答中表明这一点。我已将上述问题尽可能简明扼要。
  • 我编辑了我的回复并添加了架构和 EF 代码首先生成的代码。
  • 据我所知,EF 6 似乎没有查询有关主键序数的信息entityframework.codeplex.com/SourceControl/latest#src/…
  • 我在这里添加了一个错误:entityframework.codeplex.com/workitem/2819

标签: entity-framework ef-code-first primary-key code-first


【解决方案1】:

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

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

【讨论】:

  • 这也是我认为应该的。但是,Microsoft 生成器按列顺序(C1、C3)将其吐出,我认为这是一个错误。
  • 这很奇怪,你能显示你的ordinal_test 实体吗?您是使用 Fluent Api 配置所有内容,还是还使用一些数据注释?
  • 我说的是尼尔:visualstudiogallery.msdn.microsoft.com/…(附注:我是实体框架反向 poco 生成器的作者)。我的生成器以主键序号将其吐出,我仍然认为这是正确的方式。
【解决方案2】:

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

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

【讨论】:

  • 允许 EF 执行“删除和创建”(例如用于单元测试)它将按列序号创建主键,这不是 SQL 最初创建的方式。
  • @SimonHughes 您还会丢失视图、索引、触发器、函数、存储过程、同义词等。同样,如果您仅使用 EF 访问它,键列顺序将成为“实现细节”实际上并不重要。
  • 对于那些来自 EDMX 的人,它需要与 VS 代码优先生成器(列顺序)对齐,我被告知将其用于 EDMX 中的键排序作为列顺序。如果人们使用西蒙的发电机并一直使用它,我相信它应该/会工作,从上面所说的。但我认为将两者对齐很重要,就好像我没有存储过程一样,我会改用 VS 系统,所以我会在两个生成器之间产生冲突。
  • (很高兴知道我猜对了你要问的人(Brice)。;)
【解决方案3】:

如果您在 Visual Studio 2015 中从数据库生成代码优先模型,它将按照架构中列的顺序为键分配 Order 属性。因此,如果您执行诸如 Find 之类的 LINQ 查询,则需要按“列顺序”对主键进行排序,因为键是按架构列顺序排序的。

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

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

OP 的示例(由于我在 VS 扩展中的查询而生成)有点不同,因为架构正在生成主键排序。因此,让我们退后一步,从“嘿,我将从您的架构生成代码优先模型。我看到 C1 和 C3 是主键。当我从您的架构生成代码优先模型时,我将按顺序迭代列并为主键列分配一个 Order 属性。”这导致直接基于列顺序的使用模式。

同样,OP 正在从模式生成模型,EF 生成每个模式/列顺序的代码顺序。

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 删除并创建数据库,您将需要与最初在 SQL 中定义的主键序号相同。因此正确使用: HasKey(x => new { x.C3, x.C1 });
  • @SimonHughes 我完全理解你的意思,奇怪的是,如果主键在模式中以这种方式排序,为什么我们不以相同的顺序使用 Find 但 EF 生成和 EDMX用法是计数器,正如您所建议的那样,它可能是 EF 中的一个错误。来自使用您的扩展程序的 EDMX 导致我必须更改所有代码以反转密钥,因此我需要深入了解并感谢您的帮助。我明白你的意思,只是不知道为什么 EF 不尊重它。显然,人们首先在不同的系统中生成代码是一个巨大的冲突问题。
猜你喜欢
  • 2012-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多