Шаблон спецификации со структурой сущностей и использованием orderby и skip/take

Я выбрал проект, который использует шаблон спецификации, шаблон, который я не использовал раньше, и мне пришлось пойти и исследовать шаблон. Я заметил, что у него нет функций orderby и skip / take, и я не могу найти нигде, где показано, как реализовать это с шаблоном.

Я изо всех сил пытаюсь придумать, как лучше всего добавить это в шаблон спецификации. Но у меня возникли проблемы, такие как спецификация имеет дело с "Выражением>", тогда как я не думаю, что смогу хранить это вместе с Orderby's и т. Д.

В основном есть такой класс:

public class Specification<T> : ISpecification<T>
{
    public Expression<Func<T, bool>> Predicate { get; protected set; }

    public Specification(Expression<Func<T, bool>> predicate)
    {
        Predicate = predicate;
    }

    public Specification<T> And(Specification<T> specification)
    {
        return new Specification<T>(this.Predicate.And(specification.Predicate));
    }

    public Specification<T> And(Expression<Func<T, bool>> predicate)
    {
        return new Specification<T>(this.Predicate.And(predicate));
    }

    public Specification<T> Or(Specification<T> specification)
    {
        return new Specification<T>(this.Predicate.Or(specification.Predicate));
    }

    public Specification<T> Or(Expression<Func<T, bool>> predicate)
    {
        return new Specification<T>(this.Predicate.Or(predicate));
    }

    public T SatisfyingItemFrom(IQueryable<T> query)
    {
        return query.Where(Predicate).SingleOrDefault();
    }

    public IQueryable<T> SatisfyingItemsFrom(IQueryable<T> query)
    {
        return query.Where(Predicate);
    }
}

Это позволяет создать спецификацию, передавая предложение where. Это также позволяет связывать правила с "И", "Или". Например:

var spec = new Specification<Wave>(w => w.Id == "1").And(w => w.WaveStartSentOn > DateTime.Now);

Как я могу добавить метод для "OrderBy" и "Take"?

Поскольку это существующий код, я не могу вносить какие-либо изменения, которые могли бы повлиять на существующий код, и было бы неплохо реорганизовать его. Поэтому любое решение должно хорошо сочетаться с тем, что есть.

1 ответ

Решение

Как насчет

public class Specification<T> : ISpecification<T>
{
    public Expression<Func<T, bool>> Predicate { get; protected set; }
    public Func<IQueryable<T>, IOrderedQueryable<T>> Sort {get; protected set; }
    public Func<IQueryable<T>, IQueryable<T>> PostProcess {get; protected set;

    public Specification<T> OrderBy<TProperty>(Expression<Func<T, TProperty>> property)
    {
        var newSpecification = new Specification<T>(Predicate) { PostProcess = PostProcess } ;
        if(Sort != null) {
             newSpecification.Sort = items => Sort(items).ThenBy(property);
        } else {
             newSpecification.Sort = items => items.OrderBy(property);
        }
        return newSpecification;
    }

    public Specification<T> Take(int amount)
    {
        var newSpecification = new Specification<T>(Predicate) { Sort = Sort } ;
        if(PostProcess!= null) {
             newSpecification.PostProcess= items => PostProcess(items).Take(amount);
        } else {
             newSpecification.PostProcess= items => items.Take(amount);
        }
        return newSpecification;
    }

    public Specification<T> Skip(int amount)
    {
        var newSpecification = new Specification<T>(Predicate) { Sort = Sort } ;
        if(PostProcess!= null) {
             newSpecification.PostProcess= items => PostProcess(items).Skip(amount);
        } else {
             newSpecification.PostProcess= items => items.Skip(amount);
        }
        return newSpecification;
    }
}

СДЕЛАТЬ:

  • аналогичная конструкция для OrderByDescending
  • Обновите другие методы, чтобы значения "Сортировка" и "PostProcess" не терялись, например, при вызове "И"

тогда ваши методы удовлетворения становятся:

private IQueryable<T> Prepare(IQueryable<T> query) 
{
    var filtered = query.Where(Predicate);
    var sorted = Sort(filtered);
    var postProcessed = PostProcess(sorted);
    return postProcessed;
}

public T SatisfyingItemFrom(IQueryable<T> query)
{
    return Prepare(query).SingleOrDefault();
}

public IQueryable<T> SatisfyingItemsFrom(IQueryable<T> query)
{
    return Prepare(query);
}

TODO: убедитесь, что Sort & PostProcess не равны NULL в методе "Подготовить"

Использование:

var spec = new Specification<Wave>(w => w.Id == "1")
              .And(w => w.WaveStartSentOn > DateTime.Now)
              .OrderBy(w => w.WaveStartSentOn)
              .Skip(20)
              .Take(5);
Другие вопросы по тегам