Linq 我应该返回列表吗或 IEnumerable当我以后还可以做更多的事情时
我有一些返回 T 列表的方法,例如 GetAllEvents。在某些情况下,我需要按日期或项目上的某些其他属性过滤事件列表(或我的列表)。
我知道 LINQ 查询可以“链接”或具有 x 行数来进一步细化它们,并且直到您需要在非 linq 语句中实际使用它们时查询才会执行(如果我错了,请纠正我) 我的
问题是,如果我的 GetAllXXX 方法返回我得到的任何内容的列表,那么我在执行 LINQ 的 GetAllXXX 代码末尾使用的是 .ToList() 方法吗?我应该返回 IEnumerable 吗?如果仅适用于我需要在实际运行查询之前对“结果”做进一步操作的情况。
这是我担心的一个例子:我有 1000 个事件。 GetAllEvents 将检索所有 1000 个事件并给我一个列表。然后,根据用户所在的页面,可能仅显示今天、本周或特定类别的事件。理想情况下,当我向用户展示今天发生的 5 个事件时,我真的不想通过网络传递所有 1000 个事件,然后将其截断为他们真正想要的 5 个。是的,我知道此时都是服务器端,但如果它仍在为 1000 分配内存,我会尽力避免这种情况。
有什么指示或建议吗?
I have some methods that return a List of T like say GetAllEvents. In some cases I need to then filter that list of events (or whatever my List is) by date or some other property on the items.
I know that LINQ queries can be "chained" or have x number of lines that further refine them and the query will not execute until some point when you need to actually use them in a non-linq statement (please correct me if I am wrong on this beliefe.)
My question is, if my GetAllXXX method returns a List of whatever I am getting, is the .ToList() method I am using at the end of my GetAllXXX code executing the LINQ? Should I return IEnumerable instead? If only for those cases where I need to do something further to the "results" BEFORE the query is actually run.
Here is an example of my worry: I have say 1000 events. The GetAllEvents will retrieve all 1000 and give me a List of them. Then, depending on what page the user is on, may only show events for Today, this week or of a certain category. Ideally, by the time it gets to the point where I am showing the user the 5 events happening today, I don't really want to pass all 1000 across the wire and then truncate that down to the 5 they really want. Yes, I know it is all server side at this point, but if it is still allocating memory for the 1000 I am trying to avoid this.
Any pointers or suggestions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
返回一个
IEnumerable
。转换为
List
既快速又轻松,而且可以使接口与方法的实现和输出的使用分离。关于您的具体担忧 - 如果返回所有 1000 个事件并在客户端上处理它们的成本很高,那么您应该考虑在服务器上进行一些过滤。您仍然可以拥有返回所有事件的方法,但拥有返回最频繁查询的专门/优化版本。今天的事件就是一个很好的例子。
Return an
IEnumerable
.The conversion to a
List
is quick and painless, plus you keep you interface decoupled from both the implementation of your methods and the use of the output.Regarding your specific worry - if it is going to be expensive to return all 1000 events and process them on the client then you should consider doing some filtering on the server. You can still have a method that returns all the events, but have specialised/optimised versions that return the most frequent queries. The events for today would be a good example.
如果将序列转换为服务器上的列表,那么您将在服务器上使用时间和内存,然后通过网络传输整个内容,然后在客户端上使用更多时间和内存来过滤列表。
如果您只是返回序列,那么您就是通过创建一个不同的问题来“解决”您的问题。现在,当客户端过滤列表时,他们必须对服务器进行一千次小点击,而不是一次昂贵的点击。同样多的信息通过网络传输,并且由于每次点击的开销而花费的时间要长得多。
如果您想要在服务器上执行过滤,那么您可以 (1) 创建一个表示常见过滤器的自定义 API(简单),或者 (2) 返回 IQueryable,并实现 LINQ 提供程序。 (困难,但功能强大。)IQueryable 允许您在客户端构建查询,通过线路将查询发送到服务器,在服务器上运行查询,然后只提供客户端想要的结果。
我的同事 Matt Warren 写了一系列关于如何实现 IQueryable 的文章;我会从那开始。
If you turn the sequence into a list on the server then you are using time and memory on the server, and then transmitting the whole thing over the wire, and then using more time and memory on the client to filter the list.
If you just return the sequence then you are "solving" your problem by creating a different problem. Now when the client goes to filter the list they have to make a thousand small hits to the server instead of one expensive hit. Just as much information is going over the wire, and taking far longer with all the per-hit overhead.
If what you want to do is perform the filtering on the server then you can (1) create a custom API that represents common filters (easy), or (2) instead return IQueryable, and implement a LINQ provider. (hard, but powerful.) IQueryable lets you construct the query on the client side, send the query over the wire to the server, run the query on the server, and then only serve up the results the client wanted.
My colleague Matt Warren wrote a whole series of articles on how to implement IQueryable; I would start with that.
Eric Lippert 的答案很好,但请注意,如果您只想提供一些服务器端过滤(甚至是自定义过滤),则不需要实现整个 IQueryable。您可以通过仅实现您实际使用的 LINQ 函数来更简单地创建与 LINQ 兼容的 API。例如,考虑定义
仅公开返回类型为 IOneTripEnumerable 的 LINQ 兼容Where 方法。
IOneTripEnumerable.Where 的实现将返回一个新对象,该对象也实现 IOneTripEnumerable 并简单地将筛选器存储为数据成员。当 IOneTripEnumerable.GetEnumerator 被调用时,您可以打包过滤器并将它们发送到服务器,然后在一个往返中返回过滤结果。
(您还可以实现客户端缓存策略:如果您希望后续调用 GetEnumerator 返回与初始调用相同结果的枚举器,只需将结果存储在可枚举对象中。)
如果您有更多时间并看到如果需要,您可以通过添加额外的 LINQ 方法来进一步优化,但只需控制Where(以允许在服务器上进行过滤)和GetEnumerator(以在单次往返中获取所有结果)就可以给您带来非常好的结果以低成本实施。您不需要实现整个 IQueryable。 (请注意,Count、Any 和 Take 也是往返优化的非常好的候选者,并且实现起来很简单)。
Eric Lippert's answer is good, but note that you do not need to implement the whole of IQueryable if you just want to provide some server-side filtering (even custom filtering). You can create a LINQ-compatible API more simpy by implementing only the LINQ functions that you'll actually use. For example, consider defining
which exposes only a LINQ-compatible Where method with return type IOneTripEnumerable
The implementation of IOneTripEnumerable.Where would return a new object that also implements IOneTripEnumerable and simply stores the filter as a data member. When IOneTripEnumerable.GetEnumerator is called you can package up the filters and send them to the server then get back the filtered results in one round-trip.
(You could also implement a client-side caching policy: if you want subsequent calls to GetEnumerator to return an enumerator over the same results as the initial call, just store the results in the enumerable object.)
If you get more time and see the need, you can optimize further by adding additional LINQ methods, but just taking control of Where (to allow the filtering to occur on the server) and GetEnumerator (to get all the results in a single round-trip) could give you pretty good results at low cost to implement. You don't need to implement the whole of IQueryable. (Note that Count, Any, and Take are also very good candidates for round-trip optimization and are trivial to implement).