【问题标题】:How to auto-increment Key named other than Id如何自动增加 ID 以外的键名
【发布时间】:2014-08-26 19:38:57
【问题描述】:

我使用的是 EF Code First,并且有一个没有名为 Id 的键的表。对于此表,将密钥命名为 SelectorId 是有意义的。问题是当我添加新行时出现错误:

无法将值 NULL 插入到表的“SelectorId”列中 'dbProgramData.dbo.Selectors';列不允许空值。 插入失败。该语句已终止。

当然它不允许空值 - 它是该表的主键,但我假设该键是自动递增的。我有这样定义的模型:

public class Selector
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int SelectorId { get; set; }

    public string ProgramId { get; set; }

    ....

}

我正在尝试使用此代码添加一行(在我的本地 PC 上使用 VS 可以正常工作,但在发布后在远程 VPS 上失败并出现上述错误:

ProgramDbContext dbProgramData = new ProgramDbContext();

Selector selector = new Selector();

selector.ProgramId = programId;

dbProgramData.Selectors.Add(selector)
dbProgramData.SaveChanges();

在我的 ProgramDbContext 类中:

public DbSet<Selector> Selectors { get; set; }

如果我将SelectorId 重命名为Id,我怀疑这个问题会解决吗?运行 Add Migration 后,up/down 脚本显示:

CreateTable(
    "dbo.Selectors",
    c => new
        {
            SelectorId = c.Int(nullable: false, identity: true),
            ProgramId = c.String(),

    ....

谁能告诉我我做错了什么?我创建的两个表也有同样的问题,它们都有一个名为 Id 以外的 Pk。

更新

按照建议,我进入远程服务器上此表的 SSMS 和设计视图。我看到Is Identity 设置为否。我仔细检查了我的本地数据库表,Is Identity 设置为是,因为它应该是。

我将数据库表发送到远程 VPS 服务器的方法是使用 SSMS,方法是右键单击数据库,然后在任务 -> 导入数据上。我指向我的本地 .\SQLEXPRESS 并执行导入。似乎导入成功,但显然没有。在执行导入之前,我将删除远程数据库中的所有表。

这种方法不应该在导入数据的同时创建确切的架构吗?

【问题讨论】:

  • 我不太了解 EF,但是如果它是自动增量的,为什么要设置 SelectorId?如果你不设置它,EF不会自动将它设置为下一个值(这就是Identity应该做的,对吧)?
  • 我没有设置它。我认为它应该完全按照您所说的那样工作 - EF/SQL 将自动递增,但是当我尝试添加一行时,我收到一个错误,即 SelectorId 不能为空。 :S
  • 但是,例如,您要添加哪一行?
  • @NickyvV,我的问题的第三个代码块中有一个示例。

标签: sql-server entity-framework


【解决方案1】:

说清楚:

  • EF 使用独立于物理数据库(实体数据模型,又名 EDM)的架构定义
  • 数据库有自己的架构,独立于 EDM

要使事情正常进行,两个架构必须保持一致。因此,如果数据库中有标识列,则必须在 EDM 中正确定义该列。如您所说,EDM 列的 DatabaseGenerated 属性必须等于 DatabaseGeneratedOption.Identity。

在你的开发机器上我不知道你是使用Database First,还是Code First,还是绘制EDM schema来生成数据库,但是很明显schema和数据库是一致的,所以可以正常工作.

如果您尝试使用与数据库无关的 EDM 架构,您将收到错误。

这是标识列的工作方式:

  • 在 SQL Server 中,当您将标识属性添加到表的列时,当您插入新行时,您无法指定该列的值:SQL Servers 会自动生成一个序号并插入该行。
  • 如果 EDM 有一个标记为 DatabaseGenerated 的列,当您使用它插入新行时,生成的 SQL:
    • 插入语句中不包含列
    • 插入后立即选择scope_identity()函数获取服务器生成的值。

所以如果EDM和Database的信息不一致,插入失败是正常的。

在这种情况下,问题显然是部署问题。您可以尝试其他方法:

  • 在您的开发机器中备份数据库,并在 VPS SQL Server 中恢复它
  • 使用 SSMS 查找数据库的文件(查看数据库属性。一旦您知道文件的位置,分离数据库(在 SSMS 中,右键单击数据库,您将在任务菜单选项下找到此选项)。在 VPS 中分离数据库(如果存在)。现在您可以将文件从开发机器复制到服务器,然后在开发机器和服务器中再次附加它。

这两种方法是完全安全的。如果您使用导入/导出或任何其他操作来复制数据库,则可能会遇到麻烦,因为需要配置一些选项。

顺便说一句,如果您使用的是 EF Code First,除非您使用属性对其进行修改,否则默认情况下,任何整数类型的 IdTableNameId 都将被视为 DatabaseGenerated 列。 (这是约定俗成的配置)。但是,如果您不遵守约定,您始终可以使用属性(或 Fluent API)来配置列,将其标记为 Key 并指定它的生成位置。

【讨论】:

  • 是的,我遇到了数据库部署问题。 Web 部署可以很好地部署应用程序,并且将创建数据库架构,但数据库没有填充我的数据。所以我恢复使用 SSMS 执行导入。我希望我可以使用这种所谓的“一键发布”,但是......所以自从我使用 SSMS 执行导入以来,我一直在先删除 VPS 上的表。奇怪的是,我看到导入期间有一个设置允许重新创建身份,但这也没有这样做。我可能只需要 xfr .bak 文件。 :(
  • 分离/附加要快得多。检查您是否有权执行此操作。我不知道你的项目的结构,所以我不知道我不能进一步帮助你。
【解决方案2】:

在 sql server 中,转到相关表的设计视图。单击要增加的列,然后在列属性面板中,向下滚动到标识规范。将其设置为yes,然后您应该能够将其设置为自动递增1。

【讨论】:

  • 我会这样做,但这不是 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 告诉 EF 的吗?
  • @rwkii 老实说不知道,我从来没有从 sql server 界面之外做过。此外,您可能还必须告诉它自动递增。
  • 我看你是对的。在 SQL 服务器(设计视图)上,身份是否设置为否。我想我不明白为什么。我的向上/向下脚本说identity: true。在初始开发阶段手动配置这些表对我们来说是一个可怕的选择。将SelectorId 重命名为Id 是否可以解决此问题? (我也不喜欢那个选项......)。我的模型中必须有一个注释。
  • @rwkii 至于将名称更改为“Id”,我不知道,但我真的很想认为这没有必要。我还假设您缺少某种注释。
  • @rwkii 嗯,老实说,我不确定,但我认为您不需要更改列的名称。这里有一个类似问题的链接,也许会有所帮助? stackoverflow.com/questions/10976251/…
猜你喜欢
  • 2011-04-26
  • 2010-12-09
  • 2014-02-10
  • 2015-03-19
  • 2019-05-04
  • 2012-01-20
  • 1970-01-01
  • 2021-10-20
  • 2015-01-15
相关资源
最近更新 更多