6

私は現在、新しいプロジェクトをセットアップしていますが、少し入力が必要ないくつかのことに遭遇しました。

これは私が考えているものです:

  • 汎用リポジトリが欲しい

  • リポジトリから IQueryable を返したくありません。

  • クエリを仕様にカプセル化したいと思います。

  • 仕様パターンを実装しました

  • 簡単にテストできる必要があります

ここで少し行き詰まります。私の質問は、find メソッドを 1 つ以上の仕様で呼び出す最もエレガントな方法はどれかということです。

(流暢):bannerRepository.Find().IsAvailableForFrontend().IsSmallMediaBanner()

または私の仕様でラムダとしてクエリを表現する

(ラムダ):bannerRepository.Find.Where(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner)

それともまったく別の方法ですか?最も重要なことは、MVC フロントを実装する担当者がリポジトリを直感的に操作できるようにすることです。

私が達成したいと思っているのは、仕様を組み合わせることができるという点である程度の柔軟性を維持し、仕様で「フィルタリング」する経験を与えることですが、IQueryable をコントローラーにリークすることはありませんが、ISpecifiable のように、 Linqではなく仕様でクエリを変更します。しかし、この方法でクエリ ロジックをコントローラに漏らしただけでしょうか?

4

3 に答える 3

2

仕様にプロパティを使用する Fluent API をいくつか見たことがあります。そのため、クライアントに括弧ノイズが追加されません。

bannerRepository.Find.IsAvailableForFrontend.IsSmallMediaBanner.Exec()

Exec() は、リポジトリに対して仕様を実行するためのメソッドです。

しかし、プロパティを使用しない場合でも、ノイズが最小限であるため、流暢な API を使用します。

于 2010-02-08T12:34:04.187 に答える
2

それともまったく別の方法ですか?

実際、私はあなたのリポジトリの実装を正確には把握していません (例えば、メソッドは何を.Find()返すでしょうか?) が、私は別の方向を選択します:

public class Foo 
{ 
    public Int32 Seed { get; set; }
}

public interface ISpecification<T> 
{
    bool IsSatisfiedBy(T item);
}

public interface IFooSpecification : ISpecification<Foo> 
{
    T Accept<T>(IFooSpecificationVisitor<T> visitor);
}

public class SeedGreaterThanSpecification : IFooSpecification
{
    public SeedGreaterThanSpecification(int threshold)
    {
        this.Threshold = threshold;
    }
    public Int32 Threshold { get; private set; }
    public bool IsSatisfiedBy(Foo item) 
    {
        return item.Seed > this.Threshold ;
    }
    public T Accept<T>(IFooSpecificationVisitor<T> visitor)
    {
        return visitor.Visit(this);
    }
}
public interface IFooSpecificationVisitor<T>
{
    T Visit(SeedGreaterThanSpecification acceptor);
    T Visit(SomeOtherKindOfSpecification acceptor);
    ...
}
public interface IFooRepository 
{
    IEnumerable<Foo> Select(IFooSpecification specification);
}
public interface ISqlFooSpecificationVisitor : IFooSpecificationVisitor<String> { }
public class SqlFooSpecificationVisitor : ISqlFooSpecificationVisitor
{
    public string Visit(SeedGreaterThanSpecification acceptor)
    {
        return "Seed > " + acceptor.Threshold.ToString();
    }
    ...
}
public class FooRepository
{   
    private ISqlFooSpecificationVisitor visitor;

    public FooRepository(ISqlFooSpecificationVisitor visitor)
    {
        this.visitor = visitor;
    }

    public IEnumerable<Foo> Select(IFooSpecification specification)
    {
        string sql = "SELECT * FROM Foo WHERE " + specification.Accept(this.visitor);
        return this.DoSelect(sql);
    }

    private IEnumerable<Foo> DoSelect(string sql)
    {
        //perform the actual selection;
    }
}

つまり、エンティティー、その仕様インターフェース、およびビジター・パターンに関与するいくつかの実装者があり、そのリポジトリー・インターフェースは仕様インターフェースを受け入れ、そのリポジトリー実装は、仕様を SQL 句に変換できるビジターを受け入れます (しかし、これは単にこのケースの問題です。もちろん)。最後に、リポジトリ インターフェイスの「外側」で仕様を作成します (流暢なインターフェイスを使用)。

これは単純な考えにすぎないかもしれませんが、非常に単純だと思います。お役に立てれば。

于 2013-09-04T16:41:32.257 に答える
1

個人的には、ラムダ方式を採用します。ラムダが大好きだからかもしれませんが、一般的なリポジトリのセットアップに多くのスペースを提供します。

次の点を考慮してください。

bannerRepository.Find.Where(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner)

あなたのパターンがどのように見えるかはわかりませんが、ここでいくつかのことをリファクタリングできます:

データ アクセス用のすべてのメソッドを含むタイプの「IRepository」と呼ばれる汎用インターフェイスを作成します。

次のようになります。

interface IRepository<T> where T : class
{
    IEnumerable<T> FindAll(Func<T, bool> exp);

    T FindSingle(Func<T, bool> exp);
}   

このインターフェースを実装する抽象「リポジトリ」クラスを作成します。

class Repository<T> : IRepository<T> where T : class
{
    TestDataContext _dataContext = TestDataContext();

    public IEnumerable<T> FindAll(Func<T, bool> exp)
    {
        _dataContext.GetTable<T>().Where<T>(exp);
    }

    public T FindSingle(Func<T, bool> exp)
    {
        _dataContext.GetTable<T>().Single(exp);
    }
}

これで、'IRepository' を実装するバナー テーブル/オブジェクトのインターフェイスと、抽象 'Repository' クラスを拡張して 'IBannerInterface' を実装する具象クラスを作成できます。

interface IBannerRepository : IRepository<Banner>
{
}

そして、それを実装するための一致するリポジトリ:

class BannerRepository : Repository<Banner>, IBannerRepository
{
}

このアプローチを使用することをお勧めします。これにより、多くの柔軟性と、所有するすべての小さなエンティティを制御するのに十分なパワーが得られるからです。

これらのメソッドの呼び出しは非常に簡単です。

BannerRepository _repo = new BannerRepository();

_repo.FindSingle(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner);

はい、それは何らかの作業を行う必要があることを意味しますが、後でデータ ソースを変更する方がはるかに簡単です。

それが役に立てば幸い!

于 2010-02-08T13:12:56.230 に答える