Entity Framework параллельная блокировка сохранений - PullRequest
0 голосов
/ 06 марта 2019

Я пишу довольно простое многопользовательское WPF-приложение, в котором каждый пользователь может изменять / удалять свои собственные данные.Означает, что каждый пользователь может видеть все данные других пользователей, но не имеет права изменять или удалять их.Все приложение работает хорошо до тех пор, пока 2 или более клиентов не попадут в метод savechanges одновременно, а затем я иногда сталкиваюсь с тупиком базы данных.

Транзакция (ID процесса 57) была заблокирована на ресурсах блокировки с другимпроцесс и был выбран в качестве жертвы тупика.Перезапустите транзакцию.

Я понятия не имею, почему возникает эта тупиковая ситуация, поскольку невозможно, чтобы разные пользователи одновременно изменяли или удаляли одни и те же данные.

Я написал короткую программу, которая демонстрирует поведение.Метод удаления в конце моделирует двух пользователей, удаляющих их собственные данные одновременно.

Вот мой DbContext и 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();
    }
}

А вот код для генерации тестовых данных:

   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();
        }

Наконец метод, который вызывает тупик

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();
            }
        }
    }

Понятия не имею, почему это заходит в тупик, есть идеи, что я делаю неправильно?

...