C# 返回 IQueryable<T> 或不返回 IQueryable<T>

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/718624/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 22:21:52  来源:igfitidea点击:

To return IQueryable<T> or not return IQueryable<T>

c#.netlinq-to-sqliqueryable

提问by CVertex

I have a repository class that wraps my LINQ to SQL Data Context. The repository class is a business line class that contains all the data tier logic (and caching and such).

我有一个包装我的 LINQ to SQL 数据上下文的存储库类。存储库类是包含所有数据层逻辑(以及缓存等)的业务线类。

Here's my v1 of my repo interface.

这是我的 repo 界面的 v1。

public interface ILocationRepository
{
    IList<Location> FindAll();
    IList<Location> FindForState(State state);
    IList<Location> FindForPostCode(string postCode);
}

But to handle paging for FindAll, I'm debating whether or not to expose IQueryable<ILocation> instead of IList to simplify the interface for circumstances such as paging.

但是为了处理 FindAll 的分页,我正在讨论是否公开 IQueryable<ILocation> 而不是 IList 以简化分页等情况的接口。

What are the pros and cons to exposing IQueryable from the data repo?

从数据存储库中公开 IQueryable 的利弊是什么?

Any help is very much appreciated.

很感谢任何形式的帮助。

采纳答案by Marc Gravell

The pros; composability:

优点;可组合性:

  • callers can add filters
  • callers can add paging
  • callers can add sorting
  • etc
  • 来电者可以添加过滤器
  • 呼叫者可以添加分页
  • 调用者可以添加排序
  • 等等

The cons; non-testability:

缺点; 不可测试性:

  • Your repository is no longer properly unit testable; you can't rely on a: it working, b: whatit does;
    • the caller could add a non-translatable function (i.e. no TSQL mapping; breaks at runtime)
    • the caller could add a filter/sort that makes it perform like a dog
  • Since callers expect IQueryable<T>to be composable, it rules out non-composable implementations - or it forces you to write your own query provider for them
  • it means you can't optimize / profile the DAL
  • 您的存储库不再可正确进行单元测试;你不能依赖 a:它工作,b:它做什么
    • 调用者可以添加不可翻译的函数(即没有 TSQL 映射;在运行时中断)
    • 调用者可以添加一个过滤器/排序,使其像狗一样执行
  • 由于调用者希望IQueryable<T>是可组合的,因此它排除了不可组合的实现 - 或者它迫使您为它们编写自己的查询提供程序
  • 这意味着您无法优化/分析 DAL

For stability, I've taken to notexposing IQueryable<T>or Expression<...>on my repositories. This means I know how the repository behaves, and my upper layers can use mocks without worrying "does the actual repository support this?" (forcing integration tests).

为了稳定性,我已经开始公开IQueryable<T>Expression<...>在我的存储库上。这意味着我知道存储库的行为方式,并且我的上层可以使用模拟而不必担心“实际存储库是否支持这个?” (强制集成测试)。

I still use IQueryable<T>etc insidethe repository - but not over the boundary. I posted some more thoughts on this theme here. It is just as easy to put paging parameters on the repository interface. You can even use extension methods (on the interface) to add optionalpaging parameters, so that the concrete classes only have 1 method to implement, but there may be 2 or 3 overloads available to the caller.

我仍然使用IQueryable<T>内部信息库-但不下来的边界。我在这里发布了关于这个主题的更多想法。将分页参数放在存储库界面上同样容易。你甚至可以使用扩展方法(在接口上)来添加可选的分页参数,这样具体的类只有 1 个方法要实现,但调用者可能有 2 或 3 个可用的重载。

回答by Konstantin Tarkus

I recommend using IEnumerableinstead of IList, with it you will have more flexibility.

我建议使用IEnumerable而不是IList,有了它你会有更多的灵活性。

This way you will be able to get from Db only that portion of data which you really gonna use without extra work done in your repository.

通过这种方式,您将能够从 Db 中仅获取您真正要使用的那部分数据,而无需在存储库中完成额外的工作。

Sample:

样本:

// Repository
public interface IRepository
{
    IEnumerable<Location> GetLocations();
}

// Controller
public ActionResult Locations(int? page)
{
    return View(repository.GetLocations().AsPagination(page ?? 1, 10);
}

Which is super clean and simple.

这是超级干净和简单。

回答by Akash Kava

As mentioned by previous answer, exposing IQueryable gives access to callers to play with IQueryable itself, which is or it can become dangerous.

正如前面的答案所提到的,公开 IQueryable 可以让调用者访问 IQueryable 本身,这可能会变得危险。

Encapsulating business logic's first responsibility is to maintain integrity of your database.

封装业务逻辑的首要职责是维护数据库的完整性。

You can continue exposing IList and may be change your parameters as following, this is how we are doing...

您可以继续公开 IList 并可能更改您的参数如下,这就是我们正在做的......

public interface ILocationRepository
{
    IList<Location> FindAll(int start, int size);
    IList<Location> FindForState(State state, int start, int size);
    IList<Location> FindForPostCode(string postCode, int start, int size);
}

if size == -1 then return all...

如果大小 == -1 然后返回所有...

Alternative way...

替代方式...

If you still want to return IQueryable, then you can return IQueryable of List inside your functions.. for example...

如果你仍然想返回 IQueryable,那么你可以在你的函数中返回 IQueryable 的 List ......例如......

public class MyRepository
{
    IQueryable<Location> FindAll()
    {
        List<Location> myLocations = ....;
        return myLocations.AsQueryable<Location>;
        // here Query can only be applied on this
        // subset, not directly to the database
    }
}

First method has an advantage over memory, because you will return less data instead of all.

第一种方法优于内存,因为您将返回较少的数据而不是全部。