2012-01-11 33 views
7

Estamos tratando de echar una instancia de IQueryable<EntityObject> a un IQueryable<SpecificEntityObject>, el tipo SpecificEntityObject solamente se conoce en tiempo de ejecución.moldeada IQueryable <EntityObject> a IQueryable <Specific>

Hemos intentado utilizar el siguiente código, que no se compila porque El tipo o espacio de nombres 'objType' no existe.

var t = query.ElementType; 
Type objType = typeof(IQueryable<>).MakeGenericType(t); 
var typed = query.Cast<IEnumerable<objType>>(); 


var grouped = typed.GroupByMany(groupBy.Select(grp => grp.Expression).ToArray()); 

¿Alguna idea?

+2

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) –

+2

¿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? –

+0

* ¿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

Respuesta

5

Uso siguiente método genérico extensión IQueryable query.ToDTO<sourceType,DestType>();:

public static class QueryableExtensions 
{ 
    public static IQueryable<TDest> ToDTO<TSource, TDest>(this IQueryable<TSource> source) 
    { 
     List<TDest> destinationList = new List<TDest>(); 
     List<TSource> sourceList = source.ToList<TSource>(); 

     var sourceType = typeof(TSource); 
     var destType = typeof(TDest); 
     foreach (TSource sourceElement in sourceList) 
     { 
      TDest destElement = Activator.CreateInstance<TDest>(); 
      //Get all properties from the object 
      PropertyInfo[] sourceProperties = typeof(TSource).GetProperties(); 
      foreach (PropertyInfo sourceProperty in sourceProperties) 
      { 
       //and assign value to each propery according to property name. 
       PropertyInfo destProperty = destType.GetProperty(sourceProperty.Name); 
       destProperty.SetValue(destElement, sourceProperty.GetValue(sourceElement, null), null); 
       destinationList.Add(destElement); 
      } 
     } 

     return destinationList.AsQueryable(); 
    } 
} 
+0

Bonito código y lo uso en mi proyecto. Un pequeño aviso: destinationList.Add (destElement); debe estar fuera de los corchetes. –

0

Si comenzó a usar el reflejo, debe usarlo también con todos los métodos. Por lo tanto, debe crear

var myEnumType = typeof(IEnumerable<>).MakeGenericType(objType); 

y también encontrar el método de extensión Emparejar el tipo necesario también en el tiempo de ejecución.

myEnumType.GetMethod("Cast", BindingFlags.Public | 
       BindingFlags.Static, 
       null, 
       CallingConventions.Any, 
       new Type[] {typeof(object)}, 
       null); 

, entonces sería capaz de llamar a ese método

0
var t = query.ElementType; 
Type objType = typeof(IQueryable<>).MakeGenericType(t); 
var typed = query.Cast<object>(); 


var grouped = typed.GroupByMany(groupBy.Select(grp => grp.Expression).ToArray()); 
+0

Hola, bienvenido a SO. ¿Podría dar alguna explicación para su solución? – GraphicsMuncher

1

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.

Cuestiones relacionadas