目前我正在使用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
}
您可以看到,返回List<User>
会带来性能问题,因为查询的执行时间早于预期。现在我需要将其更改为IQueryable<T>
:
//Repository<User> class
public TableQuery<User> List(where, orderby, topN parameters and etc)
{
//query and return
}
TableQuery<T>
是sqlite驱动程序的一部分,它几乎等于EF中的IQueryable<T>
,它提供查询并且不会立即执行。但现在的问题是:在UserManager.cs
中,它不知道什么是TableQuery<T>
,我需要在业务层项目中添加新的引用和导入名称空间,如using SQLite.Query
。它真的带来了糟糕的代码感觉。为什么我的业务层应该知道数据库的细节?为什么业务层应该知道什么是SQLite?那么正确的设计是什么?
答案 0 :(得分: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);
}
}
答案 1 :(得分:2)
我建议您使用IEnumerable<T>
而不是IQueryable<T>
,这也允许延迟加载。但是,IEnumerable
并不意味着您可以用任何方式查询数据。您的数据库LINQ提供程序可能具有缩减的功能集。
答案 2 :(得分:1)
由于TableQuery<T>
实现IEnumerable<T>
而不是IQueryable<T>
,最好的解决方案是简单地更改存储库界面以返回IEnumerable<T>
而不是TableQuery<T>
。这不仅打破了与SqlLite库的显式客户端依赖关系,而且使用抽象(IEnumerable<T>
)而不是实现也是一种更好的设计(您的界面中TableQuery<T>
)。
您的示例方法应如下所示:
//Repository<User> class
public IEnumerable<User> List(where, orderby, topN parameters and etc)
{
//query and return
}
答案 3 :(得分:0)
延迟加载可以是PITA,我宁愿尝试将加载策略注入存储库,而不是试图在我的应用程序或业务层中摆弄查询对象。特别是当查询对象强迫我紧密耦合图层时。