IEnumerable<T> as return value, deferred execution and N-tier applications
Asked Answered
S

3

6

First, I do not consider this question be the dup of these SO questions:

Should I always return IEnumerable<T> instead of IList<T>? and IEnumerable<T> as return type

As we all know ont of the main purposes of introducing several tiers is to decrease coupling.
We must define some interface for data access and our BL should not care about the details of DAL implementation. If mentioned interface returnes IEnumerable<T> BL does not know whether it is just a static IEnumerable or something that has deferred execution. At the same time this particular detail can affect perfromance considerably and requires different coding depending on the implementation.
Well, it is possible to call .ToList() for each IEnumerable in situations when we are going to iterate collection several times. But this decreases perfromance for static collections because of unnecessary new list instantiation.

So I'm trying to understand which approach is better.
More universal and potentially less performant vs More coupled-more performant. I guess, there's no silver bullet but it could be other approaches I've missed.

Stclair answered 28/1, 2013 at 16:19 Comment(4)
Note that if you use foreach to iterate an IEnumerable<T> which is in reality a List<T> or a plain array, it's very fast. The implementation does some optimisations. (You can verify this by using Stopwatch to time things.)Nw
Sure seems like a duplicate to me. Especially of the second question you linked. The accepted answer to that question addresses your concerns quite well. The key is, "it depends." If deferred execution is going to cause a performance problem, then have your DAL return a concrete list.Consensual
If you're on .Net 4.5, you can use IReadOnlyList<T>.Tuft
@Jimmischel. I agree but in case we want to provide modularity and changeability how can we expect more from the DAL than a mere interface?Stclair
D
2

If it's important, conceptually, for the caller to know that the return type of the method is a List and not an IEnumerable (for example, to know that there will be no negative consequences for iterating it multiple times), then you should return a List. The point of returning the interface instead is a way of saying, "It doesn't matter what the implementation is." If the implementation does matter, then don't use the interface (in that specific situation).

Digitalin answered 28/1, 2013 at 16:23 Comment(5)
" to know that there will be no negative consequences for iterating it multiple times" for IEnumerable. What do you mean ?Marja
@Marja It's conceivable that an IEnumerable could return completely different results when iterated multiple times, or it could involve querying a database or doing computationally intensive tasks. When given an IEnumerable from an unknown source it is therefore best to avoid iterating it multiple times at all costs. In certain situations this means putting the sequence into a list, so that you can be sure that iterating it multiple times won't have those (possibly undesirable) side effects. If it's already a list, you have just needlessly done work.Digitalin
Returning a list has it's own set of worries... such as, did the getter make a defensive copy?Nw
@MatthewWatson Sure. That doesn't mean you shouldn't ever return a list from any method.Digitalin
@Servy: I see, you're talking about declarative intention, but the OP, asks about concrete trade offs (possible ones) in its application. So your answer does not really answers his question. Declaring IEnumerable, defines an intention to define a stream of data, but it does not absolutely mean that you can or should not iterate over it multiple times, as it all depends on implementation details. So the question is: how it's better to architect return/and pass values in order possibly avoid scallability problems between different layers in the future.Marja
V
8

So I'm trying to understand which approach is better: more universal and less performant vs more coupled and more performant.

First, though that is an interesting tradeoff, in this case I would think that the relevant tradeoff is actually correct vs incorrect, which surely trumps any question of performance. A deferred-execution query usually has the property that it gives you the most up to date results of the query, whereas calling ToList gives you a snapshot of a past version of the query results. There are surely cases where one is correct and the other is incorrect.

Second, assuming that you have dealt with the correctness issue and really do have a performance tradeoff to make, the tradeoff you actually want to make is: more universal and unacceptable performance vs more coupled and acceptable performance, at which is becomes clear that you have to choose the one with acceptable performance.

If both have acceptable performance and one is a few nanoseconds slower than the other, or consumes a few bytes more memory than the other, and both are correct, then who cares which one you choose? Spend your valuable time thinking about something else. And if neither have acceptable performance then you have a bigger problem to solve.

Verduzco answered 28/1, 2013 at 16:31 Comment(2)
Unfortunately world is not ideal and we have to keep balance between openess of the architecture (the less effort we need to address radical requirements change the better it is) and YAGNI. I think that is a mixture of guessing and experience what helps to tell correct and incorrect apart. Here I'd add that IEnumerable leaves some notion of flexibility concerning data requests. If we treat BL as a CRUD layer for the clients IEnumerable allows them to ask for specific data. Thus we can keep BL simple not bloating it with a bunch of methods for different data read scenarios.Stclair
On the other hand as soon as you wish to move BL to the server the situation becomes the opposite. A set of methods is a great advantage. Now consider that you must refactor old highly couple application to the N-tier app and than move some of its parts to the application server. Next I add that developers of the client apps will be other guys and our team has to respond on their requests quite quickly. And all this a slow evolving process. If I had an experience what is correct and incorrect I wouldn't even ask =)Stclair
D
2

If it's important, conceptually, for the caller to know that the return type of the method is a List and not an IEnumerable (for example, to know that there will be no negative consequences for iterating it multiple times), then you should return a List. The point of returning the interface instead is a way of saying, "It doesn't matter what the implementation is." If the implementation does matter, then don't use the interface (in that specific situation).

Digitalin answered 28/1, 2013 at 16:23 Comment(5)
" to know that there will be no negative consequences for iterating it multiple times" for IEnumerable. What do you mean ?Marja
@Marja It's conceivable that an IEnumerable could return completely different results when iterated multiple times, or it could involve querying a database or doing computationally intensive tasks. When given an IEnumerable from an unknown source it is therefore best to avoid iterating it multiple times at all costs. In certain situations this means putting the sequence into a list, so that you can be sure that iterating it multiple times won't have those (possibly undesirable) side effects. If it's already a list, you have just needlessly done work.Digitalin
Returning a list has it's own set of worries... such as, did the getter make a defensive copy?Nw
@MatthewWatson Sure. That doesn't mean you shouldn't ever return a list from any method.Digitalin
@Servy: I see, you're talking about declarative intention, but the OP, asks about concrete trade offs (possible ones) in its application. So your answer does not really answers his question. Declaring IEnumerable, defines an intention to define a stream of data, but it does not absolutely mean that you can or should not iterate over it multiple times, as it all depends on implementation details. So the question is: how it's better to architect return/and pass values in order possibly avoid scallability problems between different layers in the future.Marja
B
0

I found good solution: All return types (the in interfaces) in DAL can't be IEnumerable<> (or can't be interface type). But it possible to use in BL. It can be architector's decision.

Burgener answered 28/1, 2013 at 16:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.