Here's the setup: I have an Open Source project called Massive and I'm slinging around dynamics as a way of creating SQL on the fly, and dynamic result sets on the fly.
To do the database end of things I'm using System.Data.Common and the ProviderFactory stuff. Here's a sample that works just fine (it's static so you can run in a Console):
static DbCommand CreateCommand(string sql) {
return DbProviderFactories.GetFactory("System.Data.SqlClient")
.CreateCommand();
}
static DbConnection OpenConnection() {
return DbProviderFactories.GetFactory("System.Data.SqlClient")
.CreateConnection();
}
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;
}
The result of running this code is "It worked!"
Now, if I change the string argument to dynamic - specifically an ExpandoObject (pretend that there's a routine somewhere that crunches the Expando into SQL) - a weird error is thrown. Here's the code:
What worked before now fails with a message that makes no sense. A SqlConnection is a DbConnection - moreover if you mouseover the code in debug, you can see that the types are all SQL types. "conn" is a SqlConnection, "cmd" is a SqlCommand.
This error makes utterly no sense - but more importantly it's cause by the presence of an ExpandoObject that doesn't touch any of the implementation code. The differences between the two routines are:
1 - I've changed the argument in CreateCommand() to accept "dynamic" instead of string
2 - I've created an ExpandoObject and set a property.
It gets weirder.
If simply use a string instead of the ExpandoObject - it all works just fine!
//THIS WORKS
static DbCommand CreateCommand(dynamic item) {
return DbProviderFactories.GetFactory("System.Data.SqlClient").CreateCommand();
}
static DbConnection OpenConnection() {
return DbProviderFactories.GetFactory("System.Data.SqlClient").CreateConnection();
}
public static dynamic DynamicWeirdness() {
dynamic ex = new ExpandoObject();
ex.TableName = "Products";
using (var conn = OpenConnection()) {
//use a string instead of the Expando
var cmd = CreateCommand("HI THERE");
cmd.Connection = conn;
}
Console.WriteLine("It worked!");
Console.Read();
return null;
}
If I swap out the argument for CreateCommand() to be my ExpandoObject ("ex") - it causes all of the code to be a "dynamic expression" which is evaluated at runtime.
It appears that the runtime evaluation of this code is different than compile-time evaluation... which makes no sense.
**EDIT: I should add here that if I hard-code everything to use SqlConnection and SqlCommand explicitly, it works :) - here's an image of what I mean:
question from:
https://stackoverflow.com/questions/7562205/why-is-an-expandoobject-breaking-code-that-otherwise-works-just-fine 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…