Para cualquier otra persona que desee proyectar valores no db desde una consulta db, proyecto this de u/Luis Aguilar fue muy, muy útil para mí.
Tenía una base de datos de legado muy grande (450 GB) que se debía servir a OData/WebAPI.
El requisito de OData significaba que no podía filtrar los datos de origen (mucho) antes de devolverlos al usuario. Podríamos aislarlo, pero aparte de eso, es su información para consultar como lo deseen.
Más importante aún, los datos heredados eran demasiado intrincados como para exponerlos tal como están, y se requería una lógica comercial significativa para cotejar los datos necesarios (Include
de propiedades de navegación/claves externas, predicados de cláusulas largas, etc.).
Esto significaba que la paginación y la limitación del resultado no estarían disponibles hasta después de que la consulta ya se haya materializado.
Los atajos normales para este tipo de cosas implican diversas estrategias que implican materialización/carga ansiosa. Sin embargo, debido al tamaño del conjunto de datos y la falta de filtrado, esto daría como resultado una gran cantidad de memoria de proceso y fallas de memoria insuficiente.
Por lo tanto, algunos códigos. Aquí está mi llamada de configuración, similar a lo que AutoMapper o OData requieren:
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);
}
}
Este diseño permite que siga las reglas de proyección en la clase DTO con el resto de la lógica de negocio. Esto es lo que se ve el código DTO-nivel como:
public static void ProjectionModel(ProjectionModelBuilder modelBuilder)
{
modelBuilder
.Projection<ClientDTO>()
.ForSource<Client>(configuration =>
{
configuration.Property(dto => dto.Name).ExtractFrom(entity => entity.Name);
// etc
});
}
Dónde Client
es mi entidad/Tipo de EDM, asignan a db mesa y un tropecientos claves externas.
A continuación, obtener un traducidos/proyecta Queryable
, esto es:
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();
Sólo los dos últimos líneas son relevantes, pero incluyen el resto de contexto.
Lo último que tuve que hacer fue establecer this.IsAutoConfigured = false;
en el constructor para ProjectionSourceTypeConfiguration.cs
en el código de Luis; esto me permitió ordenar mis definiciones de proyección manualmente para que las propiedades de navegación dentro de las clases principales configuraran sus proyecciones con éxito.
No puedo agradecer a https://stackoverflow.com/users/543712/luis-aguilar suficiente para su trabajo. Después de escribir mi propio Proveedor de LINQ/ExpressionVisitor
con varias invocaciones de métodos genéricos, traducciones y caminatas de árbol para tener aún varios problemas, su proyecto fue un regalo del cielo.
Si usted encuentra que tiene que tubería de su propio procesamiento de expresión para el rendimiento u otros motivos, se lo recomiendo thesetwo respuestas para empezar.
Básicamente, no puede hacer eso * convenientemente * - tendría que usar la reflexión, y luego 'tipeado' va a ser' objeto' o 'IQueryable' no genérico, y ninguna de las otras cosas trabajo. Hay cosas que puedes hacer aquí, pero casi todas serán feas como cualquier cosa ... aquí no hay una bala mágica. Como nota al margen, la llamada 'Cast' sería' ', no a' > '(si perdonas la pseudo sintaxis incómoda) –
¿Qué estás tratando de hacer exactamente ?: lanzar el resultado de una consulta en un tipo que solo conoces en tiempo de ejecución, o extiendes la consulta para expresar una operación de conversión a un tipo específico que solo conoces en tiempo de ejecución? –
* ¿Por qué * quieres hacer esto? ¿Cómo es que dices que conoces el tipo solo en tiempo de ejecución, pero luego lo usas como si lo conocieras en tiempo de compilación? – svick