質問

ここで本当にシンプルなリポジトリとサービスレイヤーパターンを作成しようとしています。 (.NET 4、C#、LINQ、この質問は部分的に言語に依存していません)。注:これは単なるR&Dです。

私の目標は、サービスレイヤーのメソッド定義の量を最小限に抑えることです。

これが私のリポジトリ契約です:

interface IFooRepository
{
   IEnumerable<Foo> Find();
   void Insert(Foo foo);
   void Update(Foo foo);
   void Delete(Foo foo);
}

そこには新しいものはありません。

さて、これが私のサービス契約で持っていようとしているものです:

interface IFooDataService
{
   public IEnumerable<Foo> Find(FooSearchArgs searchArgs);
}

基本的に、特定の「Foo」には多くのプロパティ(ID、名前など)があり、検索できるようにしたいと思います。

だから、私は異なるプロパティごとに1倍の検索方法を持ちたくない、私はただそれを望んでいる - そうすれば、私は契約を変更する必要はない追加のプロパティを作成するとき。

「FoosearchArgs」は、すべての異なる「Foo」プロパティを備えた単純なPocoです。

だから、それが私がやろうとしていることです、ここに私の質問があります:

  • これは貧弱なデザインですか?もしそうなら、代替手段は何ですか?
  • このフィルタリングをサービスレイヤーに実装するにはどうすればよいですか? 「FoosearchArgs」のどのプロパティが設定されているかを確認してから、フィルタリングを続けなければなりませんか? (もしそうなら、Query。どこで、これ、Query.whereなど)誰かがこれを行うための巧妙なlinq ienumerable拡張方法のアイデアを持っていますか? (すなわち repository.WhereMeetsSearchCriteria(fooSearchArgs))

助けに感謝します。

役に立ちましたか?

解決

非常によく似たものを使用しています。あなたが決定する必要があることの1つは、リポジトリの外部でiQueryableを公開するかどうかです。 FINDメソッドは、WHIN句から返されるiQueryableである可能性があるIENUMERABLEを返します。

iQueryableを返すことの利点は、リポジトリレイヤーの外側で基準をさらに改善できることです。

repository.Find(predicate).Where(x => x.SomeValue == 1);

式は、返されたデータを使用するようになったときにのみコンパイルされ、ここでは不利な点があります。実際に結果を使用するようになったときにのみデータベースにヒットするため、セッション(nhibernate)または接続が閉じられた後にデータベースを呼び出そうとする可能性があります。

私の個人的な好みは、検索方法を渡す仕様パターンを使用することです。

public interface ISpecification<TCandidate>
{
    IQueryable<TCandidate> GetSatisfyingElements(IQueryable<TCandidate> source);
}

public class TestSpecification : ISpecification<TestEntity>
{
    public IQueryable<TestEntity> GetSatisfyingElements(IQueryable<TestEntity> source)
    {
        return source.Where(x => x.SomeValue == 2);
    }
}

public class ActiveRecordFooRepository: IFooRepository
{
    ...

    public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    {
        ...

        return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).ToArray();

        ...
    }

    public TEntity FindFirst<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    {
        return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).First();
    }
}

クエリが実行された後、リポジトリコールは、仕様から返されたiQueryableが返され、クエリがそこに評価され、その後に評価されます。これはiQueryableを公開するよりも柔軟性が低いように見えるかもしれませんが、いくつかの利点があります。

  1. クエリはすぐに実行され、セッションが終了した後に行われているデータベースへの呼び出しを防ぎます。
  2. クエリが仕様にバンドルされるため、単体テスト可能になりました。
  3. 仕様は再利用可能です。つまり、同様のクエリを実行しようとする場合、コードの重複がないことを意味し、クエリ内のバグは1つの場所で修正する必要があります。
  4. 適切な種類の実装を使用すると、仕様を一緒にチェーンすることもできます。

repository.Find(
    firstSpecification
        .And(secondSpecification)
        .Or(thirdSpecification)
        .OrderBy(orderBySpecification));

他のヒント

渡しています func FoosearchArgsの代わりに、サービスレイヤーの検索方法のパラメーターとして、オプションですか? Enumerablesには、funcをパラメーターとして使用する方法(linq)があるため、結果をフィルタリングするために使用できます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top