リポジトリと仕様パターン
-
19-09-2019 - |
質問
現在、新しいプロジェクトを立ち上げているのですが、いくつかの点に遭遇したので、少し入力が必要です。
これが私が検討していることです:
汎用リポジトリが欲しい
リポジトリから IQueryable を返したくありません。
クエリを仕様にカプセル化したいと思います。
仕様パターンを実装しました
簡単にテストできる必要がある
ここで少し行き詰まってしまったのですが、私の質問は、1 つ以上の仕様を指定して find メソッドを呼び出す最もエレガントな方法はどれかということです。
(流暢): bannerRepository.Find().IsAvailableForFrontend().IsSmallMediaBanner()
または、私の仕様に従ってクエリをラムダとして表現します
(ラムダ): bannerRepository.Find.Where(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner)
それとも全く別の方法でしょうか?最も重要なことは、MVC フロントを実装する担当者がリポジトリを直感的に理解できる必要があるということです。
私が達成したいのは、仕様を組み合わせることができるという点である程度の柔軟性を維持し、IQueryableをコントローラーにリークすることなく、仕様で「フィルタリング」するエクスペリエンスを提供することですが、ISpecifiableに似たものであり、 Linq ではなく仕様を使用してクエリを変更します。しかし、この方法でクエリ ロジックをコントローラーにリークすることに戻っただけでしょうか?
解決
私は、仕様のプロパティを使用して、いくつかの流暢APIのを見てきたので、彼らはクライアントに括弧ノイズを追加しないでください。
bannerRepository.Find.IsAvailableForFrontend.IsSmallMediaBanner.Exec()
ビーイングExecの()レポに対する仕様を実行するための方法。
しかし、あなたがプロパティを使用しない場合でも、それは最小のノイズを持っているので、私は、流暢なAPIのために行くだろう。
他のヒント
(?メソッドが返すまたはおそらくいくつかの完全に他の方法はありますか?
.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句に仕様を翻訳することができ、訪問者を受け入れを持っている(それだけの問題です当然ののこのの場合、)。最後に、仕様「外部」(流れるようなインターフェイスを使用して)リポジトリインターフェースを構成することになる。
多分これは単なる素朴なアイデアですが、私はそれが非常に簡単見つけます。 この情報がお役に立てば幸いです。
個人的に私はラムダ道を行くだろう。それが原因で、ラムダのための私の愛であってもよいが、それは一般的なリポジトリのセットアップのために多くのスペース年代を提供しています。
以下の考慮ます:
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」を実装し、具体的なクラスは、抽象「リポジトリ」クラスを拡張し、「IBannerInterface」を実装するバナーテーブル/オブジェクトのためのインタフェースを作成することができます
interface IBannerRepository : IRepository<Banner>
{
}
そして、それを実現するために、一致するリポジトリます:
class BannerRepository : Repository<Banner>, IBannerRepository
{
}
それはあなたに多くの柔軟性を与えるだけでなく、あなたが持っているすべての小さなエンティティを制御するのに十分な電力として、私はこのアプローチを使用することをお勧めします。
これらのメソッドを呼び出すと、そのように超簡単になります:
BannerRepository _repo = new BannerRepository();
_repo.FindSingle(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner);
はい、それはあなたがいくつかの仕事をしなければならないが、地獄あなたが後でデータソースを変更するために簡単であることを意味します。
それが役に立てば幸い!