实体框架并发savechanges死锁 [英] Entity framework concurrent savechanges deadlock

查看:77
本文介绍了实体框架并发savechanges死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个非常简单的多用户WPF应用程序,每个用户都可以修改/删除自己的数据。表示每个用户可以查看其他用户的所有数据,但不允许修改或删除它。整个应用程序运行良好,直到2个或更多客户端同时点击savechanges方法,然后我有时会遇到数据库死锁。

事务(进程ID 57)已死锁锁定资源与另一个进程,并已被选为死锁受害者。重新运行该事务。



我不知道为什么会出现这种死锁,因为不可能由不同的用户同时修改或删除相同的数据。



我写了一个简短的程序来证明这种行为。最后的删除方法模拟两个用户同时删除自己的数据。



这是我的DbContext和Datamodel:



  public   class 上下文:DbContext 
{
public DbSet< City> dbsCities { get ; set ; }
public DbSet< House> dbsHouses { get ; set ; }
public DbSet< Person> dbsPersons { get ; set ; }

public Context(): base @ Server = .\SQLExpress; Database = test; Trusted_Connection = Yes; Connection Timeout = 5
{
Configuration.AutoDetectChangesEnabled = true ;
Configuration.LazyLoadingEnabled = false ;
Configuration.ProxyCreationEnabled = false ;
}

受保护 覆盖 void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity< City>()。HasMany(c = > c.Houses).WithRequired(c = > c.City).WillCascadeOnDelete( true );
modelBuilder.Entity< House>()。HasMany(p = > p.Residents).WithRequired(p = > p.House).WillCascadeOnDelete( true );
}
}

public class City
{
public Guid Id { get ; set ; }
public string 名称{ get ; set ; }
public virtual ICollection< House>房屋{获取; set ; }

public City()
{
Id = Guid.NewGuid();
Houses = new List< House>();
}
}

public class House
{
public Guid Id { get ; set ; }
public int Number { get ; set ; }
public string 代码{ get ; set ; }

public virtual City City {获得; set ; }
public virtual ICollection< Person>居民{获取; set ; }

public House()
{
Id = Guid.NewGuid();
Residents = new List< Person>();
}
}


public class Person
{
public Guid Id { get ; set ; }
public string 名字{ get ; set ; }
public string 姓氏{ get ; set ; }

public virtual House House {获得; set ; }

public Person()
{
Id = Guid.NewGuid();
}
}



这里生成testdata的代码:

 < span class =code-keyword> using (Context ctx =  new  Context())
{
List< City> cities = new 列表< City>(){ new City(){Name = 纽约}};

for int h = 1 ; h < = 50 ; h ++)
{
string code = A ;
if (h% 2 == 0
code = B;

House house = new House(){Number = h,Code = code};
cities [ 0 ]。Houses.Add(house);
for int i = 0 ; i < = 100 ; i ++)
house.Residents.Add(< span class =code-keyword> new Person(){Firstname = A,Lastname = B});
}

ctx.dbsCities.AddRange(cities);
ctx.SaveChanges();
}



最后导致死锁的方法

 私有  void 删除(对象发​​件人,RoutedEventArgs e)
{
string [] to = new string [] { A B};
Parallel.ForEach(to,code = >
{
DeleteHouses(code);
});
}

private void DeleteHouses( string 代码)
{
使用 var ctx = new Context())
{
ctx.Database.Log + = Console.WriteLine;
var todel = ctx.dbsHouses.Where(d => d.Code == code);
if (todel!= null
{
ctx。 dbsHouses.RemoveRange(todel);
ctx.SaveChanges();
}
}
}





我尝试了什么:



我尝试过使用一个使用IsolationLevel Snapshot的TransactionScope,这在其他一些论坛帖子中推荐使用

但没有成功。

无论如何,我认为它与IsolationLevel无关,因为我的查询正在删除

不同的行。

解决方案

< blockquote>谁告诉你使用并行编程?



