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

前端之家收集整理的这篇文章主要介绍了sql-server – 在Entity Framework 4.1 Code First中手动生成主键的最佳方法是什么前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
在Entity Framework 4.1 Code First中手动生成主键的最佳方法是什么?

我正在编写ASP.NET MVC 3并使用存储库模式.

我目前使用以下代码按顺序生成密钥:

'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并且插入将失败.

这是一个好方法,还是有更好的方法

请注意,我不能(也不会)使用数据库生成的身份字段!

解决方法

您的担忧是有效的 – 在经常使用的网站中,这种情况最有可能发生,解决方案并不容易.您可以使用@Mikecito所描述的客户端Guid,但它有很大的性能损失,我想您不想使用它.

你现在这样做的方式非常糟糕,因为唯一的解决方案是将你的代码包装在单个可序列化的事务中 – 事务必须同时包含选择Id和保存记录.这将使您的InventoryObjects顺序访问,因为每个select max将锁定整个表,直到提交事务 – 在插入事务期间,没有其他人能够读取或写入数据到表.它不一定是很少访问的网站的问题,但它可以在经常访问的网站中没有GO.在当前设置中无法以不同方式执行此操作.

部分改进是使用单独的表来保存最大值存储过程以获取下一个值并在原子操作中增加存储的值 – (它实际上模拟来自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();
}

猜你在找的MsSQL相关文章