EF Core - обеспечивает приоритет при выполнении команд в транзакции - PullRequest
0 голосов
/ 12 ноября 2018

Я хочу удалить 2 набора данных в базе данных, используя EF Core.
Все коды являются гипотетическими.

Модели данных:

class Parent
{
    public int Id { get; set; }
}

class Child
{
    public int Id { get; set; }

    public int ParentId { get; set; }

    public virtual Parent Parent { get; set; }

    public bool Flag { get; set; }
}

Давайте предположим, что я хочу удалить все записи [Child] с (ParentId = 100) и (flag = false), после этого, если (child.ParentId = 100) .length = 0, затем удалить и самого родителя.
Итак, вот класс обслуживания:

class Service
{
    public void Command(int parentId)
    {
        Parent parent = GetParent(parentId);
        List<Child> children = GetChildren(parent);

        List<Child> toDelete = children.Where(x => !x.Flag).ToList();
        foreach(var child in toDelete)
        {
            var entry = DbContext.Entry(child);
            entry.State = EntityState.Deleted;
        }

        List<Child> remainChildren = children.Where(x => x.Flag).ToList();
        if (!remainChildren.Any())
        {
            var entry = DbContext.Entry(parent );
            entry.State = EntityState.Deleted;
        }

        SaveChanges();
    }
}

У меня есть несколько сценариев, которые вызывают метод Service.Command.
Поскольку я вызываю SaveChanges() только один раз, я предполагаю, что все операции удаления будут выполнены в одной транзакции, и, конечно, они будут в следующем порядке:

  1. Удалить дочерние записи
  2. Удалить родителя

но EF отправляет запросы к базе данных следующим образом:

  1. Удалить родителя
  2. Удалить дочерние записи

Очевидно, это вызовет исключение ForeignKey.

Есть ли способ заставить EF Core выполнять запросы для того, чтобы я написал код?

1 Ответ

0 голосов
/ 12 ноября 2018

Установить каскадное удаление родительского потомка на уровне БД.

Запрос необходимых данных одним нажатием ...

var data = context.Parents.Where(p => p.ParentId == parentId)
  .Select(p => new 
  {
    Parent = p,
    ChildrenToRemove = p.Children.Where(c => c.Flag).ToList(),
    HasRemainingChildren = p.Children.Any(c => !c.Flag)
  }).Single();

Тогда это просто вопрос проверки данных и принятия соответствующих мер. Если оставшихся дочерних элементов нет, удалите родительский элемент и дайте каскаду позаботиться об этом. В противном случае просто удалите дочерние элементы из контекста.

if(!data.HasRemainingChildren)
  context.Parents.Remove(data.Parent);
else
  context.Children.RemoveRange(data.ChildrenToRemove);

Для больших объектов вы можете дополнительно оптимизировать это, выбрав только идентификаторы, затем связав их с новыми экземплярами сущностей, прикрепив их к новому DbContext, а затем выполните вызовы Remove / RemoveRange. Эта опция является оптимизацией для работы с большим количеством элементов или «большими» объектами, которые в противном случае привели бы к большому количеству данных по сети.

...