Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specification inside LINQ with EF 4.3

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);
}
like image 882
Kim Tranjan Avatar asked Nov 04 '22 03:11

Kim Tranjan


2 Answers

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.

like image 169
Ladislav Mrnka Avatar answered Nov 09 '22 16:11

Ladislav Mrnka


Take a look at using AsExpandable extension method.

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

like image 25
kerem Avatar answered Nov 09 '22 15:11

kerem