6

目前我正在使用 sqlite 构建一个 Windows 应用程序。在数据库中有一个表说User,在我的代码中有一个Repository<User>和一个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
}

可以看到,returnList<User>带来了性能问题,因为查询执行得比预期的要早。现在我需要将其更改为类似IQueryable<T>

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

TableQuery<T>是sqlite驱动的一部分,几乎等同IQueryable<T>于EF中的,提供查询,不会立即执行。但现在的问题是:在 中UserManager.cs,它不知道什么是 a 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 个帐户的用户。

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>实现IEnumerable<T>而不是实现,IQueryable<T>最好的解决方案是简单地将存储库接口更改为 returnIEnumerable<T>而不是TableQuery<T>. 这不仅打破了 SqlLite 库的显式客户端依赖关系,而且在接口中使用抽象( 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 回答