首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实体框架6与悲观并发

实体框架6与悲观并发
EN

Stack Overflow用户
提问于 2017-05-11 10:05:21
回答 2查看 7.6K关注 0票数 1

我正在进行一个项目,以逐步淘汰遗留应用程序。在过程中,作为临时解决方案,我们使用数据库与遗留应用程序集成。

遗留应用程序使用具有可序列化隔离级别的事务。由于数据库与遗留应用程序的集成,我目前最好使用相同的悲观并发模型和可序列化的隔离级别。

这些序列化的事务不仅应该包装在SaveChanges语句的周围,而且还应该包含一些数据读取。

我这么做是靠

  • 在我的transactionScope周围创建一个具有序列化隔离级别的DbContext。
  • 创建一个DbContext
  • 读一些书
  • 对对象做一些更改
  • 在SaveChanges上调用DbContext
  • 提交事务范围(从而保存更改)

我的概念是,这将我的整个读写过程封装在序列化的事务中,然后提交。

我认为这是一种悲观的并发方式。

然而,阅读本文,https://learn.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application指出ef不支持悲观并发。

我的问题是:

  • 答: EF支持我在读写过程中使用可序列化事务的方式吗?
  • B:将读和写封装在一个事务中,可以保证在提交事务时不会更改读取的数据。
  • C:这是一种悲观的并发,对吗?
EN

回答 2

Stack Overflow用户

发布于 2017-07-26 10:27:00

实现悲观并发的一种方法是使用如下一种方式:

代码语言:javascript
复制
var options = new TransactionOptions
{
   IsolationLevel = System.Transactions.IsolationLevel.Serializable,
   Timeout = new TimeSpan(0, 0, 0, 10)
};

using(var scope = new TransactionScope(TransactionScopeOption.RequiresNew, options))
{ ... stuff here ...}

在Assemblies\Microsoft\Framework.NETFramework\v4.6.1\System.Transactions.dll中,您似乎必须右键单击TransactionScope,然后将其添加为: reference TransactionScope的引用

但是,如果有两个线程试图增加同一个计数器,则会发现一个成功,而另一个线程则在10秒内完成超时。这样做的原因是,当它们继续保存更改时,它们都需要将它们的锁升级到独占状态,但它们不能这样做,因为其他事务已经在同一行上持有共享锁。然后,Server将在一个事务失败以解决死锁后检测死锁。失败的一个事务将释放共享锁,而第二个事务将能够将其共享锁升级为独占锁并继续执行。

解决此死锁的方法是使用以下内容向数据库提供UPDLOCK提示:

代码语言:javascript
复制
private static TestEntity GetFirstEntity(Context context) {
return context.TestEntities
              .SqlQuery("SELECT TOP 1 Id, Value FROM TestEntities WITH (UPDLOCK)")
              .Single();
}

这段代码来自Ladislav的博客,现在看起来是不可用的。另一种选择是求助于乐观锁定。

票数 3
EN

Stack Overflow用户

发布于 2017-05-11 19:19:59

该文档指出,EF没有内置的悲观并发支持。但这并不意味着你不能悲观地锁定EF。,这样您就可以对EF!进行悲观的锁定。

菜谱很简单:

  • 使用事务(不一定是可串行化的,因为它会导致糟糕的perf.) -重新提交对use...but依赖.
  • 执行更改,调用dbcontext.savechanges()
  • 请手动锁定表-执行the,或者随意使用代码att。如下所示。
  • 带有提示的给定until命令将保持数据库锁定,直到给定事务的持续时间为止。
  • 有一件事您需要注意:在执行锁时,加载的实体可能会过时,因此锁定表中的所有实体都应该重新获取(重新加载)。

我做了很多悲观的锁定,但乐观锁定更好。你不能这样做。--

悲观锁定无法帮助的一个典型示例是父级子关系,您可以锁定父级,并将其视为一个聚合(因此假设您也是唯一能够访问该子节点的)。因此,如果其他线程试图访问父对象,则在其他线程从父表释放锁之前,它将无法工作(将被阻塞)。但是使用ORM,任何其他编码器都可以独立地加载子程序-从那时起,2个线程将对子对象进行更改.使用悲观锁定,您可能会弄乱数据,如果乐观,您将得到异常,您可以重新加载有效数据并再次尝试保存.

所以代码:

代码语言:javascript
复制
public static class DbContextSqlExtensions
{
    public static void LockTable<Entity>(this DbContext context) where Entity : class
    {
        var tableWithSchema = context.GetTableNameWithSchema<Entity>();
        context.Database.ExecuteSqlCommand(string.Format("SELECT null as dummy FROM {0} WITH (tablockx, holdlock)", tableWithSchema));
    }
}

public static class DbContextExtensions
{
    public static string GetTableNameWithSchema<T>(this DbContext context)
                where T : class
    {
        var entitySet = GetEntitySet<T>(context);
        if (entitySet == null)
            throw new Exception(string.Format("Unable to find entity set '{0}' in edm metadata", typeof(T).Name));

        var tableName = GetStringProperty(entitySet, "Schema") + "." + GetStringProperty(entitySet, "Table");
        return tableName;
    }

    private static EntitySet GetEntitySet<T>(DbContext context)
    {
        var type = typeof(T);
        var entityName = type.Name;
        var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;

        IEnumerable<EntitySet> entitySets;
        entitySets = metadata.GetItemCollection(DataSpace.SSpace)
                         .GetItems<EntityContainer>()
                         .Single()
                         .BaseEntitySets
                         .OfType<EntitySet>()
                         .Where(s => !s.MetadataProperties.Contains("Type")
                                     || s.MetadataProperties["Type"].ToString() == "Tables");
        var entitySet = entitySets.FirstOrDefault(t => t.Name == entityName);
        return entitySet;
    }

    private static string GetStringProperty(MetadataItem entitySet, string propertyName)
    {
        MetadataProperty property;
        if (entitySet == null)
            throw new ArgumentNullException("entitySet");
        if (entitySet.MetadataProperties.TryGetValue(propertyName, false, out property))
        {
            string str = null;
            if (((property != null) &&
                (property.Value != null)) &&
                (((str = property.Value as string) != null) &&
                !string.IsNullOrEmpty(str)))
            {
                return str;
            }
        }
        return string.Empty;
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43912368

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档