使用EF 4.3在LINQ内部规范

时间:2012-03-14 20:27:46

标签: c# linq entity-framework-4 specification-pattern

我偶然发现在LINQ查询中使用我的规范。麻烦在于我的规范与params。

让我们假设一个简单的场景:

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);
    }
}

我真正想做的事情

// 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;

我知道这有点令人困惑,但我试图避免在我的真实(大)场景中进行大量的往返,我知道实际上使用原始LINQ和实体框架是不可能的。我厌倦了尝试这么多博客和失败(我的)方法。 有人知道一些真正好的方法。还有另一种方法吗?

错误

  

System.NotSupportedException:LINQ to Entities无法识别   方法'Boolean IsSatisfiedBy(App.Model.Piece)'方法,这个   方法无法转换为商店表达式。

更新

基本规格模式

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);
    }
}

更新

如果我这样做,这很容易整洁

// 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();

存储库

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

3 个答案:

答案 0 :(得分:1)

如果要在LINQ-to-entities查询中使用它,则无法编译和调用表达式。尝试直接使用Predicate,因为LINQ-to-entities构建表达式树,由EF LINQ提供程序评估并转换为SQL。

IMHO使用规范这种方式没有意义。 LINQ到实体查询是一个复合规范。因此,要么使用Linq-to-entities,要么使用规范构建自己的查询语言,并让您的存储库将您的查询转换为LINQ查询。

答案 1 :(得分:1)

看一下使用AsExpandable扩展方法。

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

答案 2 :(得分:0)

也许将IsSatisfiedBy()和扩展方法设为IQueryable。这是K. Scott Allen的方法:  http://odetocode.com/Blogs/scott/archive/2012/03/19/avoiding-notsupportedexception-with-iqueryable.aspx