为什么在其他情况下可以正常工作的ExpandoObject破坏代码? [英] Why is an ExpandoObject breaking code that otherwise works just fine?
问题描述
设置如下:我有一个名为 Massive 的开源项目,并且我在动态范围内徘徊作为动态创建SQL和动态结果集的一种方式.
要完成数据库操作,我正在使用System.Data.Common和ProviderFactory的东西.这是一个很好用的示例(它是静态的,因此您可以在控制台中运行):
静态DbCommand CreateCommand(字符串sql){返回DbProviderFactories.GetFactory("System.Data.SqlClient").CreateCommand();}静态DbConnection OpenConnection(){返回DbProviderFactories.GetFactory("System.Data.SqlClient").CreateConnection();}公共静态动态DynamicWeirdness(){使用(var conn = OpenConnection()){var cmd = CreateCommand("SELECT * FROM Products");cmd.Connection = conn;}Console.WriteLine(成功了!");Console.Read();返回null;}
运行此代码的结果是行得通!"
现在,如果我将字符串参数更改为dynamic-特别是ExpandoObject(假设某个地方的例程将Expando转换为SQL),则会引发奇怪的错误.这是代码:
以前的工作现在失败,并显示一条毫无意义的消息.SqlConnection 是 DbConnection-此外,如果将鼠标悬停在调试代码中,则可以看到所有类型都是SQL类型."conn"是一个SqlConnection,"cmd"是一个SqlCommand.
该错误完全没有意义-但更重要的是,它是由于存在一个ExpandoObject而引起的,该ExpandoObject没有涉及任何实现代码.两种例程之间的区别是:1-我已将CreateCommand()中的参数更改为接受动态"而不是字符串2-我创建了ExpandoObject并设置了一个属性.
变得怪异
如果只是使用字符串而不是ExpandoObject-一切都很好!
//此工作静态DbCommand CreateCommand(动态项目){返回DbProviderFactories.GetFactory("System.Data.SqlClient").CreateCommand();}静态DbConnection OpenConnection(){返回DbProviderFactories.GetFactory("System.Data.SqlClient").CreateConnection();}公共静态动态DynamicWeirdness(){动态ex = new ExpandoObject();ex.TableName =产品";使用(var conn = OpenConnection()){//使用字符串代替Expandovar cmd = CreateCommand("HI THERE");cmd.Connection = conn;}Console.WriteLine(成功了!");Console.Read();返回null;}
如果我将CreateCommand()的参数换成我的ExpandoObject("ex"),则会导致 all 的所有代码成为动态表达式",并在运行时进行评估./p>
此代码的运行时评估似乎与编译时评估不同……这没有道理.
**我应该在这里补充一下,如果我对所有内容进行硬编码以显式使用SqlConnection和SqlCommand,那么它会起作用:)-这是我的意思的图片:
当您将动态传递给 CreateCommand
时,编译器会将其返回类型视为必须在运行时解析的动态.不幸的是,您在该解析器和C#语言之间遇到了一些奇怪的问题.幸运的是,通过消除对 var
的使用(使编译器执行您期望的工作),很容易解决:
公共静态动态DynamicWeirdness(){动态ex = new ExpandoObject();ex.Query ="SELECT * FROM产品";使用(var conn = OpenConnection()){DbCommand cmd = CreateCommand(ex);//<-请勿使用VARcmd.Connection = conn;}Console.WriteLine(成功了!");Console.Read();返回null;}
这已经在Mono 2.10.5上进行了测试,但是我确定它也可以在MS上使用.
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:
When you pass the dynamic to CreateCommand
, the compiler is treating its return type as a dynamic that it has to resolve at runtime. Unfortunately, you're hitting some oddities between that resolver and the C# language. Fortunately, it's easy to work around by removing your use of var
forcing the compiler to do what you expect:
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;
}
This has been tested on Mono 2.10.5, but I'm sure it works with MS too.
这篇关于为什么在其他情况下可以正常工作的ExpandoObject破坏代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!