在将其保存在实体框架中之前是否可以获取Identity Field值

Rey*_*ldi 6 c# sql-server entity-framework azure-sql-database

我有一个客户和销售表

CUSTOMER
--------------
Id (int auto increment)
Name

SALES
---------------
Id (int auto increment)
CustomerId (int)
OrderTotal (decimal)
Run Code Online (Sandbox Code Playgroud)

有了Guid,我可以做到这一点.

dbTransaction = dbContext.Database.BeginTransaction(isolationLevel);

var customer = new Customer()
{
  Id = Guid.NewGuid(),
  Name = "John Doe"
};

var sales = new Sales() 
{
  Id = Guid.NewGuid(),
  CustomerId = customer.Id,
  OrderTotal = 500
};

dbContext.SaveChanges();
dbTransaction.Commit();
Run Code Online (Sandbox Code Playgroud)

如果我的主键是int(使用DatabaseGeneratedOption.Identity),我该怎么做?

sta*_*ica 15

你不能.进入IDENTITY列的ID 由数据库在插入时生成,并且所有用于规避该ID并自行确定ID的"技巧"可能存在缺陷.

简短回答:如果您在保存之前想要生成ID,请使用GUID(UNIQUEIDENTIFIER)或a SEQUENCE(如果您正在使用SQL Server 2012或更高版本).

为什么你不应该自己计算下一个免费ID:

甚至不要考虑运行诸如context.Customers.Max(c => c.Id) + 1可行解决方案之类的查询,因为总有可能存在并发数据库访问:在您阅读下一个"免费"之后,另一个进程或线程可能会将新实体持久保存到同一个表中ID,但在存储实体之前.计算下一个空闲ID将容易发生冲突,除非您获取ID,对其执行某些操作以及存储具有该ID的实体的整个操作都是原子的.这可能需要DB中的表锁,这可能是低效的.

(即使使用SEQUENCESQL,SQL Server 2012中引入的新功能,也存在同样的问题.)(我错了 ;请参阅答案结束.)

可能的解决方案:

  1. 如果需要在保存对象之前确定对象的ID,请不要使用IDENTITY列中的ID .保持GUID,因为你极不可能与这些碰撞.

  2. 没有必要在一个另一个之间做出选择:你实际上可以吃蛋糕并吃掉它!没有什么可以阻止你有两个ID列,一个是你在外部确定的(GUID),另一个是DB内部的(IDENTITY列); 请参阅Mark Seemann 撰写的博客文章"CQS与服务器生成的ID",以更详细地了解这一想法.以下是一般的想法:

    CREATE TABLE Foos
    (
        FooId INT IDENTITY NOT NULL PRIMARY KEY CLUSTERED,
     -- ^^^^^ assigned by the DBMS upon insertion. Mostly for DB-internal use.
        Id UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL UNIQUE DEFAULT (NEWID()),
     -- ^^ can be dictated and seen by the users of your DB. Mostly for DB-external use.
        …
    );
    
    CREATE TABLE FooBars
    (
        FooId INT NOT NULL FOREIGN KEY REFERENCES Foos (FooId),
     --   use DB-internal ID in foreign key constraints ^^^^^
        …
    );
    
    CREATE VIEW PublicFoos AS
    SELECT Id, … FROM Foos;
    --     ^^ publish the public ID for users of your DB
    
    Run Code Online (Sandbox Code Playgroud)

    (确保遵循一些约定,以便始终命名内部和公共ID字段名称.)

  3. SEQUENCEs,SQL Server 2012中引入的功能,是拥有IDENTITY列的可能替代方案.它们会自动增加,并且在使用时获得下一个免费ID时,您可以获得唯一的号码NEXT VALUE FOR SomeSequence.MSDN提到的一个用例是:

    在以下场景中使用序列而不是标识列:[...]应用程序在插入表之前需要一个数字.

    一些警告:

    • 获取下一个序列值将需要额外的数据库往返.

    • 与标识列一样,序列可以重置/重新播种,因此存在ID冲突的理论可能性.如果可以提供帮助,最好不要重新定位标识列和序列.

    • 如果您使用获取下一个空闲序列值NEXT VALUE FOR,但随后决定不使用它,则会导致ID中出现"间隙".使用常规(非顺序)GUID显然不会发生差距,因为它们没有固有的排序.