8

これまで使用したことのないパターンである仕様パターンを使用するプロジェクトを取り上げたので、そのパターンを調査する必要がありました。OrderByおよびSkip/Take機能がないことに気付きました。パターンでこれを実装する方法を示す場所が見つかりません。

これを仕様パターンにどのように追加するのが最善かを考えるのに苦労しています。しかし、仕様が " " を扱っているのExpression<Func<T, bool>>に対し、これを orderby などと一緒に保存できるとは思わないなど、ヒットの問題があります。

基本的に、次のようなクラスがあります。

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 句を渡すことができます。また、「And」、「Or」を使用してルールを連鎖させることもできます。例えば:

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

「OrderBy」と「Take」のメソッドを追加するにはどうすればよいですか?

これは既存のコードなので、既存のコードに影響を与えるような変更を行うことはできず、リファクタリングするのはかなりの作業になります。したがって、どのソリューションも、そこにあるものとうまく連携する必要があります。

4

1 に答える 1

8

どうですか

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

TODO:

  • OrderByDescending の同様の構造
  • たとえば、「And」を呼び出したときに「Sort」値と「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: 「Prepare」メソッドで 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);
于 2014-08-11T13:17:49.993 に答える