5

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

3 に答える 3

1

LINQ-to-entities クエリで使用する場合、式をコンパイルして呼び出すことはできません。PredicateLINQ-to-entities は、EF LINQ プロバイダーによって評価され、SQL に変換される式ツリーを構築するため、直接使用してみてください。

このように仕様を使用する私見は意味がありません。LINQ-to-entities クエリは複合仕様です。したがって、Linq-to-entities を使用するか、仕様を使用して独自のクエリ言語を構築し、リポジトリでクエリを LINQ クエリに変換します。

于 2012-03-14T20:58:27.230 に答える
1

AsExpandable 拡張メソッドの使用を見てみましょう。

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

于 2012-03-21T08:27:14.480 に答える
0

IsSatisfiedBy() と IQueryable への拡張メソッドを作成するかもしれません。K. Scott Allen のアプローチは次のとおり です。

于 2012-03-23T02:02:22.917 に答える