首先使用代码禁用整数主键上的标识(自动递增)

Posted

技术标签:

【中文标题】首先使用代码禁用整数主键上的标识(自动递增)【英文标题】:Disabling identity (auto-incrementing) on integer primary key using code first 【发布时间】:2020-05-29 09:50:07 【问题描述】:

我在 ASP.NET MVC 3 应用程序中使用代码优先方法,模型中的所有整数主键 (public int Id get; set; ) 默认配置为具有自动递增功能的标识。如何禁用此功能并启用手动输入主键整数的方法?

实际情况是Id 整数具有特殊含义,因此我希望它们可以在创建时选择并在以后进行编辑。如果在创建时未给出整数,则它会自动递增,否则将使用指定的值,这将是理想的。但可编辑的主要字段是我的主要需求。有没有办法在 ASP.NET MVC 3 中优雅地做到这一点?

【问题讨论】:

【参考方案1】:

使用这些数据注释选项:

[System.ComponentModel.DataAnnotations.KeyAttribute()] [System.ComponentModel.DataAnnotations.DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.None)]

【讨论】:

NAMESPACE CHANGE: 现在是 2015 年,这个答案的命名空间已经改变:[System.ComponentModel.DataAnnotations.KeyAttribute()] [System.ComponentModel.DataAnnotations.**Schema**.DatabaseGenerated( System.ComponentModel.DataAnnotations.**Schema**.DatabaseGeneratedOption.None)] 另外,“KeyAttribute”的“Attribute”部分是不需要的,没有理由不在顶部放置“Using”:using System.ComponentModel.DataAnnotations;using System.ComponentModel.DataAnnotations.Schema;[Key][DatabaseGenerated(DatabaseGeneratedOption.None)]【参考方案2】:

您可以使用 FluentMapping:

modelBuilder.Entity<*entityname*>().Property(m => m.*fieldname*)
             .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

【讨论】:

【参考方案3】:

如果你想在 EntityFramework Core 2.0 中使用 fluent api,你可以这样写:

modelBuilder.Entity<*myEntity*>()
  .Property(e => e.*myFieldname*)
  .ValueGeneratedNever();

【讨论】:

【参考方案4】:

使用属性:

public class MessageSubject
    
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Id  get; set; 
        public string Title  get; set; 
        public string Comment  get; set; 
        public bool BuildIn  get; set; 
    

【讨论】:

【参考方案5】:

我刚刚安装了最新接受的 EntityFramework.dll 版本 5.0.0,我相信...

