2011-12-11 2 views
35

У меня есть функция, которая возвращает анонимный тип, который я хочу протестировать в моем MVC-контроллере.Кастинг анонимного типа для динамического

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

Я хочу, чтобы проверить данные, которые я получаю от функции Foo, что я делаю сейчас получаю тип данных и получить его значение свойства с отражением.

[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 за указание, что анонимные объекты являются «внутренними» ! – TrueWill

+0

+1 для InternalsVisibleTo предложение. Работает как шарм. –

ответ

34

скрытых объектов internal, что означает, что их члены очень ограничены за пределами сборки, объявляющего их. dynamic уважает доступность, поэтому делает вид, что не может видеть этих участников. Если сайт вызова находился в одной и той же сборке, я ожидаю, что это сработает.

Ваш код отражения соответствует члену доступность, но обходит доступность типа - следовательно, он работает.

Вкратце: нет.

+2

@gdoron, почему? В конце концов, это объект. Он просто не раскрывает многое другое. ToString(), Equals(), GetHashCode() и т. Д. Все равно будут работать через динамический. Он просто не добавляет ничего другого. –

6

Anonymous тип является постоянным статическим типом в .NET, это просто, что вы не даете ему имя (компилятор, однако, делает). Вот почему кастинг до dynamic не будет работать. Однако, если у вас есть контроль над Foo(), вы можете создать и вернуть объект dynamic вместо анонимного, а затем ваш код будет работать. Это должно сделать трюк:

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

. Данные являются «объектами». Между «объектом» и «динамикой» мало различий, кроме как у потребителя (в котором он получает удовольствие). Я не верю, что изменение между «объектом» и «динамическим» (а затем назад к динамическому) будет здесь много. –

+0

@MarcGravell Я добавил код, чтобы уточнить, что я подразумеваю под * return dynamic * (я на самом деле имел в виду «построить динамический объект и вернуть его», а не просто «изменить тип возврата на динамический»). Спасибо, что поймали его - первоначальное редактирование действительно было непонятным. – dasblinkenlight

+1

Главное, что делает эту работу: ExpandoObject является общедоступным, а не внутренним (и, конечно же, реализует интерфейс IDynamicBlahBlahBlah в качестве публичного заявления). Тем не менее, это вызывает важный вопрос: является ли слой JSON, например, ExpandoObject? (это может быть * из-за использования IDictionary). Это также означает, что мы больше не используем анионный тип (вопрос) –

22

Этот блог имел рабочий ответ: http://blog.jorgef.net/2011/06/converting-any-object-to-dynamic.html - Спасибо @ Хорхе-Фиоранелли.

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 Динамический Осторожно! Благодаря @MarcGravell за указание, что анонимные объекты являются внутренними!

Подводя итог: Настройте отображение [assembly: InternalsVisibleTo("foo")], если вы хотите поделиться анонимным объектом с одной сборки на другую. В случае OP это будет вопрос об установке этого в проекте контроллера MVC, ссылаясь на тестовый проект . В моем конкретном случае, наоборот (поскольку я передаю анонимный объект из моего тестового проекта в проект «производственный код»).

Простейший способ использования этого «другого проекта», чтобы использовать его, - это, безусловно, бросить его на dynamic, а затем просто использовать свойства, как обычно. Это действительно работает, никаких проблем.

Итак, нижняя строка: я чувствую, что ответ Марка Гравелла немного неверен; это, безусловно, можно сделать.
(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))

Смежные вопросы