我现在可以看到为什么EF会说糟糕的说唱。



用保存更改等方式敲击系统



您需要回归基础知识。



c# - Parallel不适用于实体框架 - Stack Overflow [ ^ ]


I am writing a quite simple Multiuser WPF application where each user can modify / delete his own data. Means each user can see all the data of other users but is not allowed to modify or delete it. The whole application performs well until 2 or more clients hit the savechanges method at the same time, then i sometimes run into a Database Deadlock.

Transaction (Process ID 57) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.


I have no idea why it comes to that deadlock, as it is not possible that the same data is modified or deleted at the same time by different users.

I wrote a short program that demonstates the behaviour. The delete method at the end simulates two users deleting their own data at the same time.

Here is my DbContext and Datamodel:

public class Context : DbContext
{
    public DbSet<City> dbsCities    { get; set; }
    public DbSet<House> dbsHouses   { get; set; }
    public DbSet<Person> dbsPersons { get; set; }

    public Context() : base(@"Server=.\SQLExpress;Database=test;Trusted_Connection=Yes;Connection Timeout=5")
    {
        Configuration.AutoDetectChangesEnabled = true;
        Configuration.LazyLoadingEnabled = false;
        Configuration.ProxyCreationEnabled = false;
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<City>().HasMany(c => c.Houses).WithRequired(c => c.City).WillCascadeOnDelete(true);
        modelBuilder.Entity<House>().HasMany(p => p.Residents).WithRequired(p => p.House).WillCascadeOnDelete(true);
    }
}

public class City
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<House> Houses { get; set; }

    public City()
    {
        Id = Guid.NewGuid();
        Houses = new List<House>();
    }
}

public class House
{
    public Guid Id                                  { get; set; }
    public int Number                               { get; set; }
    public string Code                              { get; set; }

    public virtual City City                        { get; set; }
    public virtual ICollection<Person> Residents    { get; set; }

    public House()
    {
        Id = Guid.NewGuid();
        Residents = new List<Person>();
    }
}


public class Person
{
    public Guid Id          { get; set; }
    public string Firstname { get; set; }
    public string Lastname  { get; set; }

    public virtual House House { get; set; }

    public Person()
    {
        Id = Guid.NewGuid();
    }
}


And here the code to generate testdata:

using (Context ctx = new Context())
        {
            List<City> cities = new List<City>() { new City() { Name = "New York" } };

            for (int h = 1; h <= 50; h++)
            {
                string code = "A";
                if (h % 2 == 0)
                    code = "B";

                House house = new House() {Number = h, Code = code };
                cities[0].Houses.Add(house);
                for (int i = 0; i <= 100; i++)
                    house.Residents.Add(new Person() { Firstname = "A", Lastname = "B" });
            }

            ctx.dbsCities.AddRange(cities);
            ctx.SaveChanges();
        }


Finally the Method that causes the deadlock

private void Delete(object sender, RoutedEventArgs e)
    {
        string[] to = new string[] {"A", "B"};
        Parallel.ForEach(to, code =>
        {
            DeleteHouses(code);
        });
    }

    private void DeleteHouses(string code)
    {
        using (var ctx = new Context())
        {
            ctx.Database.Log += Console.WriteLine;
            var todel = ctx.dbsHouses.Where(d=>d.Code == code);
            if (todel != null)
            {
                ctx.dbsHouses.RemoveRange(todel);
                ctx.SaveChanges();
            }
        }
    }



What I have tried:

I have tried using a TransactionScope using IsolationLevel Snapshot which was
recommended in some other forum posts without success.
Anyway i think it has nothing to do with the IsolationLevel as my queries are deleting
different rows.

解决方案

Who told you to use "parallel" programming?

I can see now why EF gets a bad rap.

Hammering the system with "Save Changes", etc.

You need to get back to basics.

c# - Parallel doesnt work with Entity Framework - Stack Overflow[^]


这篇关于实体框架并发savechanges死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