Передача запроса GetWhere (Func<entityDTO, bool>) в метод уровня данных, для работы которого требуется параметр (Func<entity, bool>)
У меня есть следующий метод в классе доступа к данным, который использует структуру объекта:
public static IEnumerable<entityType> GetWhere(Func<entityType, bool> wherePredicate)
{
using (DataEntities db = new DataEntities())
{
var query = (wherePredicate != null)
? db.Set<entityType>().Where(wherePredicate).ToList()
: db.Set<entityType>().ToList();
return query;
}
}
Это прекрасно работает, когда я использую сущности во всех слоях... однако я пытаюсь перейти к использованию класса DTO, и я хотел бы сделать что-то вроде следующего:
public static IEnumerable<EntityTypeDTO> GetWhere(Func<EntityTypeDTO, bool> wherePredicate)
{
//call a method here which will convert Func<EntityTypeDTO,bool> to
// Func<EntityType,bool>
using (DataEntities db = new DataEntities())
{
var query = new List<EntityType>();
if (wherePredicate == null)
{
query = db.Set<EntityType>().ToList();
}
else
{
query = (wherePredicate != null)
? db.Set<EntityType>().Where(wherePredicate).AsQueryable<EntityType>().ToList()
: db.Set<EntityType>().ToList();
}
List<EntityTypeDTO> result = new List<EntityTypeDTO>();
foreach(EntityType item in query)
{
result.Add(item.ToDTO());
}
return result;
}
}
По сути, я хочу метод, который будет конвертировать Func в Func.
Я думаю, что я должен разбить Func в дерево выражений, а затем перестроить его как-то в entityType?
Я хочу сделать это, чтобы слой представления мог просто передавать запросы Expression?
Я что-то упускаю из виду или есть более простой шаблон проектирования, который может передавать запрос из DTO в класс доступа к данным, не зная деталей запроса?
Я пытался заставить DTO наследовать сущность, которая, похоже, тоже не работает?
Если есть лучший шаблон дизайна, который мне не хватает, мне бы понравился указатель, и я смогу провести расследование оттуда...
2 ответа
Во-первых, я бы посоветовал вам поместить собственный запрашивающий слой перед Entity Framework, а не разрешать передачу любого произвольного Func, потому что в будущем будет очень легко передать Func, который Entity Framework не может перевести в SQL оператор (он может переводить только некоторые выражения - основы хороши, но если ваше выражение вызывает, например, метод C#, то Entity Framework, вероятно, потерпит неудачу).
Таким образом, в вашем поисковом слое могут быть классы, которые вы создаете в качестве критериев (например, класс поиска "ContainsName" или класс "ProductHasId"), которые затем преобразуются в выражения в вашем слое поиска. Это полностью отделяет ваше приложение от ORM, что означает, что детали ORM (например, сущности или ограничения, которые могут и не могут быть переведены Funcs) не просачиваются. Там было много написано об этом, некоторые договоренности.
И последнее замечание: если вы работаете близко к слою ORM, Entity Framework очень умен, и вы, вероятно, могли бы пройти долгий путь, не пытаясь перевести ваш Func
var results = context.Products
.Select(p => new { ID = p.ProductID, Name = p.ProductName })
.Where(p => p.ID < 10)
.ToList();
Выполненный SQL:
SELECT
[Extent1].[ProductID] AS [ProductID],
[Extent1].[ProductName] AS [ProductName]
FROM [dbo].[Products] AS [Extent1]
WHERE [Extent1].[ProductID] < 10
Итак, если вы изменили свой код, чтобы получить что-то вроде..
return context.Products
.Map<Product, ProductDTO()>()
.Where(productDtoWherePredicate)
.ToList();
... тогда вы можете быть в порядке с Funcs, которые у вас уже есть. Я предполагаю, что у вас уже есть какие-то функции отображения, которые можно получить от EF-сущностей до DTO (но если нет, то вы можете обратиться к AutoMapper, чтобы помочь вам - у которого есть поддержка "проекций", которые в основном являются картами IQueryable).
Я собираюсь изложить это как ответ. Спасибо Дэну за быстрый ответ. Глядя на то, что вы говорите, я могу написать набор классов запросов / фильтров. например, возьмите следующий код:
GetProducts().GetProductsInCategory().GetProductsWithinPriceRange(minPrice, maxPrice);
Этот код будет работать так: Get Products будет получать все продукты в таблице, а остальные функции будут фильтровать результаты. если все запросы выполняются таким образом, это может создать значительную нагрузку на соединения уровня доступа к данным / соединения с сервером БД... не уверен.
или же
Альтернативный вариант, над которым я буду работать: если каждая функция создает выражение Linq, я могу объединить их следующим образом: как объединить несколько запросов linq в один набор результатов? это может позволить мне сделать это таким образом, чтобы я мог вернуть отфильтрованный набор результатов из базы данных.
В любом случае я отмечаю это как ответ. Я обновлю, когда у меня будет больше деталей.