【问题标题】:Entity Framework DB First: Timestamp column not workingEntity Framework DB First:时间戳列不起作用
【发布时间】:2013-08-22 15:05:59
【问题描述】:

使用 db first 方法,我希望我的应用程序在尝试更新(过期)实体时抛出并发异常,该实体在数据库中对应的行已被另一个应用程序/用户/会话更新。

我在 .Net 4.5 上使用实体框架 5。对应的表有一个 Timestamp 列来维护行版本。

【问题讨论】:

    标签: concurrency timestamp entity-framework-5


    【解决方案1】:

    我过去通过在您希望执行并发检查的表中添加时间戳字段来完成此操作。 (在我的示例中,我添加了一个名为 ConcurrencyCheck 的列)

    这里有两种并发模式,根据你的需要:

    1 并发模式:固定:

    然后在您的模型中重新添加/刷新您的表格。对于固定并发,确保在将表导入模型时将并发模式设置为固定:像这样:

    然后捕获这个:

        try 
    
        { 
    
        context.SaveChanges(); 
    
        } 
    
        catch (OptimisticConcurrencyException ex) { 
    
    
    ////handle your exception here...
    

    2。并发模式:无

    如果您希望处理自己的并发检查,即提出验证通知用户,甚至不允许保存发生,那么您可以设置并发模式无。

    1.确保将刚刚添加的新列的属性中的ConcurrencyMode更改为“无”。 2. 要在您的代码中使用它,我将创建一个变量来将您当前的时间戳存储在您要检查保存的屏幕上。

    private byte[] CurrentRecordTimestamp 
            { 
                get 
                { 
                    return (byte[])Session["currentRecordTimestamp"]; 
                } 
    
                set 
                { 
                    Session["currentRecordTimestamp"] = value; 
    
                } 
            }
    

    1.在页面加载时(假设您使用的是 asp.net 而不是上面未提及的 mvc/razor),或者当您使用希望编辑的数据填充屏幕时,我会在下面拉出当前记录将 ConcurrencyCheck 值编辑到您创建的此变量中。

     this.CurrentRecordTimestamp = currentAccount.ConcurrencyCheck;
    

    然后,如果用户让记录保持打开状态,同时其他人更改了它,然后他们也尝试保存,您可以将之前保存的这个时间戳值与现在的并发值进行比较。

    if (Convert.ToBase64String(accountDetails.ConcurrencyCheck) != Convert.ToBase64String(this.CurrentRecordTimestamp)) 
    { 
    } 
    

    【讨论】:

    • 这个解决方法是我在自己的回答中已经指出的。它的缺点是,每当重新创建 .edmx 文件时,修改都会默默地丢失。
    • 您遇到了什么错误?在我的解决方案中,“无”适用于上述场景。如果您按照上面的示例进行操作,它可以正常工作。我已经在实时系统中实施了 12 个月,没有任何问题。
    • 我澄清一下:第二种解决方案仅在考虑到某个应用程序空间中的并发更改时才有效。例如,如果您在 Management Studio 中运行查询来修改行,则解决方案将失败。
    • 此外,在您的解决方案中,Timestamp 的值应该存储在比 Session 范围更广的地方。如果在另一台机器上进行并发修改呢?
    • @Alireza 你不能只创建一个MetadataType 以便在修改 EDMX 时属性不会丢失...吗?
    【解决方案2】:

    在查看了这里和网络上许多解释 Entity Framework 5 中的并发和时间戳的帖子后,我得出的结论是,当模型从现有数据库生成时,基本上不可能出现并发异常。

    一种解决方法是修改 .edmx 文件中生成的实体,并将实体的时间戳属性的“并发模式”设置为“固定”。不幸的是,如果模型反复从数据库中重新生成,这种修改可能会丢失。

    但是,有一个棘手的解决方法:

    1. 使用可重复读取或更高的隔离级别初始化事务范围

    2. 获取行的时间戳

    3. 比较新时间戳和旧时间戳

    4. 不等于 --> 异常

    5. 相等 --> 提交事务

    隔离级别对于防止并发修改推断很重要。

    PS: Erikset 的解决方案似乎很好地克服了重新生成模型文件。

    【讨论】:

    • 我对这个问题的回答:stackoverflow.com/questions/11980516/… 提供了一个控制台应用程序,它可以自动执行在 edmx 文件中查找时间戳属性和设置并发模式的过程。您可以将其作为预构建步骤运行以自动化该过程。
    • 不幸的是,我认为你是对的。没有办法自动进行,尽管有一个工作项计划用于未来的版本。 entityframework.codeplex.com/workitem/588
    • 我和@Erikest 做了同样的事情。创建了一个控制台应用程序,它读取 XML 以在所有表​​上设置 ConcurrencyMode 并将我的概念模型的主键重命名为“ID”,以便我的 POCO 模型可以共享一个公共接口。我将控制台应用程序添加到解决方案中,并使用 Visual Studio 的 Task Runner 使控制台应用程序更易于使用,但将其转换为 VS 扩展会很酷。
    • 感谢您的建议。我们已经有一个日期修改日期时间列。因此,我在该专栏中应用了此答案中建议的 5 个步骤。虽然 datetime 列方法不是没有错误和干净的,但我们不想修补 edmx。遗憾的是,即使在 4 年后,也没有简单的方法解决这个问题。
    【解决方案3】:

    如果没有行受到影响,EF 会检测到并发冲突。然后,如果您使用存储过程来删除和更新,您可以在 where 子句中手动添加时间戳值:

    UPDATE | DELETE ... WHERE PKfield = PkValue and Rowversionfield = rowVersionValue
    

    那么如果该行已被其他人删除或修改,Sql 语句会影响 0 行,EF 将其解释为并发冲突。

    【讨论】:

    • 好吧,虽然你是对的,但首先,使用存储过程执行原生 Sql 与 EF 不(直接)相关。其次,我所说的是通过调用 context.SaveChanges() 来更新实体
    • 您可以将存储过程附加到您的实体,然后当您调用 savechanges 时,EF 将为您调用正确的存储过程。如果 sp 没有更新任何行,您将得到并发异常
    • 能否请您发布一行代码,说明如何将存储过程附加到实体以进行更新?
    • 在 EF 设计器中: 1:创建存储过程 2:从数据库更新模型并包含存储过程 3:单击实体并转到“映射”右上角的“映射详细信息”详细信息窗口”单击“将实体映射到函数”并为您的实体选择插入、删除和更新存储过程。给我一点时间,我会为每个步骤发布一些屏幕截图。
    • Santiago,我要再说一次,如果重新生成模型,手动修改模型将丢失。是否有可能在重新生成模型时保证修改是通过一些技巧恢复的?
    猜你喜欢
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 2012-05-09
    • 1970-01-01
    • 2015-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多