EF4, Lambda, Шаблон репозитория и DTO - PullRequest
6 голосов
/ 08 сентября 2010

У меня полусложный вопрос, касающийся Entity Framework4, лямбда-выражений и объектов передачи данных (DTO).

Итак, у меня небольшой проект EF4, и, следуя установленным принципам ОО, у меня есть DTO, обеспечивающий уровень абстракции между потребителями данных (GUI) и моделью данных.

  • VideoDTO = DTO с геттерами / сеттерами, используемыми графическим интерфейсом
  • VideoEntity = объект, созданный EF4

Мой вопрос вращается вокруг использования DTO GUI (и вообще без использования GUI Entity), в сочетании с необходимостью передавать лямбду на уровень данных. Мой слой данных - это базовый шаблон хранилища с надписью Add. Изменить, Удалить, Получить, GetList и т. Д. Попытка реализовать метод Find с подписью, например, так:

public IEnumerable<VideoDTO> Find(Expression<Func<VideoEntity, bool>> exp)
...
_dataModel.Videos.Where(exp).ToList<Video>()
---

Моя проблема / беспокойство заключается в том, что «exp» должен иметь тип VideoEntity вместо VideoDTO. Я хочу сохранить разделение задач, чтобы GUI не знал об объектах Entity. Но если я попытаюсь перейти в

Func<VideoDTO, bool> 

Тогда я не могу выполнить LINQ Where для этого выражения, используя фактическую модель данных.

Есть ли способ преобразовать Func<VideoDTO,bool> в Func<VideoEntity, bool>

В идеале моя подпись метода будет принимать Func<VideoDTO, bool>, и в этом случае GUI не будет иметь ссылки на базовый объект данных.

Это достаточно ясно? Спасибо за вашу помощь


Спасибо за ответы вам обоим.

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

Еще раз спасибо!

Ответы [ 2 ]

1 голос
/ 08 сентября 2010

В архитектурах, подобных CQRS , такое преобразование вообще не требуется, поскольку стороны приложения для чтения и записи разделены.

Но в Вашем случае Вы не можете сбежать от перевода.

Прежде всего - Вы должны быть более точными при определении репозиториев. Подпись репозитория - это то, что вы хотите оставить явным, а не родовым.

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

В вашем примере это немного лучше, потому что универсальность выражений связана с dto, а не entity.

Это то, что я делаю (используя NHibernate.Linq, но идея остается)

public class Application{
  public Project Project {get;set;}    
}

public class ApplicationRepository{
 public IEnumerable<Application> Search(SearchCriteria inp){
       var c=Session.Linq<Application>();
       var q=c.AsQueryable();
       if(!string.IsNullOrEmpty(inp.Acronym))
        q=q.Where(a=>a.Project.Acronym.Contains(inp.Acronym));
       /*~20 lines of similar code snipped*/
       return q.AsQueryable();
 }
}

//used by client
public class SearchCriteria{
 public string Acronym{get;set;}
 /*some more fields that defines how we can search Applications*/
}

Если вы хотите сохранить свои выражения, одним из способов было бы определить словарь вручную следующим образом:

var d=new Dictionary<Expression<Func<VideoDTO,object>>,
                     Expression<Func<VideoEntity,object>>{
  {x=>x.DtoPropNumberOne,x=>x.EntityPropNumberOne} /*, {2}, {3}, etc.*/
};

И использовать его позже:

//can You spot it?
//client does not know explicitly what expressions dictionary contains
_dataModel.Videos.Where(d[exp]).ToList<Video>();
//and I'm not 100% sure checking expression equality would actually work

Если вы не хотите писать словарь сопоставления вручную, вам понадобятся некоторые продвинутые приемы. Одной из идей было бы перевести выражение dto в строку, а затем обратно в выражение объекта. Здесь - некоторые идеи (хотя и относящиеся к сортировке), которые могут помочь. Выражения довольно сложные звери.

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

0 голосов
/ 08 сентября 2010

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

Чтобы пойти дальше, вы можете открыть доступные для поиска поля из VideoEntity через интерфейс (IVideoEntityQueryFields) и использовать этокак тип в выражении.

Если вы не хотите добавлять интерфейс к вашим сущностям, то более сложным вариантом является использование объекта VideoEntityQuery и чего-то, что переводит Expression<Func<VideoEntityQuery,bool>> в Expression<Func<VideoEntity,bool>>.

...