Я работаю с EF6, а не с Core, но, насколько я знаю, то же самое применимо и здесь.
Прежде всего, переместите эту функцию в свой репозиторий, чтобы все вызовы совместно использовали экземпляр DbContext.
Во-вторых, используйте Включите в свой DbSet в свойствах, чтобы загружать их:
ctx.DbSet<ChannelObjectModel>()
.Include(x => x.Fields)
.Include(x => x.Mapping)
.Include(x => x.ParentObject)
...
Хорошей практикой является сделать это функцией контекста (или метода расширения), вызываемой, например, BuildChannelObject (), и она должна возвращать IQueryable - только включения.
Затем вы можете запустить рекурсивную часть:
public ChannelObjectModel GetChannelObjectModel(Guid id)
{
var set = ctx.BuildChannelObject(); // ctx is this
var channelModel = set.FirstOrDefault(x => x.Id == id); // this loads the first level
LoadRecursive(channelModel, set);
return channelModel;
}
private void LoadRecursive(ChannelObjectModel c, IQueryable<ChannelObjectModel> set)
{
if(c == null)
return; // recursion end condition
c.ParentObject = set.FirstOrDefault(x => x.Id == c?.ParentObject.Id);
// all other properties
LoadRecursive(c.ParentObject, set);
// all other properties
}
Если весь этот код использует один и тот же экземпляр DbContext, он должен быть довольно быстрым. Если нет, вы можете использовать другой трюк:
ctx.DbSet<ChannelObjectModel>().BuildChannelObjectModel().Load();
Это загружает все объекты в кэш-память вашего DbContext. К сожалению, он умирает с экземпляром контекста, но делает эти рекурсивные вызовы намного быстрее, так как не выполняется отключение базы данных.
Если это все еще медленно, вы можете добавить AsNoTracking()
в качестве последней инструкции BuildChannelObjectModel ().
Если это по-прежнему происходит медленно, просто внедрите кэш памяти этих объектов в масштабах приложения и используйте его вместо запросов к базе данных каждый раз - это прекрасно работает, если ваше приложение представляет собой службу, которая может иметь длительный запуск, но затем работать быстро.
Весь другой подход состоит в том, чтобы включить отложенную загрузку, помечая свойства навигации как виртуальные, но помните, что возвращаемый тип будет производным типом анонимного прокси, а не вашего исходного ChannelObjectModel! Кроме того, свойства будут загружаться только до тех пор, пока вы не утилизируете контекст - после этого вы получите исключение. Загрузить все свойства с контекстом, а затем вернуть завершенный объект, также немного сложно - самый простой (но не лучший!) Способ сделать это для сериализации объекта в JSON (помните о ссылочных ссылках) перед его возвратом.
Если вас это не устраивает, переключитесь на nHibernate, который, как я слышал, по умолчанию имеет кеш приложения.