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