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.

Print | posted on Thursday, April 03, 2008 4:45 AM

Feedback


Gravatar

# re: Internals are Evil 4/3/2008 5:25 AM Jarod

Thats pretty cool stuff...

maybe I am missing something here, but wouldnt AsQueryable<T> work for this?


Gravatar

# re: Internals are Evil 4/3/2008 5:40 AM alwin

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 =
iqueryable.Expression;


Gravatar

# re: Internals are Evil 4/3/2008 5:41 AM alwin

ah Jarod beat me to it :)


Gravatar

# re: Internals are Evil 4/3/2008 8:56 AM Nick

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!


Gravatar

# re: Internals are Evil 4/3/2008 9:42 AM Andreas

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;


Gravatar

# re: Internals are Evil 4/3/2008 10:27 AM Tuna Toksoz

or do would you have another method returning IOrderedQueryable such as IOrderedQueryable<T> Linq();


Gravatar

# re: Internals are Evil 4/3/2008 11:13 AM Benny Thomas

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.


Gravatar

# re: Internals are Evil 4/3/2008 1:34 PM Ayende Rahien

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.


Gravatar

# re: Internals are Evil 4/3/2008 2:32 PM Tuna Toksoz

@Andreas
It would be hard to implement IOrderedQueryable since it requires you to explicit implement(private)

#region IEnumerable Members

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}

#endregion

this would make windsor throw error, unfortunately.


Gravatar

# re: Internals are Evil 4/3/2008 6:22 PM Nathan

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.


Gravatar

# re: Internals are Evil 4/3/2008 7:28 PM Jarod

@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 :)


Gravatar

# re: Internals are Evil 4/3/2008 9:20 PM Andrey Shchekin

> 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.

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.


Gravatar

# re: Internals are Evil 4/4/2008 2:15 AM Ayende Rahien

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


Gravatar

# re: Internals are Evil 4/4/2008 2:38 AM Andrey Shchekin

> It is not discoverable at all, however.
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.

> And I don't see how new EnumerableQuery<T> is uglier, or how this relates to the discussion
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.


Gravatar

# re: Internals are Evil 4/4/2008 4:16 AM Ayende Rahien

Andrey,
I entered my house today through the window


Gravatar

# re: Internals are Evil 4/4/2008 11:40 AM Jon Skeet

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.


Gravatar

# re: Internals are Evil 4/7/2008 2:44 PM ian Cooper

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



Comments have been closed on this topic.