Ayende @ Rahien

Refunds available at head office

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

Jarod
04/03/2008 02:25 AM by
Jarod

Thats pretty cool stuff...

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

alwin
04/03/2008 02:40 AM by
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 grades = new List { 78, 92, 100, 37, 81 };

// Convert the List to an IQueryable.

IQueryable iqueryable = grades.AsQueryable();

// Get the Expression property of the IQueryable object.

System.Linq.Expressions.Expression expressionTree =

iqueryable.Expression;
alwin
04/03/2008 02:41 AM by
alwin

ah Jarod beat me to it :)

Nick
04/03/2008 05:56 AM by
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!

Andreas
04/03/2008 06:42 AM by
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 customerRepository .....

var q =from c in customerRepository

where c.City == "London"

orderby c.CustomerID

select c;

Tuna Toksoz
04/03/2008 07:27 AM by
Tuna Toksoz

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

Benny Thomas
04/03/2008 08:13 AM by
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.

Ayende Rahien
04/03/2008 10:34 AM by
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.

Tuna Toksoz
04/03/2008 11:32 AM by
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.

Nathan
04/03/2008 03:22 PM by
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.

Jarod
04/03/2008 04:28 PM by
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 :)

Andrey Shchekin
04/03/2008 06:20 PM by
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(list) is much uglier than list.AsQueryable. And this is one of the reasons the alternative way was made internal -- to make choice simpler.

Ayende Rahien
04/03/2008 11:15 PM by
Ayende Rahien

It is not discoverable at all, however.

And I don't see how new EnumerableQuery is uglier, or how this relates to the discussion

Andrey Shchekin
04/03/2008 11:38 PM by
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 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.

Ayende Rahien
04/04/2008 01:16 AM by
Ayende Rahien

Andrey,

I entered my house today through the window

Jon Skeet
04/04/2008 08:40 AM by
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 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.

ian Cooper
04/07/2008 11:44 AM by
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.