如何使Entity Framework生成的INSERT语句包含数据库生成的IDENTITY列?

时间:2016-12-09 18:46:02

标签: c# sql-server entity-framework

这是Entity Framework 6.1.3

SQL Server表有一个双列复合键。

ID      INT Identity(1,1) NOT NULL
VERSION INT               NOT NULL

插入新记录是有效的,因为我没有在我的对象上设置ID;我只设置了VERSION。 因此,新记录将如下所示:

ID    VERSION
1     1

完美!数据库生成ID,因为列配置了Identity,我的模型使用[DatabaseGenerated(DatabaseGeneratedOption.Identity)]进行修饰。

但是现在我需要插入另一行具有相同ID但不同的VERSION;因此复合键。所以我希望第二行是:

ID   Version   
1    1
1    2       <- second row has same ID and different version

我确实需要这两种方式,因为有一种情况是数据库应该自动生成一个新的ID,而另一种情况是我有相同的ID但是VERSION不同。

问题: 因为我的Code-First模型具有使用DatabaseGeneratedOption.Identity配置的ID,所以当我在我的对象上设置ID属性时,我的SaveChanges生成插入语句而没有 ID!

(VS中的诊断工具显示实体框架生成了此语句)

ADO.NET: Execute Reader "INSERT [dbo].[T1]([Version], ... VALUES (@0, ...)

请注意遗漏身份证明。因为我在我的对象上显式设置了ID,所以我希望看到这个语句。

INSERT [dbo].[T1]([ID], [Version], ... VALUES (@0, @1, ...)

这就是我想要完成的事情。

问题是: 如何以优雅的方式使Entity Framework 在其生成的insert语句中包含该ID列

我不想使用存储过程或硬编码SQL语句或通过“挤压”来修改插入语句。专栏。

如果没有办法,我知道我必须完全删除身份的使用,并定义我自己的ID,我试图避免使用。

另外,我的SaveChanges()已经使用了SET IDENTITY_INSERT ON / OFF,因此不存在任何问题。

以下是我模型的相关部分:(我省略了其他属性)

[Key, Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }

[Key, Column(Order = 1)]
public int VERSION { get; set; }

我探索过的一个途径是在OnModelCreating中重置我的DbContext,但这并没有什么不同。 当然,在该修订版中,我确实从类中的ID属性中删除了DatabaseGenerated装饰器。我将其插入OnModelCreating:

if (this.AllowIdentityInsert)
{
    modelBuilder.Entity<T1>().Property(x => x.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
else
{
    modelBuilder.Entity<T1>().Property(x => x.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}

如果我可以通过在SaveChanges之前将ID属性更改为None来成功控制模型,那么这可以工作并且是一个优雅的解决方案。

有没有人遇到这种情况并找到了一个好的解决方案? 非常感谢您的意见或建议。

1 个答案:

答案 0 :(得分:1)

通常,您不希望以这种方式使用标识列,但我想如果您使用复合键,则可以。插入第二条记录时您将遇到的问题是您必须打开和关闭IDENTITY_INSERT。因此,在这里考虑它的SQL就是一个例子,向您展示完成任务需要做些什么。

IF OBJECT_ID('tempdb..#TblName') IS NOT NULL
    BEGIN
        DROP TABLE #TblName
    END

CREATE TABLE #TblName (
    ID INT IDENTITY(1,1) NOT NULL, Version INT NOT NULL
)

INSERT INTO #TblName (Version) VALUES (1)

SET IDENTITY_INSERT #TblName ON

INSERT INTO #TblName (ID, Version) VALUES (1,2)

SET IDENTITY_INSERT #TblName OFF

SELECT *
FROM
    #TblName

更典型的设计是通过触发器实际维护日志表并将历史记录存储在其中。因为在该表中,不需要标识列只是另一个INT。

还有一些桌面设计可以解决这个限制,但您可能还想研究创建SQL SEQUENCE https://msdn.microsoft.com/en-us/library/ff878058.aspx而不是使用IDENTITY ID列会在您需要时检索SEQUENCE并始终插入值。如果您使用SEQUENCE,则可以获得额外的好处,即能够添加另一个IDENTITY列,该列将是本地表ID,通常建议使用该列,而不是仅仅依赖于复合键。

好的,这是(对我而言)一种非常有趣的方式来解决您的身份问题并维持“增加版本”。您可以使用Update able View而不是直接使用表格。您可以使用2 SEQUENCES一个用于ID,一个用于VersionId,然后获取版本,您将在ROW_NUMBER()中使用view。您可以通过添加INSTEAD OF INSERT/UPDATE trigger来扩展此解决方案,以更自动地处理IDS的设置,但我通常不喜欢触发器。无论如何,这是一个有趣的解决方案:

CREATE TABLE dbo.yourTable (
    TableId INT NOT NULL IDENTITY(1,1)
    ,Id INT NOT NULL
    ,VersionId INT NOT NULL
    ,Col VARCHAR(100) NOT NULL
    ,PRIMARY KEY (Id, VersionId)
)

GO

CREATE SEQUENCE dbo.SEQ_yourTableIdBy1
    START WITH 1
    INCREMENT BY 1;
GO

CREATE SEQUENCE dbo.SEQ_yourTableVersionIdBy1
    START WITH 1
    INCREMENT BY 1;
GO

CREATE VIEW dbo.yourTable_v
AS
    SELECT
       Id
       ,VersionId
       ,Version = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY VersionId)
       ,Col
       ,LatestVersion = CASE
                  WHEN ROW_NUMBER() OVER (PARTITION BY Id ORDER BY VersionId DESC) = 1
                  THEN 1 ELSE 0 END
    FROM
       dbo.yourTable
GO

--New Record
INSERT INTO dbo.yourTable_v(Id, VersionId, Col)
VALUES (NEXT VALUE FOR dbo.SEQ_yourTableIdBy1, NEXT VALUE FOR dbo.SEQ_yourTableVersionIdBy1, 'A')

SELECT * FROM dbo.yourTable_v

--Change To Existing Record
INSERT INTO dbo.yourTable_v(Id, VersionId, Col)
VALUES (1, NEXT VALUE FOR dbo.SEQ_yourTableVersionIdBy1, 'B')

SELECT * FROM dbo.yourTable_v

链接显示其工作原理http://rextester.com/GBHG23338

要使实体框架相信视图是一个表,您可能需要更改密钥定义和实体类型这里是关于该主题的msdn博客。 https://blogs.msdn.microsoft.com/alexj/2009/09/01/tip-34-how-to-work-with-updatable-views/

优点:

  • 如果2个人试图同时提交等,这不会破坏。
    • 实体框架会认为这是一个普通的表,一旦你通过链接稍微伪造它。
    • 虽然它会在所有记录中增加VersionId,但它仍然会在您的应用程序中提供一个很好的版本+ 1。
    • 您可以轻松添加最新版本列,以便在您的应用程序/查询中使用。
相关问题