这是设置:我有一个名为"Massive"的开源项目(github/robconery/massive),我正在动态地将动态作为一种动态创建SQL的方式,以及动态结果集.
要做数据库的结束我正在使用System.Data.Common和ProviderFactory的东西.这是一个工作正常的示例(它是静态的,因此您可以在控制台中运行):
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;
}
Run Code Online (Sandbox Code Playgroud)
运行此代码的结果是"它工作了!"
现在,如果我将字符串参数更改为dynamic - 特别是ExpandoObject(假设某个例程将Expando压缩为SQL) - 会抛出一个奇怪的错误.这是代码:

之前有效的方法失败了,没有任何意义的消息.SqlConnection 是一个DbConnection - 而且如果你在debug中鼠标悬停代码,你可以看到这些类型都是SQL类型."conn"是一个SqlConnection,"cmd"是一个SqlCommand.
这个错误完全没有意义 - 但更重要的是它是由于没有触及任何实现代码的ExpandoObject的存在.两个例程之间的区别是:1 - 我在CreateCommand()中更改了参数以接受"dynamic"而不是字符串2 - 我创建了一个ExpandoObject并设置了一个属性.
它变得更奇怪了.
如果只是使用一个字符串而不是ExpandoObject - 一切正常!
//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;
}
Run Code Online (Sandbox Code Playgroud)
如果我将CreateCommand()的参数换成我的ExpandoObject("ex") - 它会导致所有代码都是在运行时计算的"动态表达式".
看来这段代码的运行时评估与编译时评估不同......这没有任何意义.
**编辑:我应该在这里添加,如果我硬编码一切明确使用SqlConnection和SqlCommand,它的工作原理:) - 这是我的意思的图像:

Fra*_*ger 32
当您将动态传递给时CreateCommand,编译器将其返回类型视为必须在运行时解析的动态.不幸的是,你在解析器和C#语言之间遇到了一些奇怪的问题.幸运的是,通过删除使用var强制编译器执行您期望的操作,可以轻松解决此问题:
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;
}
Run Code Online (Sandbox Code Playgroud)
这已经在Mono 2.10.5上进行了测试,但我确信它也适用于MS.
Ale*_*oun 18
它表现得好像你试图在程序集中传递动态 匿名类型,这是不受支持的.ExpandoObject虽然支持传递.我使用的解决方法,当我需要传递程序集,并且我已成功测试它时,是将动态 输入变量转换为ExpandoObject传递它的时间:
public static dynamic DynamicWeirdness()
{
dynamic ex = new ExpandoObject();
ex.TableName = "Products";
using (var conn = OpenConnection()) {
var cmd = CreateCommand((ExpandoObject)ex);
cmd.Connection = conn;
}
Console.WriteLine("It worked!");
Console.Read();
return null;
}
Run Code Online (Sandbox Code Playgroud)
编辑: 正如评论中指出的那样,您可以跨程序集传递动态,您无法在不首先投射它们的情况下跨程序集传递匿名类型.
上述解决方案的有效性与Frank Krueger在上述说明的原因相同.
将动态传递给CreateCommand时,编译器会将其返回类型视为必须在运行时解析的动态.
小智 15
因为您使用dynamic作为参数CreateCommand(),所以cmd变量也是动态的,这意味着它的类型在运行时被解析为SqlCommand.相比之下,conn变量不是动态的,而是编译为类型DbConnection.
基本上,SqlCommand.Connection是类型SqlConnection,因此conn变量(类型DbConnection)是要设置的无效值Connection.您可以通过转换conn为SqlConnection或修改conn变量来解决此问题dynamic.
它之前工作正常的原因是因为cmd它实际上是一个DbCommand变量(即使它指向同一个对象),并且DbCommand.Connection属性是类型的DbConnection.即SqlCommand该类具有new该Connection属性的定义.
来源问题注释:
public static dynamic DynamicWeirdness() {
dynamic ex = new ExpandoObject();
ex.TableName = "Products";
using (var conn = OpenConnection()) { //'conn' is statically typed to 'DBConnection'
var cmd = CreateCommand(ex); //because 'ex' is dynamic 'cmd' is dynamic
cmd.Connection = conn;
/*
'cmd.Connection = conn' is bound at runtime and
the runtime signature of Connection takes a SqlConnection value.
You can't assign a statically defined DBConnection to a SqlConnection
without cast.
*/
}
Console.WriteLine("It will never get here!");
Console.Read();
return null;
}
Run Code Online (Sandbox Code Playgroud)
修复源的选项(仅挑选1):
转换为静态声明conn为SqlConnection:
using (var conn = (SqlConnection) OpenConnection())
使用运行时类型conn:
using (dynamic conn = OpenConnection())
不要动态绑定CreateCommand:
var cmd = CreateCommand((object)ex);
静态定义cmd:
DBCommand cmd = CreateCommand(ex);
查看抛出的异常,似乎即使OpenConnection返回静态类型(DbConnection)而CreateCommand返回静态类型(DbCommand),因为传递给DbConnection的参数是dynamic类型,它基本上将以下代码视为动态绑定现场:
var cmd = CreateCommand(ex);
cmd.Connection = conn;
Run Code Online (Sandbox Code Playgroud)
因此,运行时绑定程序试图找到可能的最具体的绑定,即将连接转换为SqlConnection.尽管该实例在技术上是一个SqlConnection,但它静态地键入为DbConnection,因此这是绑定器尝试从中进行转换的内容.由于没有从DbConnection到SqlConnection的直接转换,它失败了.
从这个处理底层异常类型的SO答案中得出的似乎是,实际上将conn声明为动态,而不是使用var,在这种情况下,绑定器找到SqlConnection - > SqlConnection setter并且正常工作,如下所示:
public static dynamic DynamicWeirdness()
{
dynamic ex = new ExpandoObject();
ex.TableName = "Products";
using (dynamic conn = OpenConnection())
{
var cmd = CreateCommand(ex);
cmd.Connection = conn;
}
Console.WriteLine("It worked!");
Console.Read();
return null;
}
Run Code Online (Sandbox Code Playgroud)
话虽如此,鉴于您静态键入CreateCommand的返回类型为DbConnection,人们会认为绑定器在这种情况下做得更好"做正确的事",这可能是一个错误C#中的动态绑定器实现.