但是,我有一半的时间感到困惑,因为有一个运行时版本 v4.0.30319 和 4.4.0.0 版本,但是,我从网站上确定我 在我的搜索中被提及(它告诉我通过“包 管理器控制台”,您可以通过 VS 中的菜单访问该控制台 “工具|库包管理器|包管理器控制台”并键入 “PM>”提示“Install-Package EntityFramework [可选:版本 数字或 -Pre 表示最新的预发布(测试版)]") 5.0.0.

... ,并且您可以使用一个属性“System.CompnentModel.DataAnnotations.DatabaseGenerated(Computed, Identity or None) (previous version) or [...].Schema.DatabaseGenerated (latest version)” .所以要么使用这个属性,要么使用上面提到的流畅的映射思想(William Haack(由 Remo Gloor 编辑)),如果不是先编码(即改变生产),那么如上所述(由 Adam Tuliper)你将需要编写和执行一次性脚本以关闭身份插入。最重要的是,如果您不提供 ID,您可以通过在代码中(并记住多用户环境中的并发问题)或触发器中检索表上的 MAX(ID) + 1 来模拟身份插入。或者,如果您想堵住漏洞,正如表达式所示,您也可以在触发器中执行此操作,方法是拦截插入的行并检查是否设置了 ID 列,如果是,则继续插入,否则 先设置值。堵住漏洞的一种方法是使用那个技巧(我在一些我不记得的网站上看到过,所以我在这里猜了一点)你可以有效地从一些大表中进行反内连接,只需一个将单列行号添加到您的表中,以查找第一个可用的未使用标识号(即查找不是目标表成员的第一个行号)。

在 SQL Server 2005 及更高版本中:

CREATE TRIGGER updInsYourTable_PlugHolesOnIDIfNull 
ON YourTable
FOR update, insert AS
BEGIN
    DECLARE @ID INT
    SELECT @ID = ID FROM INSERTED
    IF @ID IS NULL
    BEGIN
        ;WITH CTE_StagedNumbers AS
        (
            SELECT ROW_NUMBER() OVER (ORDER BY o.object_id) AS NextFreeIdentity
            FROM (  SELECT object_id FROM sys.objects
                 -- UNION ALL 
                 -- SELECT object_id FROM sys.objects
                 /* NB: Here if sys.objects is not larger enough say on a small schema 
                    configured database then use a different table otherwise you can 
                    always union all on the same table as many times as you want to 
                    double, triple etc. its size. */
                 )  o
        )
        UPDATE YourTable
        SET ID = (
                    SELECT TOP 1 NextFreeIdentity
                    FROM CTE_StagedNumbers
                    WHERE NextFreeIdentity NOT IN (SELECT ID FROM YourTable)
                 )
        WHERE ID IS NULL
    END
END
GO

注意,CTE_StagedNumbers 不是必需的,它只是为了强调,主要技巧不一定是设置行号,但是,如果您要设置一个永久临时表(比如 StagedNumbers),只需没有自动标识的单个整数列(例如 NextFreeIdentity INT NOT NULL PRIMARY KEY),(注意:您的 ID 列的 YourTable 定义必须接受空值,因为我们使用的是后触发器)预先填充了相应的正整数值开始在 1 使用上述技术,然后完全删除 CTE,并将最终选择中的 CTE_StagedNumbers 替换为 StagedNumbers,这样效率最高。

【讨论】:

是的,触发器可能是一种方法,但直接访问和操作 SQL 数据库并不符合 MVC 框架的精神。【参考方案6】:

如果这是数据库中的身份字段,则不能。好吧,您可以通过在数据库端设置身份插入关闭,但通过良好的做法,除非批量插入记录,否则您不应该这样做。如果您不希望将其作为身份,则在该列的数据库中将身份选项设置为 false。

【讨论】:

我使用的是代码优先的方法,这意味着数据库是从代码中生成的。我只提到了可以在数据库中看到的内容,因为我认为这是问题的根源。那么是否有用于禁用自动身份字段选项的数据注释? 那么在创建时使用代码优先方法将“SET IDENTITY_INSERT YourTableName OFF”(或某事)传递给数据库的最简单的方法是什么? 啊.. 代码优先。你在使用 [Key()] 吗?如果是这样,我认为您有些卡住了。我相信它适用于更简单的场景。在您的情况下,您不希望生成自动编号,但您仍然需要我假设的索引。不幸的是,Key() 字段没有给你这个控制,所以我会删除 Key 字段。如果您在任何地方都命名 Id 并且不使用 key() 属性,我会认真考虑将其更改为名为 Id 的数据库字段没有任何好处,并且当您跨表链接(fk 关系)时,说 Customer.Id = OtherTable 很奇怪.CustomerId 所以我会保持一致。 顺便说一下这里的一些 cmets:***.com/questions/5813190/… 我发现在数据库上关闭身份并不像更改表那么简单......但是这里有一个可行的简单解决方案:***.com/questions/7227812/…【参考方案7】:

从外键表中删除Required属性对我来说是这样做的。

【讨论】:

以上是关于首先使用代码禁用整数主键上的标识(自动递增)的主要内容,如果未能解决你的问题,请参考以下文章

在不修改实体类(注释)或数据上下文(使用 fluentapi)的情况下禁用标识(自动递增)

防止自动递增整数主键?

如果我们使用自动递增的标识列和PK,则违反3NF

如果我们使用自动递增的标识列和 PK,则违反 3NF

无法使用 flask-sqlalchemy 创建自动递增主键

自动递增主键值始终为 0