Приведение анонимного типа к динамическому - PullRequest
40 голосов
/ 11 декабря 2011

У меня есть функция, которая возвращает анонимный тип, который я хочу проверить в своем контроллере 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);
}

Ответы [ 5 ]

36 голосов
/ 11 декабря 2011

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

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

Короче говоря: нет.

25 голосов
/ 07 марта 2013

В этом блоге был рабочий ответ: 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;
    }
}
7 голосов
/ 26 ноября 2013

По предложению @TrueWill и @Marc Gravell, которые также ссылались на это сообщение в блоге

Поскольку это для модульного тестирования, вы можете использовать InternalsVisibleTo. Смотрите, анонимные типы являются внутренними, C # 4.0, будьте осторожны! Спасибо @MarcGravell за указание на то, что анонимные объекты являются внутренними!

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

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

Итак, суть: я чувствую, что ответ Марка Гравелла немного неправильный; это явно можно сделать
( если указанные проекты могут быть изменены вами, поэтому вы можете соответствующим образом настроить отображение InternalsVisibleTo, и это не представляет проблемы по любой другой причине).

7 голосов
/ 11 декабря 2011

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

dynamic JsonResult Foo() {
    dynamic data = new ExpandoObject();
    data.details = "something";
    data.mode = "More";
    return Json(data);
}
1 голос
/ 27 февраля 2015

Вы можете использовать библиотеки NewtonSoft или Asp.net MVC:

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

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

...