Динамически типизированный ViewPage
Возможно ли это? Вот что я пытаюсь:
public ActionResult Index()
{
dynamic p = new { Name = "Test", Phone = "111-2222" };
return View(p);
}
И тогда мое представление наследует от System.Web.Mvc.ViewPage<dynamic>
и пытается распечатать Model.Name.
Я получаю сообщение об ошибке: '< > f__AnonymousType1.Name' недоступен из-за уровня защиты
Итак, в основном, это то, что я пытаюсь сделать, просто невозможно? Почему или почему нет?
Обновление: здесь мой взгляд
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<asp:Content ...>
<%=Model.Name%>
<%=Model.Phone%>
</asp:Content>
Конструктор View встроен в фреймворк.
Ответы
Ответ 1
Неизвестные типы не могут быть возвращены методом; они действительны только в рамках метода, в котором они определены.
Вы должны использовать класс модели, который вы ранее определили, и передать это в свой вид. Нет ничего плохого в передаче класса Model, который не имеет каждого определенного поля.
Обновление
Я думаю, что раньше был неправ. Это должно сработать. Возможно, проблема находится в представлении. Можете ли вы разместить больше кода? Особенно View и его конструктор.
Обновить второе:
Хорошо, я ошибся в передаче анонимного типа другому методу для использования в качестве динамической переменной - это можно сделать.
Но я также ошибался в своем убеждении, что то, что вы пытаетесь сделать, будет работать. К сожалению, для вас это не так. Проблема в том, что вы используете ViewPage<TModel>
, который использует ViewDataDictionary<TModel>
внутренне. Поскольку они требуют сильных типов, вы не сможете использовать с ними динамические объекты. Внутренняя структура не использует внутреннюю динамику и указывает динамику по мере того, как тип терпит неудачу.
Для чего нужен класс DynamicViewPage
и соответствующий класс DynamicViewDataDictionary
, которые принимают object
и сохраняют его внутренне как динамическое. Затем вы можете использовать анонимный тип и передать его в свои представления.
Тем не менее, вы ничего не выиграете. Вы могли бы указать свои Представления, как вы это делали (т.е. <%=Model.Name%>
), но вы не получили бы сильную типизацию. Не было бы никакой интеллигенции и не было бы никакой безопасности типа. Вы бы так же хорошо использовали нетипизированный ViewDataDictionary
, как предлагает @Dennis Palmer.
Это был интересный (и, к сожалению, для меня, поглощающий) мысленный эксперимент, но я думаю, в конечном счете, что этого не произойдет. Либо объявите публичный тип и передайте его в свои представления, либо используйте нетипизированный словарь.
Ответ 2
Какую пользу вы надеялись получить от использования динамического типа здесь?
Использование словаря ViewData - очень простой способ добавить к вашему представлению любые объекты/элементы.
Вам не нужно отражать, чтобы получить имена свойств в вашем представлении. Просто используйте ViewData.Keys
, чтобы получить коллекцию имен.
Изменить: Я только немного узнал о динамике, и я думаю, возможно, вам нужно создать свой собственный класс динамических объектов, который наследуется от DynamicObject. Вы захотите иметь частный словарь в этом классе, а затем переопределить TrySetMember
и TryGetMember
.
Редактировать в стороне: Я думаю, что одно преимущество сильно типизированного ViewModel заключается в том, что вы можете принять его как параметр в методах POST-действия. Структура MVC будет обрабатывать привязку модели, и в методе действий у вас просто есть экземпляр класса ViewModel. Я не думаю, что у вас будет такое преимущество при динамике, даже если они будут работать.
Изменить результат: Ну, я попытался использовать класс, полученный из DynamicObject, но VS2010 падает, когда он пытается отобразить представление. Я не получаю никаких исключений, просто жесткий сбой и перезагрузка Visual Studio. Здесь код, который я придумал, вызывает сбой.
Пользовательский динамический класс:
public class DynViewModel : DynamicObject
{
private Dictionary<string, object> ViewDataBag;
public DynViewModel()
{
this.ViewDataBag = new Dictionary<string, object>();
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
this.ViewDataBag[binder.Name] = value;
return true;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = this.ViewDataBag[binder.Name];
return true;
}
}
В контроллере:
public ActionResult DynamicView()
{
dynamic p = new DynamicViewModel.Models.DynViewModel();
p.Name = "Test";
p.Phone = "111-2222";
return View(p);
}
Мой взгляд в основном совпадает с тем, что указано в вопросе:
<p>Name: <%=Model.Name %></p>
<p>Phone: <%=Model.Phone %></p>
Мое заключение: Это может сработать, но в бета-версии 1 VS2010 я не могу понять, почему мой код заставляет Visual Studio сбой. Я попробую еще раз в VS2010 Beta 2, когда он будет выпущен, потому что это интересное упражнение в изучении динамики. Однако, даже если это будет работать, я по-прежнему не вижу никакого преимущества перед использованием словаря ViewData.
Фил Хаак на помощь! Вот сообщение в блоге Phil Haack, которое может помочь вам. Похоже, это то, что вы искали. Fun With Method Missing и С# 4
Ответ 3
Фактическая ошибка здесь (<>f__AnonymousType1.Name' is inaccessible due to its protection level
) является результатом использования анонимных типов. Анонимные типы неявно internal
(по крайней мере, на С#), поэтому их можно получить только из обычной сборки. Поскольку ваше представление скомпилировано в отдельную сборку во время выполнения, оно не может получить доступ к анонимному типу internal
.
Решение состоит в том, чтобы передать конкретные/названные классы в качестве моделей для вашего представления. В самом представлении можно использовать dynamic
, если хотите.
Ответ 4
В .NET 4.0 Анонимные типы могут быть легко преобразованы в ExpandoObjects, и, следовательно, все проблемы устраняются с накладными расходами самого преобразования.
Проверьте здесь