3

現在のリポジトリ メソッドの実装を改善することを考えてGetAllおり、以下のコードを書きました。Google でそのようなアプローチのアイデアを見つけようとしましたが、成功しませんでした。コードを確認して、以下のいくつかの質問に答えてください。簡単な例を次に示します。

class Service
{
    public void DoOperation()
    {
        // Let's say we need to retrieve users by criteria
        var items = UnitOfWork
            .Over<User>() // Get repository of User
            .GetAll()     // Start querying

            // If we need simple WHERE then use:
            .Where(x => x.Email == "some@email.com")

            // If we need more complex condition then use specification:
            .Using(new UserNameContains("John"))

            // Execute:
            .List();
    }
}

class UnitOfWork
{
    public Repository<T> Over<T>()
    {
        // Get from DI container or injected field
        return new Repository<T>();
    }
}

class Repository<T>
{
    public QueryWrapper<T> GetAll()
    {
        return new QueryWrapper<T>(Session.QueryOver<T>());
    }
}

class QueryWrapper<T>
{
    // Query from DB session. Init from constructor.
    private IQueryOver<T> _query;

    public QueryWrapper<T> Where(Expression<Func<T, bool>> expression)
    {
        _query = _query.Where(expression);
        return this;
    }

    public QueryWrapper<T> Using(Specification<T> spec)
    {
        var spec = new TSpec();
        _query = spec.Apply(_query);
        return this;
    }

    public IEnumerable<T> List()
    {
        return return _query.List();
    }
}

abstract class Specification<T>
{
    public abstract IQueryOver<T> Apply(IQueryOver<T> query);
}

class UserNameContains : Specification<User>
{
    // Init from constructor:
    private string _name;

    public override IQueryOver<User> Apply(IQueryOver<User> query)
    {
        return /* apply filter condition here */;
    }
}

その結果、次のようなメリットが得られます。

  1. カスタム Repos を作成する必要はもうありません。単一のジェネリックで十分です。
  2. カスタム仕様の実装がデータ層から分離され、テスト可能になりました。
  3. サービスから使いやすい。
  4. などQueryWrapperの追加のメソッドをサポートするために、常に拡張できます。OrderBy
  5. そして、それはまだ単体テスト可能です。

私のアプローチのリークを教えてください。または、問題のビジョンを教えてください。また、既存の記事へのリンクも素晴らしいでしょう。

4

1 に答える 1

1

私のアドバイスは、アーキテクチャを最小限に減らし、追加されたすべてのレイヤーと抽象化の利点を自分自身に正当化することです。最小の GetAll 実装は次のとおりです。

Session.Query<User>();

再利用される制限がある場合は、それらを拡張メソッドとして追加できます。再利用可能なコードが明らかになった場合、拡張メソッドにリファクタリングするのは簡単です。それでも、この例のように Lambda 式をラップするだけの単純なケースでは、リファクタリングの利点はほとんどありません。

public static IQueryable<User> UserNameContains(this IQueryable<User> queryable, string text)
{
    return queryable.Where(u => u.UserNameContains(text));
}

リポジトリ パターンはあまり有用ではありませんが、コードを整理するための合理的な方法になる可能性があります。しかし、クラスルールごとに 1 つのリポジトリが強制されるため、一般的なリポジトリ パターンは本当に嫌いです。リポジトリを使用する場合、ルート オブジェクト (Project、ProjectStatus など) に関連するクエリをグループ化するのが好きです。さらに、ジェネリック リポジトリで Get または GetAll メソッドを提供するメリットはありません。これらは ISession によって既に提供されているためです。

テスト容易性に関しては、クエリのテストは常に統合テストであり、ASP.NET MVC では、コントローラーまたは独立したクエリ メソッドを直接テストします。テスト用に Windows フォーム プロジェクトのリポジトリを使用しています。

于 2013-03-01T14:43:18.817 に答える