在 Entity Framework 4.1 Code First 中手动生成主键的最佳方法是什么?
我正在编写 ASP.NET MVC 3 并使用存储库模式。
我目前使用以下代码按顺序生成 key :
'Code First Class
Public Class Foo
<Key()>
<DatabaseGenerated(DatabaseGeneratedOption.None)>
Public Property iId As Integer
Public Property sBar As String
End Class
'Context Class
Public Class FooBarContext : Inherits DbContext
Public Property Foos As DbSet(Of Foo)
End Class
'Get the current Id
'Part of code in repository that stores Entity Foo.
Dim iCurrId as Integer = (From io In context.Foo
Select io.iId()).Max
Dim iNewId as Integer = iCurrId + 1
Foo.iId = iNewId
我的看法是(但不太可能)两个(或更多)用户将尝试同时保存实体 Foo,因此将获得相同的 ID,插入将失败。
这是一个好方法,还是有更好的方法?
请注意,我不能(也不会)使用数据库生成的身份字段!
最佳答案
您的担忧是有道理的 - 在经常使用的网站中,这很可能发生,解决方案并不容易。您可以使用客户端 Guid
如@Mikecito 所述,但它对性能有显着影响,我猜您不想使用它。
您目前这样做的方式非常糟糕,因为唯一的解决方案是将您的代码包装在单个可序列化事务中 - 事务必须同时包含选择 Id 和保存记录。这将访问您的 InventoryObjects
顺序,因为每个 select max 将锁定整个表,直到事务提交 - 在插入事务期间没有其他人能够读取或写入数据到表中。在很少访问的站点中它不一定是一个问题,但在经常访问的站点中它可能不是问题。在您当前的设置中没有办法做到这一点。
部分改进是使用单独的表来保存最大值 + 存储过程以获取下一个值并在原子操作中递增存储值 - (它实际上模拟了来自 Oracle 的序列)。现在唯一的复杂问题是您是否需要没有间隙的序列。例如,如果保存新 InventoryObject
出现问题选定的 ID 将丢失,并且会在 ID 的序列中产生间隙。如果您需要没有间隙的序列,您必须再次使用事务来获取下一个 Id 并保存记录,但这次您将只锁定序列表中的单个记录。从序列表中检索 Id 应尽可能接近保存更改,以最大限度地减少序列记录被锁定的时间。
以下是 SQL Server 的序列表和序列过程示例:
CREATE TABLE [dbo].[Sequences]
(
[SequenceType] VARCHAR(20) NOT NULL, /* Support for multiple sequences */
[Value] INT NOT NULL
)
CREATE PROCEDURE [dbo].[GetNextSequenceValue]
@SequenceType VARCHAR(20)
AS
BEGIN
DECLARE @Result INT
UPDATE [dbo].[Sequences] WITH (ROWLOCK, UPDLOCK)
SET @Result = Value = Value + 1
WHERE SequenceType = @SequenceType
RETURN @Result
END
该表不需要首先通过代码映射 - 您永远不会直接访问它。您必须创建自定义数据库初始值设定项,以便在 EF 创建数据库时为您添加表和存储过程。您可以尝试与 described here 类似的方法.您还必须为具有起始值的序列添加初始化记录。
现在您只需要在保存记录之前调用存储过程来获取一个值:
// Prepare and insert record here
// Transaction is needed only if you don't want gaps
// This whole can be actually moved to overriden SaveChanges in your context
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
record.Id = context.Database.ExecuteStoreCommand("dbo.GetNextSequenceValue @SequenceType",
new SqlParameter("SequenceType", "InventoryObjects"));
context.SaveChanges();
}
关于sql-server - 在 Entity Framework 4.1 Code First 中手动生成主键的最佳方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5924258/