Проблемы с производительностью Entity Framework при добавлении дочернего элемента в список - PullRequest
0 голосов
/ 19 декабря 2018

Я работаю над проектом, в котором мы используем Entity Framework 6.1.3.Прямо сейчас у нас возникают довольно большие проблемы с производительностью при добавлении дочернего объекта в список родительского объекта (см. Пример кода ниже).

Мы используем отложенную загрузку, поэтому я заметил, что все работает нормальнопока мы не вызовем _parent.Children.Add(child);, так как кажется, что он загружает все дочерние элементы из базы данных, чтобы иметь возможность добавить нового.Поскольку некоторые из наших родительских объектов имеют около 50 000 дочерних объектов, это задерживает этот простой вызов вставки на 7-8 секунд, а иногда даже вызывает тайм-ауты.

Для меня не имеет смысла загружать все Entity Frameworkдети просто для того, чтобы добавить один, так есть ли способ, которым я могу избежать этого, или это недостаток дизайна Entity Framework, и мы должны найти обходной путь?

Я, очевидно, хотел бы найти решение для этого и предпочел бы не реализовывать чисто запросы ADO для этой одной проблемы.

Спасибо!

public class Parent 
{
    public Guid Id { get; set; }
    public virtual ICollection<Child> Children { get; set; }
}

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

public class ParentAggregate
{
    private readonly Parent _state;

    public ParentAggregate(Parent state)
    {
        _state = state;
    }

    public void AddChild(Guid id)
    {
        var child = new Child { Id = id };
        _state.Children.Add(child);
    }
}

1 Ответ

0 голосов
/ 19 декабря 2018

На мой взгляд, для Entity Framework не имеет смысла загружать всех дочерних элементов только для того, чтобы добавить один

Ленивая загрузка происходит в первый раз, когда вы получаете доступ свойство навигации через его getter .И пример кода

_parent.Children.Add(child);

состоит из двух операций:

(1) получить свойство Children (через свойство getter !):

var children = _parent.Children;

(2) выполнить с ним некоторую операцию (в этом случае вызвать метод Add):

children.Add(child);

Из-за операции (1) происходит отложенная загрузка.Как видите, EF не имеет к этому никакого отношения, потому что не контролирует его.И нет никакого способа узнать, что вы собираетесь делать со значением этого свойства - перечислите его, проведите подсчет или используйте методы Add, Remove и т. Д.

Вот несколько решений.

Во-первых, зачем вообще ленивая загрузка?У него так много побочных эффектов и неэффективностей, и все они могут быть легко решены с помощью EF, предоставляемой из готовой к загрузке системы методами Include.Вот почему по умолчанию EF Core («будущее EF») не использует отложенную загрузку по умолчанию и требует специального пакета и процедуры для его включения.

Во-вторых, если вы настаиваете на использовании отложенной загрузки, то у вас естьследующие две опции:

(A) Отключить отложенную загрузку во время изменения данных (требуется доступ к / управление экземпляром DbContext):

dbContext.Configuration.LazyLoadingEnabled = false;
_parent.Children.Add(child);
dbContext.Configuration.LazyLoadingEnabled = true;

Для этого также требуется свойство коллекциибыть инициализированным во избежание NRE.

(B) Используйте явное поле поддержки и предоставьте некоторый прямой доступ к нему (чтобы не вызывать ленивую загрузку с помощью средства доступа к свойству).Например:

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

    private ICollection<Child> children;
    public virtual ICollection<Child> Children { get => children; set => children = value; }

    public void Add(Child child)
    {
        // use the backing field directly
        if (children == null) children = new HashSet<Child>();
        children.Add(child); 
    }
}
...