2011-12-11 175 views
35

我有一个函数返回一个匿名类型,我想在我的MVC控制器中进行测试。将匿名类型转换为动态

public JsonResult Foo() 
{ 
    var data = new 
        { 
         details = "something", 
         more = "More" 
        }; 
    return Json(data); 
} 

我想验证我从富函数获取数据,我在做什么,现在越来越的数据类型,并把它的属性值与反思。

[Test] 
public void TestOne() 
{ 
    var data = _controller.Foo().Data; 
    var details = data.GetType().GetProperty("details").GetValue(data, null); 
    var more = data.GetType().GetProperty("more").GetValue(data, null); 

    Assert.AreEquals("something", details); 
    Assert.AreEquals("More", more); 
} 

有没有类似于此的检查匿名属性的简单方法?

[Test] 
public void TestTwo() 
{ 
    var data = (dynamic) _controller.Foo().Data; 
    var details = data.details; // RunTimeBinderException object does not contain definition for details 
    var more = data.more; 

    Assert.AreEquals("something", details); 
    Assert.AreEquals("More", more); 
} 
+7

由于这是单元测试,你可以使用'InternalsVisibleTo'。请参阅[匿名类型是内部的,C#4.0 Dynamic Beware!](http://www.heartysoft.com/anonymous-types-c-sharp-4-dynamic)感谢@MarcGravell指出匿名对象是'internal' ! – TrueWill

+0

+1为InternalsVisibleTo建议。奇迹般有效。 –

回答

34

匿名对象将internal,这意味着它们的成员在声明这些组件外部受到很大限制。 dynamic尊重可访问性,所以假装不能看到这些成员。如果呼叫站点在同一个程序集中,我预计它会起作用。

您的反思代码尊重会员可访问性,但绕过该类型的可访问性 - 因此它可以工作。

总之:没有。

+2

@gdoron为什么会这样?毕竟,它仍然是一个对象。它只是不暴露其他许多。 ToString(),Equals(),GetHashCode()等仍然通过动态工作。它只是不添加任何其他可见。 –

6

匿名类型是.NET中的常规静态类型,它只是您不给它起个名字(然而编译器)。这就是为什么将它投射到dynamic不起作用。但是,如果您可以控制Foo(),则可以构造并返回dynamic对象而不是匿名对象,然后您的代码即可运行。这应该做的伎俩:

dynamic JsonResult Foo() { 
    dynamic data = new ExpandoObject(); 
    data.details = "something"; 
    data.mode = "More"; 
    return Json(data); 
} 
+0

。数据是“对象”。 “对象”和“动态”之间几乎没有什么区别,除了消费者(这是获得乐趣的地方)。我不相信在“对象”和“动态”之间(然后回到动态)之间的转换在这里将会有很大的作用。 –

+0

@MarcGravell我添加了代码来阐明我的意思是*返回动态*(我的意思是“构造一个动态对象并将其返回”,而不是简单地将返回类型更改为动态)。感谢您的支持 - 最初的编辑确实不清楚。 – dasblinkenlight

+1

使这项工作的主要事情是:ExpandoObject是公开的而不是内部的(当然实现了IDynamicBlahBlahBlah接口作为公共声明)。但是,它提示了一个重要问题:像ExpandoObject这样的JSON层? (由于IDictionary的使用,它*可能*)。这也意味着我们不再使用匿名类型(问题) –

22

此博客有一个工作答案:http://blog.jorgef.net/2011/06/converting-any-object-to-dynamic.html - 谢谢@ Jorge-Fioranelli。

public static class DynamicExtensions { 
    public static dynamic ToDynamic(this object value) { 
     IDictionary<string, object> expando = new ExpandoObject(); 

     foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType())) 
      expando.Add(property.Name, property.GetValue(value)); 

     return expando as ExpandoObject; 
    } 
} 
4

至于建议的@TrueWill和@Marc Gravell,谁也称为this blog post

由于这是单元测试,你可以使用InternalsVisibleTo。请参阅匿名类型是内部的,C#4.0 Dynamic Beware!感谢@MarcGravell指出匿名对象是内部的!

底线:如果要将匿名对象从一个程序集共享到另一个程序集,请设置[assembly: InternalsVisibleTo("foo")]映射。在OP的情况下,这是在MVC控制器项目中设置的,参考测试项目。在我的具体情况下,反过来(因为我从我的测试项目传递一个匿名对象到“生产代码”项目中)。

在“其他项目”能够使用它的最简单的方法是将其转换为dynamic,然后使用像普通的属性。它确实有效,没有任何问题。

所以,底线:我觉得Marc Gravell的回答有点不正确;这可以很清楚地完成
iff您所讨论的项目是可以修改的,因此您可以设置InternalsVisibleTo映射,这不会造成任何其他原因的问题)。

1

您可以使用NewtonSoft或Asp。净MVC库:

var data = Json.Decode(Json.Encode(_controller.Foo().Data));

var data=JsonConvert.DeserializeObject<Dictionary<string,object>>(JsonConvert.SerializeObject((_controller.Foo().Data))