sql-server - 在 Entity Framework 4.1 Code First 中手动生成主键的最佳方法是什么

标签 sql-server vb.net ef-code-first entity-framework-4.1

在 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/

相关文章:

.net - 为什么 Socket.BeginReceive 会丢失 UDP 数据包?

sql-server - 使用 Entity Framework 代码优先创建存储过程?

c# - 组合键作为外键

sql-server - 使用 Oracle Loader 文件填充 SQL Server 2k8

vb.net - VB.NET 中的递归文件搜索

asp.net - FileUpload1.HasFile 为 False

entity-framework - 为什么我必须手动提供外键属性值(在这种情况下)?

c# - 如何在 ASP.NET MVC/SQL 应用程序中正确编码外来重音字符(请提供 C-R-U 示例)

sql-server - 如何让 SQL Server 事务使用记录级锁?

sql-server - 为什么通过在 SQL 查询中执行所描述的修改可以显着提高性能?