Для всех, кто хочет проецировать значения не-db из запроса db, этот проект от u / Luis Aguilar был очень, очень полезен для меня.
У меня была очень большая устаревшая база данных (450 ГБ), которую нужно было обслуживать в OData / WebAPI.
Требование OData означало, что я не мог отфильтровать исходные данные (много), прежде чем вернуть их пользователю. Мы могли бы выдать это, но помимо этого это их данные, которые они запрашивают по своему усмотрению.
Что еще более важно, однако, унаследованные данные были слишком сложными, чтобы представлять их как есть, и существовала значительная бизнес-логика, необходимая для сопоставления необходимых данных (Include
свойств навигации / внешних ключей, предикатов длинных предложений и т. Д.) .
Это означало, что разбиение на страницы и ограничение результатов не будут доступны до тех пор, пока запрос не будет выполнен.
Обычные ярлыки для такого рода вещей включают различные стратегии, которые включают материализацию / энергичную загрузку. Однако из-за размера набора данных и отсутствия фильтрации это может привести к значительному увеличению объема памяти и сбоям в памяти.
Итак, некоторый код. Вот мой вызов конфигурации, аналогичный тому, который требуется для AutoMapper или OData:
using ExpressionFramework.Projections;
using ExpressionFramework.Projections.Configuration;
public class ProjectionModelBuilder : ProjectionModel
{
protected override void OnModelCreating(ProjectionModelBuilder modelBuilder)
{
ClientDTO.ProjectionModel(modelBuilder);
OrderDTO.ProjectionModel(modelBuilder);
AnotherDTO.ProjectionModel(modelBuilder);
}
}
Этот дизайн позволяет мне сохранять правила проекции в классе DTO с остальной частью бизнес-логики. Вот как выглядит код уровня DTO:
public static void ProjectionModel(ProjectionModelBuilder modelBuilder)
{
modelBuilder
.Projection<ClientDTO>()
.ForSource<Client>(configuration =>
{
configuration.Property(dto => dto.Name).ExtractFrom(entity => entity.Name);
// etc
});
}
Где Client
- это мой тип сущности / EDM, сопоставленный с таблицей БД и внешним миллионом ключей.
Чтобы затем получить переведенное / спроецированное Queryable
, вот оно:
IClientQueryService service = _ioc.Resolve<IClientQueryService>(); // Repository pattern
var q = service.GetClients(); // withManyNavigationIncludes
var r = q.Where<Item>(
i =>
i.Name != null
&& i.Name != ""
// lather rinse repeat, with many sub-objects navigated also
).AsQueryable();
var projectionModel = new ProjectionModelBuilder();
var s = projectionModel.Project<ClientDTO, Client>(r).AsQueryable();
Уместны только последние две строки, но остальные включены для контекста.
Последнее, что мне нужно было сделать, это установить this.IsAutoConfigured = false;
в конструкторе для ProjectionSourceTypeConfiguration.cs
в коде Луиса; это позволило мне упорядочить определения проекций вручную, чтобы свойства навигации внутри родительских классов успешно конфигурировали их проекции.
Не могу отблагодарить https://stackoverflow.com/users/543712/luis-aguilar за его работу. После написания моего собственного поставщика LINQ / ExpressionVisitor
с различными вызовами, переводами и переходами к общим методам для решения различных проблем его проект стал находкой.
Если вы обнаружите, что необходимо направить вашу собственную обработку выражений для повышения производительности или по другим причинам, я бы рекомендовал эти два ответа для начала.