2011-12-11 179 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))