Спецификация внутри LINQ с EF 4.3 - PullRequest
5 голосов
/ 15 марта 2012

Я споткнулся, пытаясь использовать мою спецификацию в запросе LINQ.Проблема здесь заключается в моей спецификации с параметрами.

Давайте подделаем простой сценарий:

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;

Я знаюэто немного сбивает с толку, но я стараюсь избегать большого количества обходов в моем реальном (большом) сценарии, и я знаю, что на самом деле невозможно использовать raw 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 ]

1 голос
/ 21 марта 2012

Взгляните на использование метода расширения AsExpandable.

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

1 голос
/ 15 марта 2012

Вы не можете скомпилировать и вызвать выражение, если хотите использовать его в запросе LINQ-to-entity.Попробуйте использовать Predicate напрямую, потому что LINQ-to-entity строит дерево выражений, которое оценивается поставщиком EF LINQ и транслируется в SQL.

IMHO, использование спецификации таким способом не имеет смысла.Запрос LINQ-to-entity является составной спецификацией.Поэтому используйте Linq-to-entity или создайте свой собственный язык запросов, используя спецификацию, и пусть ваш репозиторий переведет ваш запрос в запрос LINQ.

0 голосов
/ 23 марта 2012

Может быть сделать IsSatisfiedBy () и метод расширения для IQueryable. Вот подход К. Скотта Аллена: http://odetocode.com/Blogs/scott/archive/2012/03/19/avoiding-notsupportedexception-with-iqueryable.aspx

...