Шаблон спецификации со структурой сущностей и использованием 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);