Breaking the C# compiler, again
Take a look at the following code:
1: var docs = new dynamic[0];2: var q = from doc in docs3: where doc["@metadata"]["Raven-Entity-Name"] == "Cases"4: where doc.AssociatedEntities != null5: from entity in doc.AssociatedEntities6: where entity.Tags != null // COMPILER ERROR HERE7: from tag in entity.Tags8: where tag.ReferencedAggregate != null9: select new {tag.ReferencedAggregate.Id, doc.__document_id};
It doesn’t compiles, complaining about an error in line 6 (The property '<>h__TransparentIdentifier0' cannot be used with type arguments).
Leaving aside the issue with the error itself, which is about as useful as C++ template errors, I have a strongly issue with this.
What do you think can fix this error? Well, if you remove line 8 (!), it just works:
1: var docs = new dynamic[0];2: var q = from doc in docs3: where doc["@metadata"]["Raven-Entity-Name"] == "Cases"4: where doc.AssociatedEntities != null5: from entity in doc.AssociatedEntities6: where entity.Tags != null7: from tag in entity.Tags8: select new { tag.ReferencedAggregate.Id, doc.__document_id };
There are several things that I can’t figure out:
- Why am I getting an error?
- Why removing a line after the error fixes the error in the line before it.
What I suspect is that the error reporting (which in C# is usually damn good) is getting confused with the error reporting location.
To make things worse, this code will crash VS 2010 every single time. I am actually editing this in notepad!!!
Here is the set of extensions methods that I use here:
public static class LinqOnDynamic { private static IEnumerable<dynamic> Select(this object self) { if (self == null) yield break; if (self is IEnumerable == false || self is string) throw new InvalidOperationException("Attempted to enumerate over " + self.GetType().Name); foreach (var item in ((IEnumerable) self)) { yield return item; } } public static IEnumerable<dynamic> SelectMany(this object source, Func<dynamic, int, IEnumerable<dynamic>> collectionSelector, Func<dynamic, dynamic, dynamic> resultSelector) { return Enumerable.SelectMany(Select(source), collectionSelector, resultSelector); } public static IEnumerable<dynamic> SelectMany(this object source, Func<dynamic, IEnumerable<dynamic>> collectionSelector, Func<dynamic, dynamic, dynamic> resultSelector) { return Enumerable.SelectMany(Select(source), collectionSelector, resultSelector); } public static IEnumerable<dynamic> SelectMany(this object source, Func<object, IEnumerable<dynamic>> selector) { return Select(source).SelectMany<object, object>(selector); } public static IEnumerable<dynamic> SelectMany(this object source, Func<object, int, IEnumerable<dynamic>> selector) { return Select(source).SelectMany<object, object>(selector); } }
WTF?!
Comments
Simple options that come to mind: What happens if you break that query down into separate queries?
Have you tried setting up to debug into the ,Net source? It might be easier to step through with the extension method syntax:
var q = docs.Where(doc => doc["@metadata"]["Raven-Entity-Name"] == "Cases" && doc.AssociatedEntities != null)
.SelectMany( doc => doc.AssociatedEntities)
.Where(entity => entity.Tags != null)
.SelectMany(entity => entity.Tags)
.Where(tag => tag.ReferencedAggregate != null)
.Select (tag => new {tag.ReferencedAggregate.Id, doc.__document_id});
Should compile down to effectively the same operations, I just find the extension syntax more consistent to follow in code.
Have you tried writing (retyping) the same code in another file. I had a couple of weird issues with whitespace that was a result of copying-and-pasting.
Sounds like a good SO question.
Try to attach Reflector Pro - it can step into Framework assemblies.
FallenGameR,
The issue is in the _compiler_, not at runtime
When I ran the query in the LinqPad, I get following error on line 5 (before your error):
5: from entity in doc.AssociatedEntities
An expression of type 'dynamic' is not allowed in a subsequent from clause in a query expression with source type 'System.Collections.Generic.IEnumerable <dynamic'. Type inference failed in the call to 'SelectMany'.
And Studio 2010 says the same error as LinqPad.
What studio & target framework are you using?
Stupid question.... =) dynamic is in 4.0 and 4.0 is in 2010.
Anyway, the code you provided doesn't crush on my side.
@David Cuccia I hope that was in reference to:
"Ayende is not your helpdesk"
It goes both ways.
:)
The compiler rewrites the syntactic sugar that are query expression to (extension) method calls. This is an early pass that the compiler does to get to plain vanilla c# statements.
I think the parser gets seriously confused by your line 8 probably in comination with previous build up state.
What happens if your rewrite the query expression to plain method calls?
I think I saw a Rob Conery post where he details issues with dynamic C# classes/properties not being able to be resolved with extension methods. Sounds like thats related to whats happening here and as a result your linq extension methods can no longer be found... thats just a wild guess though.
Obviously a compiler bug as the transparent identifier class names should never be visible. On stack overflow you can find other C# compiler bugs and even bugs in the Jitter. I personally have only hit one single compiler bug in C# at all. Cant complain about Microsofts quality standards at all (in which product or lib that you are using for five years have you found <= 1 bug?).
My day-job includes developing a compiler and I can so well relate to this scenario. But, of course, we're talking about VS here. You would obviously expect a lot from the compiler.
It seems like MS have a lot to fix in their c# 4.0 compiler that is dealing with dynamics.
'Cause as simple code as this will crash the compiler:
using System;
using System.Collections.Generic;
namespace ExcelToRavenDbExporter
{
<string,>
}
Interesting, the lambda-syntax fails here too.
This doesn't compile:
var q = docs.SelectMany(doc => doc.AssociatedEntities);
Neither does this:
var q2 = docs.SelectMany((dynamic doc) => doc.AssociatedEntities);
Both with the same error: 'object' does not contain a definition for 'AssociatedEntities' and no extension method 'AssociatedEntities' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
However this works:
var q = docs.Where(doc => doc.AssociatedEntities != null);
So you can absolutely use dynamic as lambda parameters. Sometimes.
MAkes sure project target is set to .net 4.0?
I just think you're idiot. That's why it keeps crashing. Reading your posts reveal complete lameness and idiocy. You're doing things the hard way and you're thinking the universe spins around you. That said - you're idiot.
"I just think you're idiot. That's why it keeps crashing. Reading your posts reveal complete lameness and idiocy. You're doing things the hard way and you're thinking the universe spins around you. That said - you're idiot. "
--> humpbacked lout : Maybe you should rethink that statement with the following in mind: If you don't understand maybe you're idiot, and stop reading this blog.
Comment preview