2012-03-14 15 views
5

Me tropecé al intentar usar mi especificación dentro de una consulta LINQ. El problema aquí es con mi especificación con params.Especificación dentro de LINQ con EF 4.3

simular un escenario simple Vamos:

public class Car { 
    public Guid Id { get; set; } 
    public string Color { get; set; } 
    public int UsedPieces { get; set; } 
    // whatever properties 
} 

public class Piece { 
    public Guid Id { get; set; } 
    public string Color { get; set; } 
    // whatever properties 
} 

public static class PieceSpecifications : ISpecification<Piece> { 
    public static ISpecification<Piece> WithColor(string color) { 
     return new Specification<Piece>(p => p.Color == color); 
    } 
} 

lo que estoy realmente tratando de hacer

// Get accepts ISpecification and returns IQueryable<Car> to force just one call to database 
var carWithPieces = _carRepository.Get(CarSpecifications.UsedPiecesGreaterThan(10)); 

var piecesWithColor = from p in _pieceRepository.Get() 
         let car = carWithPieces.FirstOrDefault() // entire query will does one call to database 
         where PieceSpecifications.WithColor(car.Color).IsSatisfiedBy(p) // unfortunately it isn't possible 
        // where p.Color == car.Color -> it works, but it's not what I want 
         select p; 

Sé que es un poco confuso, pero estoy tratando de evitar una gran cantidad de ida y vuelta dentro de mi escenario real (grande) y sé que en realidad es imposible hacerlo usando LINQ sin procesar con el marco de la entidad. Estoy cansado de probar tantos blogs y enfoques fallidos (míos). Alguien sabe algo realmente bueno. Hay otra manera de hacer eso?

Error

System.NotSupportedException: LINQ to Entities no reconoce el método método 'Boolean IsSatisfiedBy (App.Model.Piece)', y este método no se puede traducir en una expresión tienda .

ACTUALIZACIÓN

especificación básica del patrón

public class Specification<T> : ISpecification<T> { 
    private readonly Expression<Func<T, bool>> _predicate; 

    public Specification(Expression<Func<T, bool>> predicate) { 
     _predicate = predicate; 
    } 

    public Expression<Func<T, bool>> Predicate { 
     get { return _predicate; } 
    } 

    public bool IsSatisfiedBy(T entity) { 
     return _predicate.Compile().Invoke(entity); 
    } 
} 

ACTUALIZACIÓN

Es bastante fácil ordenada si hago esto

// call to database 
var car = _carRepository 
    .Get(CarSpecifications.UsedPiecesGreaterThan(10)) 
    .FirstOrDefault(); 

// Whoah! look I'm working, but calling to database again. 
var piecesWithColor = _pieceRepository 
    .Get(PieceSpecifications.WithColor(car.Color)) 
    .ToArray(); 

Repositorio

// The Get function inside repository accepts ISpecification<T>. 
public IQueryable<T> Get(ISpecification<T> specification) { 
    return Set.Where(specification.Predicate); 
} 
+1

¿Podría explicar qué * IsSatisfiedBy (p) * hace? – Aducci

+0

Verifique mi publicación, actualicé con una implementación de especificación simple. –

+0

Tienes razón, simplemente no puedes hacerlo. Linq no es tan listo para interpretar tu código personalizado en t-sql. Esperemos .NET 4.5 y EF 5.0 donde con la compatibilidad enum (esta característica se conecta con la pregunta) podemos ver algunos nuevos azúcares de sintaxis. También soy interesante en eso. –

Respuesta

1

No se puede compilar e invocar la expresión si desea utilizarlo en la consulta LINQ a las entidades. Intente utilizar Predicate directamente porque LINQ-to-entities crea un árbol de expresiones que es evaluado por el proveedor EF LINQ y traducido a SQL.

En mi humilde opinión utilizando la especificación de esta manera no tiene sentido. La consulta LINQ-a-entidades es una especificación compuesta. Entonces, use Linq-to-entities o cree su propio lenguaje de consulta usando la especificación y deje que su repositorio traduzca su consulta a la consulta LINQ.

+1

Mi aplicación requiere una reutilización de predicados y es por eso que pensé que el patrón de especificación encajaría perfectamente.No me puedo imaginar cómo implementar algunos predicados reutilizables. –

+0

¿Qué ocurre si intentas usar el método 'Where' de extensión como este:' .Where (PieceSpecifications.WithColor (car.Color) .Predicate) 'en su lugar? –

+1

No puedo hacer eso porque 'car' está declarado dentro de la consulta. 'let car = carWithPieces.FirstOrDefault()'. Como dije, estoy tratando de evitar los viajes de ida y vuelta. –

1

Eche un vistazo al método de extensión AsExpandable.

http://www.albahari.com/nutshell/linqkit.aspx

+0

LINQK realmente extiende una gran parte de LINQ a Entidades, pero no puedo encontrar la manera de trabajar 'PieceSpecifications.WithColor (car.Color) .Predicate.Invoke (p)'. Desafortunadamente, parece que LINQKit no admite un método con params, como WithColor. Gracias de cualquier manera. –

Cuestiones relacionadas