为什么在其他情况下可以正常工作的ExpandoObject破坏代码? [英] Why is an ExpandoObject breaking code that otherwise works just fine?

查看:84
本文介绍了为什么在其他情况下可以正常工作的ExpandoObject破坏代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

设置如下:我有一个名为 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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