Подтвердить что ты не робот

Entity Framework. Есть ли способ автоматически загружать дочерние объекты без Include()?

Есть ли способ украсить ваши классы POCO, чтобы автоматически загружать дочерние объекты без необходимости использовать Include() каждый раз, когда вы их загружаете?

Скажем, у меня есть классный автомобиль со сложными свойствами для колес, дверей, двигателя, бампера, окон, выхлопных газов и т.д. И в моем приложении мне нужно загрузить мой автомобиль из моего DbContext 20 разных мест с разными запросами, и т.д. Я не хочу указывать, что я хочу включать все свойства каждый раз, когда я хочу загрузить свой автомобиль.

Я хочу сказать

List<Car> cars = db.Car
                .Where(x => c.Make == "Ford").ToList();

//NOT .Include(x => x.Wheels).Include(x => x.Doors).Include(x => x.Engine).Include(x => x.Bumper).Include(x => x.Windows)


foreach(Car car in cars)
{

//I don't want a null reference here.

String myString = car.**Bumper**.Title;
}

Могу ли я каким-то образом украсить свой класс POCO или в моем OnModelCreating() или установить конфигурацию в EF, которая сообщит, что он просто загрузит все части моего автомобиля, когда я загружу свой автомобиль? Я хочу сделать это с нетерпением, поэтому я понимаю, что создание виртуальных виртуальных навигационных систем не работает. Я знаю, что NHibernate поддерживает аналогичные функции.

Просто интересно, не хватает ли я чего-то. Спасибо заранее!

Приветствия,

Натан

Мне нравится решение ниже, но интересно, могу ли я вложить вызовы методам расширения. Например, скажем, что у меня похожая ситуация с Engine, где у меня много частей, я не хочу включать всюду. Могу ли я сделать что-то вроде этого? (Я пока не нашел способ для этого работать). Таким образом, если позже я узнаю, что Engine требует FuelInjectors, я могу добавить его только в BuildEngine и не добавлять его в BuildCar. Также, если я могу вложить вызовы, как я могу установить вызов в коллекцию? Как вызвать BuildWheel() для каждого из моих колес из моего BuildCar()?

public static IQueryable<Car> BuildCar(this IQueryable<Car> query) {
     return query.Include(x => x.Wheels).BuildWheel()
                 .Include(x => x.Doors)
                 .Include(x => x.Engine).BuildEngine()
                 .Include(x => x.Bumper)
                 .Include(x => x.Windows);
}

public static IQueryable<Engine> BuildEngine(this IQueryable<Engine> query) {
     return query.Include(x => x.Pistons)
                 .Include(x => x.Cylendars);
}

//Or to handle a collection e.g.
 public static IQueryable<Wheel> BuildWheel(this IQueryable<Wheel> query) {
     return query.Include(x => x.Rim)
                 .Include(x => x.Tire);
}

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

Entity framework linq query Включить() несколько дочерних объектов

4b9b3361

Ответ 1

Нет, вы не можете сделать это при сопоставлении. Типичным обходным путем является простой метод расширения:

public static IQueryable<Car> BuildCar(this IQueryable<Car> query) {
     return query.Include(x => x.Wheels)
                 .Include(x => x.Doors)
                 .Include(x => x.Engine)
                 .Include(x => x.Bumper)
                 .Include(x => x.Windows);
}

Теперь каждый раз, когда вы хотите запросить Car со всеми отношениями, вы просто выполните:

var query = from car in db.Cars.BuildCar()
            where car.Make == "Ford"
            select car;

Edit:

Вы не можете так называть вызовы. Включите работы над основной сущностью, с которой вы работаете, - эта сущность определяет форму запроса, поэтому после вызова Include(x => Wheels) вы все еще работаете с IQueryable<Car>, и вы не можете вызвать метод расширения для IQueryable<Engine>. Вы должны снова начать с Car:

public static IQueryable<Car> BuildCarWheels(this IQuerable<Car> query) {
    // This also answers how to eager load nested collections 
    // Btw. only Select is supported - you cannot use Where, OrderBy or anything else
    return query.Include(x => x.Wheels.Select(y => y.Rim))
                .Include(x => x.Wheels.Select(y => y.Tire));
}

и вы будете использовать этот метод следующим образом:

public static IQueryable<Car> BuildCar(this IQueryable<Car> query) {
     return query.BuildCarWheels()
                 .Include(x => x.Doors)
                 .Include(x => x.Engine)
                 .Include(x => x.Bumper)
                 .Include(x => x.Windows);
}

Использование не вызывает Include(x => x.Wheels), потому что оно должно добавляться автоматически, когда вы запрашиваете загрузку своих вложенных объектов автоматически.

Остерегайтесь сложных запросов, создаваемых такими сложными загружаемыми структурами загрузки. Это может привести к очень низкой производительности и большому количеству дублированных данных, переданных из базы данных.

Ответ 2

У этой же проблемы была и другая ссылка, в которой упоминался атрибут Include. В моем решении предполагается, что вы создали атрибут IncludeAttribute. С помощью следующего метода расширения и метода утилиты:

    public static IQueryable<T> LoadRelated<T>(this IQueryable<T> originalQuery)
    {
        Func<IQueryable<T>, IQueryable<T>> includeFunc = f => f;
        foreach (var prop in typeof(T).GetProperties()
            .Where(p => Attribute.IsDefined(p, typeof(IncludeAttribute))))
        {
            Func<IQueryable<T>, IQueryable<T>> chainedIncludeFunc = f => f.Include(prop.Name);
            includeFunc = Compose(includeFunc, chainedIncludeFunc);
        }
        return includeFunc(originalQuery);
    }

    private static Func<T, T> Compose<T>(Func<T, T> innerFunc, Func<T, T> outerFunc)
    {
        return arg => outerFunc(innerFunc(arg));
    }