public
Last active

Massive dynamic issues

  • Download Gist
gistfile1.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
// Example 1:
// conn has a compile time type of DbConnection, as this is what is returned by OpenConnection
// cmd has compile time type of DbCommand, as this is what is returned by CreateCommand
// Both have runtime types of the more specific SqlConnection/SqlCommand
public static dynamic DynamicWeirdness() {
using (var conn = OpenConnection()) {
var cmd = CreateCommand("SELECT * FROM Products");
cmd.Connection = conn;
}
Console.WriteLine("It worked!");
Console.Read();
return null;
}
 
// Example 2:
// conn has a compile time type of DbConnection, as this is what is returned by OpenConnection
// cmd is only evaluated at runtime due to dynamic. At runtime, it becomes the most specific SqlCommand
// SqlCommand.Connection requires SqlConnection and although conn's runtime type is SqlConnection, its
// compile-time is still DbConnection and thus you get the error
public static dynamic DynamicWeirdness() {
dynamic ex = new ExpandoObject();
ex.TableName = "Products";
using (var conn = OpenConnection()) {
var cmd = CreateCommand(ex);
cmd.Connection = conn;
}
Console.WriteLine("It worked!");
Console.Read();
return null;
}
 
// Example 3:
// conn has a compile time type of DbConnection, as this is what is returned by OpenConnection
// cmd is evaluated at runtime due to dynamic to be any type compatible with DbCommand.
// Runtime type is still SqlCommand, but compile-time is specified as DbCommand (due to not using var).
// DbCommand.Connection accepts DbConnection, which is the compile-time type of conn
public static dynamic DynamicWeirdness() {
dynamic ex = new ExpandoObject ();
ex.Query = "SELECT * FROM Products";
using (var conn = OpenConnection()) {
DbCommand cmd = CreateCommand(ex); // <-- DON'T USE VAR
cmd.Connection = conn;
}
Console.WriteLine("It worked!");
Console.Read();
return null;
}

I don't think this is the case. The reasoning is that the entire block is a dynamic expression - given that then there is no compile time type. This has nothing to do with DbCommand/SqlCommand - if I cast "ex" as ExpandoObject it still works regardless of what the compiler things the Command is.

I disagree. I assume by 'the entire block is a dynamic expression' you mean the fact that the method returns dynamic? This has nothing to do with compile time types within the method, where applicable. Furthermore, the behaviour remains when you change the method to return object or make it a void method. Casting to ExpandoObject works for the same reason that removing var and using DbCommand works - it changes the compile-time type to DbCommand. If you use the dynamic object as parameter, hover over the var keyword in VS, the tooltip says its type is dynamic. Cast ex to ExpandoObject and hover again, it now has a compile-time type of DbCommand.

OK so the next twist is that if I set "using dynamic conn = OpenConnection()" it works as well. What I mean by "the entire block is an expression" means exactly that - if you hover over it the compiler has no idea what's going on - there's "too much dynamic" happening. So it hands it off to the runtime binder to figure out.

Now here's where it gets interesting. Take a look at the question again - see the last edit? See where I output the runtime types for each variable? They're SqlClient.

We're talking in circles here - but everything you're writing here is not convincing me that there's a compile-time type for the code I've written. There can't be. It's dynamic and maybe IL is taking a swing at figuring it out... but there's no way that you and I can know what that special magic is since we're not compilers.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.