I know these questions have been asked before, I'll start by listing a few of them (the ones I've read so far):
As you can see, there's some great resources on SO alone on the subject, but there is one question/section of the question I'm still not sure about having read these all through.
I'm primarily concerned with the IEnumerable vs IQueryable question, and more specifically the coupling between the DAL and it's consumers.
I've found varying opinions suggested regarding the two interfaces, which have been great. However, I'm concerned with the implications of a DAL returning IQueryable. As I understand it IQueryable suggest/implies that there is a Linq Provider under the hood. That's concern number one - what if the DAL suddenly requires data from a non-Linq provided source? The following would work, but is it more of a hack?
public static IQueryable<Product> GetAll()
{
// this function used to use a L2S context or similar to return data
// from a database, however, now it uses a non linq provider
// simulate the non linq provider...
List<Product> results = new List<Product> { new Product() };
return results.AsQueryable();
}
So I can use the AsQueryable() extension though I don't admit to knowing exactly what this does? I always imagine IQueryables as being the underlying expression trees which we can append as necessary until we're ready to perform our query and fetch the results.
I could rectify this by changing the return type of the function to IEnumerable. I can then return IQueryable from the function because it inherits IEnumerable, and I get to keep the deferred loading. What I lose is the ability to append to the query expression:
var results = SomeClass.GetAll().Where(x => x.ProductTypeId == 5);
When returning IQueryable, as I understand it, this would simply append the expression. When returning IEnumerable, despite maintaining the deferred loading, the expression has to be evaluated so the results will be brought to memory and enumerated through to filter out incorrect ProductTypeIds.
How do other people get round this?
- Provide more functions in the DAL - GetAllByProductType, GetAllByStartDate,... etc
Provide an overload that accepts predicates? i.e.
public static IEnumerable<Product> GetAll(Predicate<Product> predicate)
{
List<Product> results = new List<Product> { new Product() };
return results.Where(x => predicate(x));
}
One last part (Sorry, I know, really long question!).
I found IEnumerable to be the most recommended across all the questions I checked, but what about the deferred loadings' requirement for a datacontext to be available? As I understand it, if your function returns IEnumerable, but you return IQueryable, the IQueryable is reliant on an underlying datacontext. Because the result at this stage is actually an expression and nothing has been brought to memory, you cannot guarantee that the DAL's/function's consumer is going to perform the query, nor when. So do I have to keep the instance of the context that the results were derived from available somehow? Is this how/why the Unit of Work pattern comes into play?
Summary of the questions for clarity ( did a search for "?"...):
- If using IQueryable as a return type, are you too tightly coupling your UI/Business Logic to Linq Providers?
- Is using the AsQueryable() extension a good idea if you suddenly need to return data from a non-Linq Provided source?
- Anyone have a good link describing how for example converting a standard list to AsQueryable works, what it actually does?
- How do you handle additional filtering requirements supplied by business logic to your DAL?
- It seems the deferred loading of both IEnumerable and IQueryable are subject to maintaining the underlying provider, should I be using a Unit of Work pattern or something else to handle this?
Thanks a lot in advance!
See Question&Answers more detail:
os