Для всех, кто хочет проецировать значения, отличные от БД, из запроса БД, этот проект 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
— это мой тип Entity/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 Provider/ExpressionVisitor
с различными вызовами универсальных методов, переводами и проходами по дереву, чтобы все еще иметь различные проблемы, его проект был удачей.
Если вы обнаружите, что должны конвейеризировать обработку собственных выражений для повышения производительности или по другим причинам, я бы рекомендовал эти два ответа для начала.
person
user326608
schedule
08.12.2015
typed
будетobject
или неуниверсальнымIQueryable
, и никакие другие вещи не будут работать. Есть вещи, которые вы можете делать здесь, но почти все они будут уродливы, как и все остальное... здесь нет волшебной пули. В качестве примечания, вызовCast
будет<t>
, а не<IQueryable<t>>
(если вы простите неловкий псевдосинтаксис) - person Marc Gravell   schedule 11.01.2012