Internals are Evil
A while ago I talked about Internal Stripping, not surprisingly, I run into another "WTF is this internal?" class just now. And I think it would be a good sample to see what can cause me to pull the pliers and start messing with things.
The scenario was simple, I was talking with Rob Conery about a webcast he is doing, and we started talking about TDD and repositories in a Linq enabled world.
What we ended up with was the requirement to turn an List<T> to IQueryable<T>, and there isn't easy way to do that. The framework does have an implementation of this, but, naturally, it is marked internal. Not content to have to deal with writing a non trivial Linq to List<T> provider, I wrote the following:
private static IQueryable<T> CreateQueryable<T>(IEnumerable<T> inner) { Type queryable = typeof (IQueryable).Assembly.GetType("System.Linq.EnumerableQuery`1").MakeGenericType(typeof (T)); ConstructorInfo constructor = queryable.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,
null, new Type[] {typeof (IEnumerable<T>)},new ParameterModifier[0]); object instance = FormatterServices.GetSafeUninitializedObject(queryable); constructor.Invoke(instance, new object[] { inner }); return (IQueryable<T>)instance; }
Not it works.
Comments
Thats pretty cool stuff...
maybe I am missing something here, but wouldnt AsQueryable<T> work for this?
Doesn't this work?
http://msdn2.microsoft.com/en-us/library/bb507003.aspx
The following code example demonstrates how to use AsQueryable<(Of <(TElement>)>)(IEnumerable<(Of <(TElement>)>)) to convert an IEnumerable<(Of <(T>)>) to an IQueryable<(Of <(T>)>).
List<int> grades = new List<int> { 78, 92, 100, 37, 81 };
// Convert the List to an IQueryable<int>.
IQueryable<int> iqueryable = grades.AsQueryable();
// Get the Expression property of the IQueryable object.
System.Linq.Expressions.Expression expressionTree =
ah Jarod beat me to it :)
Haha :) got me too: http://ubik.com.au/article/named/mocking_iqueryable
I think the problem is that we naturally look for .ToQueryable() - I still find myself confused!
How would a linq:ified version of your IRepository look like?
Do you let IRepository implement IOrderedQueryable
so you can do stuff like:
IRepository<Customer> customerRepository .....
var q =from c in customerRepository
where c.City == "London"
orderby c.CustomerID
select c;
or do would you have another method returning IOrderedQueryable such as IOrderedQueryable<T> Linq();
I feel this is somewhat the problem with the CLR enviroment. You think that this feature isnt there or not available, but it's only hidden straight infront of you so you want find it until is to late.
Jarod,
Yes, AsQueryable would work. Except that I didn't know about it, and going the simple path, of walking the inheritance chain only revealed EnumerableQuery.
Since it is directly used in AsQueryable, I don't know why this is not a public type.
@Andreas
It would be hard to implement IOrderedQueryable since it requires you to explicit implement(private)
this would make windsor throw error, unfortunately.
This is a good article about how to use UnitOfWork concepts with Linq to Sql. http://iancooper.spaces.live.com/blog/cns!844BD2811F9ABE9C!397.entry
The ITable interface is much like IRepository but you use Linq expressions instead of Criteria or HQL and it's very easy to implement.
@Ayende
Ya, extension methods do not show. I use the object browser, make sure I am browsing only 3.5 and set the filter to 'Show Extension Members'. When a type is selected, you will see an Extension Members folder which contains them.
Heres a quick post with a visual if anyone is interested
http://elegantcode.com/2008/04/03/exploring-extension-methods-in-net-35/
@Nick
I agree, ToQueryable would have been a natural choice :)
Hm. I remember making a following point:
"There are a lot of cases when using some lower-level code for functionality that is already provided by the higher-level one is more complex, more problematic, and can lead to code duplication.
Now, the perfect developer instantly knows what classes/methods to use for his specific task. The real developers I worked with often choose what they found first, just because when they invent the way to use it, they stop searching."
To which you answered:
"I am not really concerned about scaring develpers.
...
I recognize the need for private classes, by all mean encapsulate using that. But internal is evil in frameworks"
Seems my point demonstrated itself, since new EnumerableQuery<T>(list) is much uglier than list.AsQueryable. And this is one of the reasons the alternative way was made internal -- to make choice simpler.
It is not discoverable at all, however.
And I don't see how new EnumerableQuery<T> is uglier, or how this relates to the discussion
For me it was discoverable in the same way new Max or Min were discoverable -- the new Enumerable is the first place where I check when I want to do something with IEnumerable. However, I understand that discoverability is not objective.
This is a longer way to write the same thing (it requires a type unless you wrap it in the method). More importantly, it would teach all people who look at your code to use this complex solution. This is similar to entering through the window -- it is possible, is it better?
As for discussion, by default I see internals as an indication of "there is a better way". Of course, it requires me to believe that "select isn't broken" -- that internals in Framework are hidden on purpose. It is harder to believe that after things like SqlCommandSet and ASP.NET insides, but I still try to figure out how to do things in a way framework authors intended.
Andrey,
I entered my house today through the window
Another vote for "easily discoverable" here - if you've got a using directive for System.Linq, it just comes up as one of the extension methods you can call with Intellisense. It also comes up in local MSDN if you look at the members of IEnumerable<T> or any of the implementations.
As for the ToQueryable vs AsQueryable debate - there's an interesting bit of consistency here which I personally missed until I read about it in LINQ in Action:
The "AsXXX" methods represent the same data source as a different type, but it's still a data source with deferred execution.
The "ToXXX" methods create an instance of XXX from the given data source immediately - they don't defer execution.
So AsQueryable is the appropriate name here.
Much as I'm always reluctant to disagree with smarter people, I have to say I have no problem with internal classes. If MS had to make all classes in the framework public then:
a) Discoverability would go down rather than up due to the extra volume of classes which largely aren't useful.
b) They'd have a lot more work to do in order to implement the same features - meaning that there'd be less released, or it would be at a worse quality.
c) They'd be much more limited in terms of changing the implementation design (if you see what I mean) later on. The more you expose, the more you get locked into that design.
I talk about LINQ, repositories and TDD a fair amount here:
http://codebetter.com/blogs/ian_cooper/archive/2008/02/17/architecting-linq-to-sql-applications-part-5.aspx
and an older version here:
http://iancooper.spaces.live.com/Blog/cns%21844BD2811F9ABE9C%21397.entry
In case that helps you find what you need
Comment preview