2012-08-28 16 views
17

Tengo un LINQ al modelo de entidades con herencia Table Per Hierarchy. Tengo una consulta sobre el tipo base, y quiero hacer una lógica específica dependiente del tipo. Por ejemplo:Transmitiendo a un tipo derivado en una consulta de LINQ a Entidades con herencia Tabla por jerarquía

IQueryable<BaseType> base = ... 

// this works fine 
var result = base.Select(b => b is DerivedType1 ? 1 : 2).ToList(); 

// this doesn't compile to SQL 
var result2 = base.Select(b => b is DerivedType1 ? ((DerivedType1)b).DerivedProperty : null).ToList(); 

¿Hay alguna manera de hacer algo como esto sin procesar IQueryables de cada tipo derivado por separado:

// I'd rather not do this: 
var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty); 
var resultB = base.OfType<DerivedType2>().Select(d => default(int?)); 
var result = resultA.Concat(resultB).ToList(); 
+1

Concat y Join parecen las únicas opciones. –

+1

¿Podría ser el problema que su propiedad DerivedProperty no se pueda anular? Entonces, ¿podría confundirse la consulta en cuanto a si la columna permitía valores nulos? –

Respuesta

21

colada directa a un tipo de entidad como (DerivedType1)b no es compatible con LINQ a Entidades pero el operador as (b as DerivedType1) es, por lo tanto, usted podría intentar:

var result2 = base 
    .Select(b => b is DerivedType1 
     ? (b as DerivedType1).DerivedProperty 
     : null) 
    .ToList(); 
+0

Esto parece funcionar, aunque recuerdo haber intentado esto antes de publicarlo, ¿entonces tal vez solo funciona en EF5 +? – ChaseMedallion

+2

@ChaseMedallion: Posiblemente, sí. Creo que nunca he usado esto antes de EF 5, pero no estoy seguro. Por cierto, mientras tanto descubrí que si 'DerivedProperty' es anulable, el operador ternario no es necesario, puedes usar:' Select (b => (b como DerivedType1) .DerivedProperty) '. Si 'b' no es del tipo' DerivedType1' EF no arrojará una excepción sino que simplemente devolverá 'null' automáticamente. – Slauma

+0

Derecha. EF no se fusiona a menos que esté sacando un nulo en la memoria como un tipo que no admite nulos. – ChaseMedallion

0

usted podría tener un método del tipo de base que se reemplaza en sus tipos derivados para proporcionar el valor de propiedad relevante.

public class MyBaseClass 
{ 
    public virtual int GetSomething() 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class MyDerivedClass1 : MyBaseClass 
{ 
    public int SomeProperty { get; set; } 

    public override int GetSomething() 
    { 
     return this.SomeProperty; 
    } 
} 

public class MyDerivedClass2 : MyBaseClass 
{ 
    public int SomeOtherProperty { get; set; } 

    public override int GetSomething() 
    { 
     return this.SomeOtherProperty; 
    } 
} 

Posteriormente, se podría:

var result = base.Select(b => b.GetSomething()).ToList(); 
+1

Esto no funcionará con LINQ to Entities - se requerirá .ToList() primero para traer entidades a la memoria, lo cual, dado que solo una propiedad por entidad es requerida por la consulta, es altamente ineficiente. –

0

Prueba de esto, nunca he hecho nada con la necesidad de hacer este tipo de esto, pero esto debería hacerlo. Además, si usa base, antes que nada, no porque sea una palabra clave, pero si debe hacerlo, use @base el @ delante del nombre indica que no se usa como palabra clave.

var resultA = base.Select(aVar => 
          (aVar is DerivedType1) ? 
           (DerivedType)(((DerivedType1)aVar).DerivedProperty) 
           : 
           (DerivedType)(default(int?)) 
         ).ToList(); 
+0

¿Querías responder a otra pregunta? Esta no es una respuesta a mi pregunta ... – ChaseMedallion

0
OfType<DerivedType1>() 

volverá un IEnumerable, si es posible, trate de cambiar de tipo de base de IEnumerable en lugar de IQueryable, es posible que en algunas restricciones en SQL cuando se utiliza IQueryable.

Eso es por supuesto si no está realmente buscando una base de datos?

0

Usted puede utilizar a EntityFramework.Extended mejorar el rendimiento de la consulta en lugar de hacer 2 viajes de ida y vuelta a DB.

var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty).Future(); 
var resultB = base.OfType<DerivedType2>().Select(d => default(int?)).Future(); 
var result = resultA.Concat(resultB).ToList(); 

En este caso, solo se ejecuta un viaje de ida y vuelta a bd. Este marco es muy útil para muchas otras cosas. EF

Cuestiones relacionadas