C# 返回可查询<;T>;或列表<;T>;在存储库中<;T>;

C# 返回可查询<;T>;或列表<;T>;在存储库中<;T>;,c#,.net,architecture,data-access-layer,business-layer,C#,.net,Architecture,Data Access Layer,Business Layer,目前,我正在使用sqlite构建一个windows应用程序。在数据库中有一个表,上面写着用户,在我的代码中有一个存储库和一个用户管理器。我认为这是一个非常普通的设计。在存储库中有一个列表方法: //Repository<User> class public List<User> List(where, orderby, topN parameters and etc) { //query and return } 您可以看到,返回List会带来性能问题,因为查询

目前,我正在使用sqlite构建一个windows应用程序。在数据库中有一个表,上面写着
用户
,在我的代码中有一个
存储库
和一个
用户管理器
。我认为这是一个非常普通的设计。在存储库中有一个
列表
方法:

//Repository<User> class
public List<User> List(where, orderby, topN parameters and etc)
{
    //query and return
}
您可以看到,返回
List
会带来性能问题,因为查询的执行时间比预期的要早。现在我需要将其更改为类似于
IQueryable

//存储库类
公共表查询列表(where、orderby、topN参数等)
{
//查询和返回
}

TableQuery
是sqlite驱动程序的一部分,它几乎等于EF中的
IQueryable
,它提供了一个查询,不会立即执行。但现在的问题是:在
UserManager.cs
中,它不知道什么是
TableQuery
,我需要在业务层项目中添加新的引用并使用SQLite.Query导入名称空间,如
。它确实带来了不好的代码感觉。为什么我的业务层应该知道数据库的详细信息?为什么业务层应该知道什么是SQLite?那么正确的设计是什么?

延迟加载可能是一种PITA,我宁愿尝试将加载策略注入存储库,而不是尝试在应用程序或业务层中处理查询对象。尤其是当查询对象迫使我将层紧密耦合时。

通常,在干净的体系结构中,数据查询逻辑封装在存储库中。使用管道和过滤器可以帮助重用查询逻辑。在数据层/存储库中用方法包装这些方法将更具可读性、可维护性和可重用性

例如,用于查询用户的管道和过滤器:

/// 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();
    }
}

我建议您使用
IEnumerable
而不是
IQueryable
,这也允许延迟加载<但是,code>IEnumerable
并不意味着您可以以任何方式查询数据。您的DB LINQ提供程序可能会减少功能集。

因为
TableQuery
实现了
IEnumerable
,而不是
IQueryable
,所以最好的解决方案是简单地将存储库接口更改为返回
IEnumerable
,而不是
TableQuery
。这不仅打破了SqlLite库对客户端的显式依赖,而且在界面中使用抽象(
IEnumerable
)而不是实现(
TableQuery
)也是一种更好的设计

您的示例方法应如下所示:

//Repository<User> class
public IEnumerable<User> List(where, orderby, topN parameters and etc)
{
    //query and return
}
//存储库类
公共IEnumerable列表(where、orderby、topN参数等)
{
//查询和返回
}

您能否“仅仅”在业务层和数据访问层之间添加一个新的抽象层(接口)?是的,这需要一组表示业务层中相同数据的新对象,以及这些对象与DA层中的对象之间的映射(某种意义上说,是代码重复),但如果我正确理解您的情况,这应该会让您在层与层之间有一个更清晰的划分。我同意@Kjartan,您真正想要的是IQueryable,遗憾的是SQLite没有一个,值得创建另一个层来转换为正确的类型。@Kjartan@ivenxu:我没有说
TableQuery
不正确,也没有说
IQueryable
正确。即使我添加了一个新的传输接口,比如
IQuery
,它仍然基于
TableQuery
,当我在业务层中使用
IQuery
时,一切都是一样的。它看起来与SQLite无关,但事实上,它戴着一个面具。我不完全清楚你所说的“基地”是什么意思。它们的外观和工作方式可能相同,但如果您明智地按接口进行分离,那么业务层就不应该明确依赖SQLite。它应该只能够对DA层进行调用
GetTheStuffIWant()
,而不必知道它的实现(或者我遗漏了什么?)。另一方面,@DennisTraub建议在存储库中使用一些加载策略,或者使用某种缓存,如果你不一定每次都需要调用DB的话,这可能是正确的。我认为最好的解决办法是改进sqlite net,使TableQuery实现IQueryable,这样你就可以使用它了。但也可能有一个原因,他们略读了,只实现了IEnumerable。你所说的“加载策略”是什么意思?你将把需求的逻辑放在哪里:“查询居住在纽约且系统中至少有两个银行帐户的用户”。这些查询写在存储库中,Ofcz可以相应地重构。是的,也有一个限制,因为不是所有的查询场景都可以通过EF中的单个sql查询进行解析。在这种情况下,我们将不得不提出替代办法。
/// 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();
    }
}
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);
    }
}
//Repository<User> class
public IEnumerable<User> List(where, orderby, topN parameters and etc)
{
    //query and return
}