I stumbled trying to use my specification inside a LINQ query. The trouble here is with my specification with params.
Let's fake a simple scenario:
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);
}
}
what I'm actually trying to do
// 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;
I know it's a little bit confusing, but I'm trying to avoid a lot of roundtrips inside my real(big) scenario and I know that actually it's impossible to do using raw LINQ with entity framework. I'm tired to try so many blogs and failed(mine) approaches. Someone knows some real good approach. There's another way to do that?
Error
System.NotSupportedException: LINQ to Entities does not recognize the method 'Boolean IsSatisfiedBy(App.Model.Piece)' method, and this method cannot be translated into a store expression.
UPDATE
Basic Specification Pattern
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);
}
}
UPDATE
It's pretty easy neat if I do this
// 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();
Repository
// The Get function inside repository accepts ISpecification<T>.
public IQueryable<T> Get(ISpecification<T> specification) {
return Set.Where(specification.Predicate);
}
You can't compile and invoke expression if you want to use it in LINQ-to-entities query. Try to use Predicate
directly because LINQ-to-entities builds expression tree which is evaluated by EF LINQ provider and translated to SQL.
IMHO using specification this way doesn't make sense. LINQ-to-entities query is a composite specification. So either use Linq-to-entities or build your own query language using specification and let your repository translate your query to LINQ query.
Take a look at using AsExpandable extension method.
http://www.albahari.com/nutshell/linqkit.aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With