2013-12-18 6 views
3

я извлечение данных из базы данных (с Entity Framework) с использованием DTOS:Загрузка виртуальных свойств

IQueryable<User> users = repository.ListFiltered<User>(n => n.Active); 

var usersDTO = from user in users 
       select new UserAccountDTO{ 
        UserId = user.Id, 
        UserName = user.Name, 
        InstitutionName = user.Institution.Name, 
        InstitutionCountry = user.Institution.Country.Name 
       }; 

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

Но проблема в том, что мне не нравится код, я хотел бы разделить код и объединить выбранные, есть ли все равно сделать это?

Я хотел бы, чтобы прийти к чему-то вроде:

users.LoadUserData().LoadInstitutionData(); 

Что вы говорите? Является ли это возможным?

+0

поэтому 'Institution' является навигационной собственностью' User'. Почему бы вам просто не «выбрать» в вашем репозитории.ListFiltered ? а затем выполните '.ToList()' в конце концов, потому что вы пытаетесь достичь, даже сложный, а не аккуратный. –

+0

Поскольку я не хочу извлекать все данные, мне нужны только некоторые поля и в некоторых условиях ... –

ответ

2

Проблема в том, что должно выглядеть LoadUserData() и LoadInstitutionData(). Предположим, что конечный продукт этой цепи методов должен быть IEnumerable<UserAccountDTO>. Последний метод в свободном синтаксисе всегда определяет выход. Таким образом, LoadInstitutionData() должен вернуть требуемый IEnumerable. Очистить.

Но как насчет ввода? Я вижу два варианта:

  • Вход является IEnumerable<User> (или IQueryable<User>). Хорошо, это означало бы, что LoadInstitutionData() не может делать ничего, кроме кода, который у вас уже есть. И это не решение, потому что вам не нравится код. Кроме того, для этого метода требуется, чтобы user.Institution и Country были загружены или ленивы загружаться, что трудно выразить в любой форме кодового контракта.

  • Вход является IEnumerable<UserAccountDTO>, в котором LoadUserData установил прямые user свойства и что LoadInstitutionData должны пополняться Institution данных. Теперь встает вопрос: как это сделать LoadInstitutionData? Во всяком случае, то, что было сделано в одном запросе, теперь займет как минимум два запроса, может быть, даже 1 + n.

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

Wait , AutoMapper.

Знаете ли вы, что AutoMapper может сделать это за вас так, как может понравиться вам?

Все, что вам нужно, это класс

class UserAccountDTO 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string InstitutionName { get; set; } 
    public string InstitutionCountryName { get; set; } // Notice the Name part! 
}; 

Отображение

AutoMapper.Mapper.CreateMap<User, UserAccountDTO>(); 

А с использованием:

using AutoMapper.QueryableExtensions; 

И краткий синтаксис:

var usersDTO = users.Project().To<UserAccountDTO>(); 

(И, конечно, AutoMapper от NuGet).

Automapper разрешит свойство InstitutionCountryName как user.Institution.Country.Name, а результат проекции - это одно выражение, которое переведено в один оператор SQL с объединением и всеми. Это код, который мне нравится.

+0

А, я не знал, что имя свойства, как «InstitutionCountryName», автоматически отображается из Institution.Country.Name? Хорошая функция. –

+0

Спасибо! Это потрясающе, но как насчет того, нужно ли мне работать с коллекциями? А как насчет применения фильтров? Является ли это возможным? Я имею в виду ... если мне нужно что-то вроде ** public IEnumerable Institutions = user.Institutions.Where (n => n.Active) ** –

+0

@ArielScherman AutoMapper также может заполнять дочерние коллекции. Частично загруженные коллекции - это другая глава. EF не поддерживает это напрямую, но вы можете достичь этого более тщательно: http://stackoverflow.com/a/13904825/861716 –

Смежные вопросы