6

現在、sqlite を使用して Windows アプリケーションを構築しています。データベースには というテーブルがUserあり、私のコードには aRepository<User>と aがありUserManagerます。よくあるデザインだと思います。リポジトリにはListメソッドがあります:

//Repository<User> class
public List<User> List(where, orderby, topN parameters and etc)
{
    //query and return
}

で複雑なことをしたい場合、これは問題を引き起こしますUserManager.cs:

//UserManager.cs
public List<User> ListUsersWithBankAccounts()
{
    var userRep = new UserRepository();
    var bankRep = new BankAccountRepository();
    var result = //do something complex, say "I want the users live in NY 
                 //and have at least two bank accounts in the system
}

List<User>クエリが予想よりも早く実行されるため、返すとパフォーマンスの問題が発生することがわかります。今、私はそれを次のようなものに変更する必要がありますIQueryable<T>:

//Repository<User> class
public TableQuery<User> List(where, orderby, topN parameters and etc)
{
    //query and return
}

TableQuery<T>IQueryable<T>クエリを提供し、すぐには実行しない EFとほぼ同じ sqlite ドライバーの一部です。しかし、問題は次のとおりです。 ではUserManager.cs、 が何であるかがわからないTableQuery<T>ため、新しい参照を追加using SQLite.Queryし、ビジネス レイヤー プロジェクトのように名前空間をインポートする必要があります。それは本当に悪いコード感をもたらします。ビジネス層がデータベースの詳細を知る必要があるのはなぜですか? ビジネス層が SQLite とは何かを知る必要があるのはなぜですか? では正しいデザインとは?

4

4 に答える 4

2

通常、クリーンなアーキテクチャでは、データ クエリ ロジックはリポジトリにカプセル化されます。パイプとフィルターを使用すると、クエリ ロジックを再利用できます。これらをデータレイヤー/リポジトリのメソッドでラップすると、読みやすく、保守しやすく、再利用しやすくなります。

たとえば、ユーザーをクエリするためのパイプとフィルター:

/// Pipes/Filters for user queries.
public static class UserExtensions
{
    public static IQueryable<User> Active(this IQueryable<User> query)
    {
        return query.Where(user => user.Active == true);
    }
}

public class UserRepository : IRepository<User>, IUserRepository
{
    /// Retrieve all users
    public List<User> List()
    {
        // Logic to query all users in the database.
    }
    public List<User> ListActive()
    {
        // Logic to query all active users in the database.
        return context.Users.Active().ToList();
    }
}

複雑なクエリでは、クエリ ロジックをそのリポジトリに抽象化するために、その目的と責任を理解する必要があります。たとえば、「このユーザーに属するすべてのアカウントを取得する」は、AccountRepositoryクラスで次のように記述できますList<Account> ListForUser(int userId) { }

編集:コメントに基づいて、少なくとも 2 つのアカウントを持つ LA 在住のユーザーを取得する検索クエリを作成するシナリオを次に示します。

public class UserRepository : IRepository<User>, IUserRepository
{
    // other queries.

    public List<User> List(ISearchQuery query)
    {
        // Logic to query all active users in the database.
        return context.Users.Active().LivesIn(query.Country).WithAccounts(query.AccountsAtLeast).ToList();
    }
}

public static class UserExtensions
{
    // other pipes and filters.

    public static IQueryable<User> LivesIn(this IQueryable<User> query, string country)
    {
        return query.Where(user => user.Country.Name == country);
    }
    public static IQueryable<User> WithAccounts(this IQueryable<User> query, int count)
    {
        return query.Where(user => user.Accounts.Count() >= count);
    }
}
于 2012-09-04T09:13:41.117 に答える
2

IEnumerable<T>ではなくを使用することをお勧めしますIQueryable<T>。これにより、遅延読み込みも可能になります。ただし、何らかのIEnumerable方法でデータをクエリできることを意味するものではありません。お使いの DB LINQ プロバイダーの機能セットはおそらく削減されています。

于 2012-09-04T10:14:42.737 に答える
1

は ではなくをTableQuery<T>実装しているため、最善の解決策は、単にリポジトリ インターフェイスをではなく を返すように変更することです。これは、SqlLite ライブラリとの明示的なクライアント依存関係を壊すだけでなく、インターフェイスで実装( ) の代わりに抽象化( ) を使用することをより適切に設計します。 IEnumerable<T>IQueryable<T>IEnumerable<T>TableQuery<T>IEnumerable<T>TableQuery<T>

メソッドの例は次のようになります。

//Repository<User> class
public IEnumerable<User> List(where, orderby, topN parameters and etc)
{
    //query and return
}
于 2012-09-04T15:36:28.640 に答える
0

遅延読み込みは PITA になる可能性があり、アプリケーションまたはビジネス レイヤーでクエリ オブジェクトを操作するのではなく、読み込み戦略をリポジトリに挿入しようとします。特に、クエリ オブジェクトによってレイヤーを密結合する必要がある場合はなおさらです。

于 2012-09-04T08:25:38.177 に答える