Искаженное свойство загрузки производного класса с использованием Include

У меня есть классы вроде:

Person
{
   Name
   Address
}

Employee : Person
{
   Compensation - object
}

Visitor : Person
{

}

Если я напишу linq:

var persons = Context.Persons
                .Include("Compensation");

Я получаю ошибку:

Указанный путь Include недействителен. EntityType 'Person' не делает объявить свойство навигации с помощью имя "Компенсация".

Он работает нормально, если я делаю:

var persons = Context.Persons
                .OfType<Employee>()
                .Include("Compensation");

Но я хотел бы получить Сотрудников и посетителей в том же запросе.

Похоже, есть запрос на эту функцию в EF4 UserVoice: http://data.uservoice.com/forums/72025-ado-net-entity-framework-ef-feature-suggestions/suggestions/1249289-include-property-of-derived-classes?ref=title

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

Каков хороший способ обхода проблемы?

Ответы

Ответ 1

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

var persons = Context.Persons
                     .OfType<Employee>()
                     .Include("Compensation")
                     .Concat<Person>(Context.Persons.OfType<Visitor>());

Ответ 2

Вот хороший пример загрузки Persons и включения Compensation для Employees

Заменить Reference() на Collection() для свойства коллекции.

IQueryable<Person> GetPersons()
{
    var persons = Context.Persons;
    foreach(var entry in persons.OfType<Employee>())
        Context.Entry(entry).Reference(e => e.Compensation).Load();
    return persons;
}

Не уверен, что он эффективен, но он работает, и намерение понятнее, чем при подключении.

Основываясь на этом SO-ответе

Ответ 3

var employees = context.Persons.OfType<Employee>().Include(x => x.Compensation).ToArray();

var nonEmployees = context.Persons.Except(context.Persons.OfType<Employee>()).ToArray();

var people = employees.Concat(nonEmployees);

Ответ 4

Эта ужасная вещь работает:

var persons = context.Persons
                .Concat(context.Employees
                    .Where(e => e.Compensation.Amount >= 0))
                .Concat(context.Visitors
                    .Where(v => v.SomeProperty == "AlwaysTrue"));

Я не уверен, почему, но когда вы фильтруете свойство объекта, объект свойства загружается. Если вы не хотите фильтровать это свойство, используйте условие, которое всегда будет истинным.

Отказ от ответственности: я не знаю, насколько эффективен результирующий запрос. Я проверил sql, сгенерированный, когда тестировал это в несколько более сложном сценарии, и он был очень большим.