我有一个客户和销售表
CUSTOMER
--------------
Id (int auto increment)
Name
SALES
---------------
Id (int auto increment)
CustomerId (int)
OrderTotal (decimal)
使用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();
如果我的主键是int(使用DatabaseGeneratedOption.Identity),我该怎么做?
答案 0 :(得分:14)
你做不到。进入IDENTITY
列的ID由数据库在插入时生成,并且所有"技巧"为了规避这一点并自己确定身份证可能存在缺陷。
简短回答:如果您想在保存前生成ID,请使用GUID(UNIQUEIDENTIFIER
)或SEQUENCE
(如果您有'重新使用SQL Server 2012或更高版本。
不要考虑运行context.Customers.Max(c => c.Id) + 1
之类的查询作为可行的解决方案,因为您可能始终有并发数据库访问:另一个进程或线程可能会持久保存新实体在您阅读了下一个" free"之后到同一张桌子。 ID,但在存储实体之前。计算下一个空闲ID将容易发生冲突,除非您获取ID,对其执行某些操作以及存储具有该ID的实体的整个操作都是原子的。这可能需要DB中的表锁,这可能效率低下。
(即使您使用SQL Server 2012中引入的新功能(I was wrong;请参阅答案结束。)< / p>
SEQUENCE
,也存在同样的问题。)
如果您需要在保存对象之前确定对象的ID,请不要使用IDENTITY
列中的ID。 坚持使用GUID ,因为您极不可能与这些人发生任何冲突。
没有必要在另一个或之间做出选择:你实际上可以吃蛋糕并吃掉它!没有什么可以阻止您拥有两个ID列,一个是您在外部确定的(GUID),另一个是在DB内部(IDENTITY
列);请参阅Mark Seemann的博客文章"CQS vs. server generated IDs",了解更详细的信息。以下是一般概念:
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
(确保遵守一些约定,以便始终命名内部和公共ID字段名称。)
SEQUENCE
s 是SQL Server 2012中引入的一项功能,可能是IDENTITY
列的替代方法。它们会自动增加,并且在使用NEXT VALUE FOR SomeSequence
获取下一个免费ID时,您可以获得唯一的号码。其中一个用例mentioned on MSDN是:
在以下场景中使用序列而不是标识列:[...]应用程序在插入表格之前需要一个数字。
一些警告:
获取下一个序列值将需要额外往返数据库。
与标识列一样,序列可以重置/重新播种,因此存在ID冲突的理论可能性。如果您可以提供帮助,最好永远重新定位标识列和序列。
如果您使用NEXT VALUE FOR
获取下一个免费序列值,但随后决定不使用它,则会产生&#34;间隙&#34;在你的ID中。使用常规(非顺序)GUID显然不会发生差距,因为它们没有固有的排序。
答案 1 :(得分:1)
据我所知,在保存数据库中的更改之前无法获取ID。在将值插入数据库后,数据库将创建ID。
要在调用.SaveChanges()
时添加它,只有它会将更改写入数据库,然后才会生成标识值。
答案 2 :(得分:1)
你可以通过小黑客获得这个价值。
在SQL Server中创建一个类似这样的函数
CREATE FUNCTION fn_getIdentity(@tbl_name varchar(30))
AS
BEGIN
IF @tbl_name = 'Employee_tbl'
RETURN IDENT_CURRENT('Employee_tbl')
ELSE IF @tbl_name = 'Department_tbl'
RETURN IDENT_CURRENT('Department_tbl')
ELSE
RETURN NULL
END
在您的Entity框架中创建一个实体以支持此功能,并在任何您想要的地方使用它。
然后使用
var nextValue = dbContext.fn_getIdentity("Employee_tbl")
IDENT_CURRENT返回标识列的最后一个递增值。这并不意味着MAX + 1,就好像您之前的事务为该列生成了一个标识值,但是回滚后,您将看到将生成的下一个值。
请注意,我没有正确检查语法,这个语法只是提出一个想法。
但是,如果使用SQL Server 2012或更高版本,我会使用Stakx提供的解决方案,即SEQUENCE 否则创建一个表来实现SEQUENCE的功能,方法是在表中永久生成ID后保留ID。