Pavel said:
I think you've missed my point. Foo<T> is a part of a reusable library.
Inside, it contains the LINQ query you gave, but it has no way to map
SomeMethod (or even know that it should be mapped). Outside, the client of
the library shouldn't have to second-guess what methods are used by
implementation of Foo<T>, and should be mapped as needed.
In that case, it will never work, or better: never use a .NET
method/property in your additional extension method calls. I mean, even
an entity class E with 10 properties might cause problems because
property Bar is a property without a mapping in the DB...
i.o.w.: don't use Linq this way. You want encapsulation? Sure, but
encapsulate it properly. If you want to append predicates to an existing
queryable outside of the scope where the queryable was created, make
sure that appending is done at the same abstraction level, otherwise use
the specification pattern.
the thing is that you THINK you abstracted away everything, but that's
a fallacy. You clearly assume that the IQueryable q is adjustable with
whatever you throw at it. This is of course wrong: the IQueryable might
us an IQueryable datasource D and Foo() might add elements using
IQueryable datasource E, though RUNNING the query, i.e. enumerating it,
will use a single linq provider, not two. Which one? Can you tell
without looking in how Linq providers are written? No. Will it work? No
way: if D is Linq to Sql and E is Linq to NHibernate, your query will
never run. Yes it's a silly example, but it illustrates the point that
abstracting things away for the purpose of "Now I can do whatever I want
in a generic way" requires a different approach than the low-level
approach you chose, for the simple reason that an IQueryable isn't a
query specification (I wished it was) but a query specification AND a
query result, created from the already embedded provider.
In short, it's very hard to deal with IQueryable<T> in a generic way even
with full-featured LINQ providers. Therefore, I question the assertion that
there is some significant leap between limited and full-featured providers -
if you can't use either one in a generic fashion in encapsulated code,
without minding the limitations of a specific provider (which kills the
genericity right on the spot), what's the real difference, aside from the
actual number of limitations?
Try to write normal queries for a day or two without running into
problems that you have to fall back to native query api's. Sure, simple
queries might work (although most simple providers fail on simple group
joins which result is used further in the query) but simple things
always work, it's in the queries which aren't simple but will make you
fall back to native query api's because the linq provider failed, and
thus make the usage of a linq provider quite unnecessary as well,
because simple linq queries are also simple in most native query api's.
example:
var q = from c in ctx.Customer
group c by c.City into g
select g;
result: a hierachical structure of IGrouping<string, Customer> objects,
grouped by key 'City'. VERY handy in a lot of situations. 2 queries
tops. I can assure you, doing this in a native query api of any o/r
mapper isn't as simple as this. That's why linq providers need to be
solid, and full features to be useful in every day work: the developer
using the provider is payed to write code WITH linq to solve the
client's problems, not to fight with an O/R mapper's Linq provider and
first wondering if the developer made an error in the linq query, then
fall back onto native query api syntaxis.
But of course, it's your time and money
FB
--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website:
http://www.llblgen.com
My .NET blog:
http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------