Asp.net mvc 3 razor view → строго типизированный Список проблем с кортежем
У меня есть странная проблема с видом бритвы asp.net MVC.
Я хочу, чтобы моя модель была List<Tuple<string, int, int, int, int>>
, что совершенно верно в моих других методах С#. Но когда я вставляю его в объявление @model
, кажется, что он выбирает только строковую часть кортежа. Поэтому у меня нет инт. Только item1.
Эта проблема отсутствует, если я привязываю ее к кортежу вместо List.
Кажется, что сгенерированный код ошибочен, возможно, это ошибка в виде бритвы?
Ошибка при компиляции:
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS1003: Syntax error, '>' expected
Source Error:
Line 27:
Line 28:
Line 29: public class _Page_Views_Dashboard_DestinationStatistics_cshtml : System.Web.Mvc.WebViewPage<List<Tuple<string {
Line 30:
Line 31: #line hidden
Чтобы изолировать эту проблему, я сделал следующее:
Создайте пустой проект asp.net mvc. Создайте новое представление. Прочтите следующий код.
@model List<Tuple<string, int, int, int, int>>
@foreach (var stat in Model)
{
<tr>
<td>
@stat.Item1
</td>
<td>
@stat.Item2
</td>
<td>
@stat.Item3
</td>
<td>
@stat.Item4
</td>
<td>
@stat.Item5
</td>
</tr>
}
Я знаю, что могу просто создать класс или структуру для хранения данных. Просто прошу из любопытства
EDIT:
Решено и сообщено команде MVC здесь http://aspnet.codeplex.com/workitem/8652
Ответы
Ответ 1
РЕДАКТИРОВАТЬ. Я отменил некоторые из текущих комментариев здесь - просто просмотрите историю, чтобы увидеть.
Итак, вы можете сделать эту работу с 1, 2, 3 или 4 кортежами общих параметров, но она не работает с 5. Как только вы используете 5 параметров, он генерирует такой код:
public class _Page_Views_Home_Index_cshtml :
System.Web.Mvc.WebViewPage<List<System.Tuple<string {
Я хотел просто узнать, ограничено ли это ограничение длины символов, поэтому я создал класс следующим образом:
namespace ASP{ //same namespace that the backend code for the page is generated
public class T { }
}
И изменилось объявление модели:
@model List<Tuple<T,T,T,T,T>>.
В конце (см. историю) я добрался до
@inherits System.Web.Mvc.WebViewPage<Tuple<T,T,T,T,T>>
Такая же проблема! Это не проблема с ключевым словом @model...
Потребовалось некоторое время (чтение через источник MVC3 и Razor, добавление нескольких тестов к этому решению), но вот тест, который показывает, почему мы получаем эту ошибку:
[TestMethod]
public void TestMethod()
{
System.CodeDom.CodeTypeReferenceCollection c =
new CodeDom.CodeTypeReferenceCollection();
c.Add("Tuple<T,T,T,T>");
c.Add("Tuple<T,T,T,T,T>");
//passes
Assert.AreEqual("Tuple<T,T,T,T>", c[0].BaseType);
//fails
Assert.AreEqual("Tuple<T,T,T,T,T>", c[1].BaseType);
}
Итак - выполняется четырехпараметрическая версия, но не версия с 5 параметрами.
И предположим, что - фактическое значение Tuple<T
- то есть усеченное общее имя типа усечено точно так же, как вы наблюдали в своем коде.
Как стандартный синтаксический анализатор Razor, так и анализатор Mvc Razor используют тип CodeTypeReferenceCollection
при разборе ключевого слова @inherits
или @model
. Вот код для @inherits
при генерации кода:
protected internal virtual void VisitSpan(InheritsSpan span) {
// Set the appropriate base type
GeneratedClass.BaseTypes.Clear();
GeneratedClass.BaseTypes.Add(span.BaseClass);
if (DesignTimeMode) {
WriteHelperVariable(span.Content, InheritsHelperName);
}
}
GeneratedClass.BaseTypes
является CodeTypeReferenceCollection
- и span.BaseClass
является строкой. После этого через ILSpy метод нарушения должен быть закрытым методом CodeTypeReference.Initialize(string typeName, CodeTypeReferenceOptions options)
. Мне не хватает времени, чтобы понять, почему это ломается - но тогда я думаю, что работа разработчика Microsoft: Обновление ниже - не удержалось. Теперь я знаю, где это неправильно
Нижняя строка
Вы не можете использовать generics с более чем 4 параметрами в операторах Razor @inherits
или @model
(по крайней мере, на С# - не знаете о VB). Похоже, что парсер Razor неправильно использует тип CodeTypeReference
.
Окончательное обновление - или у меня был бит между зубами:)
Одна из вещей, которые выполняет CodeTypeReference
, - это удаление информации об имени сборки из имени прошедшего типа с вызовом метода CodeTypeReference.RipOffAssemblyInformationFromTypeName(string typeName)
.
И, конечно, если вы думаете об этом - Tuple<T,T,T,T,T>
точно так же, как имя типа сборки: Тип type = Tuple<T
, Assembly = T
, Version = T
, Culture = T
, PublicKeyToken = T
(если вы пишете действительно BAD С# parser!).
Конечно же, если вы перейдете в Tuple<T,T,T,T,T,T>
в качестве имени типа, вы получите Tuple<T,T>
.
Глядя глубже в код, он загружается, чтобы получить нейтральное по отношению к языку имя типа (обрабатывает '[', но ничего для '<', например), поэтому на самом деле команда MVC не должна просто передавать С# typename из нашего источника прямо.
Команда MVC должна изменить способ генерации базового типа. Они могут использовать конструктор public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments)
для новой ссылки (вместо того, чтобы просто полагаться на создание .Add(span.BaseClass)
), и проанализируйте сами общие параметры, так как они знают, что имя типа будет С#/VB, а не языковым нейтральным .Net-стилем с скобками и т.д. как часть фактического имени.
Ответ 2
Это было через несколько волнений обсуждения внутри, и я боюсь, что конечный продукт заключается в том, что мы не сможем исправить это для Razor 2.0 (MVC 4). Позвольте мне немного рассказать о рассуждениях.
Во-первых, важно отметить, что анализатор Razor намеренно анализирует С# как можно меньше. Основная причина этого - предоставить вам свободу для С#, которую вы хотите, без нашего мешания. В результате (и вы можете проверить это самостоятельно, проверив код!), Мы не разбираем имена типов в директивах @inherits
и @model
, мы просто запускаем их до конца строки. Мы также являемся механизмом синтаксического разбора редактора Razor, что означает, что мы должны поддерживать частично полные утверждения типа @model Foo<Bar
, которые технически недействительны, но если бы вы набрали @model Foo<Bar>
, это был бы ожидаемый промежуточный шаг, поэтому мы должны иметь возможность обрабатывайте его.
Теперь нам нужно подумать о том, что произойдет, и мы решили изменить способ генерации кода. Как указано CodeTypeReference documentation, нам нужно будет использовать синтаксис 1[[ ... ]]
для определения общего. Тем не менее, мы все равно будем вводить все, что пользователь вводил, поэтому, если вы набрали @model Foo<Bar>
, мы бы сказали CodeDOM, что базовый тип был чем-то вроде System.Web.Mvc.WebViewPage`1[[Foo<Bar>]]
. Как вы можете видеть, мы по-прежнему получаем <>
в имени типа. В результате мы приняли решение использовать тот факт, что CodeDOM обычно не жалуется на синтаксис <>
и (Of ...)
(в VB), чтобы взломать наш путь вокруг этой проблемы.
Даже синтаксический анализ всего имени типа, который вы предоставили, будет затруднен с учетом того, что нам придется обрабатывать неполные утверждения типа @model Foo<Bar, Baz
. Фактически, это также сделает редактор очень хрупким, так как редактор фактически зависит от того, насколько мы можем точно сказать, какой диапазон текста Razor сопоставляется с тем, какой диапазон С#/VB сгенерировал код, и если мы вводим дополнительные слои перевода (такие поскольку код CodeDOM будет выполняться, если мы используем []
или даже другие перегрузки конструктора CodeTypeReference), мы больше не можем делать эти заверения редактору, и вы увидите странное поведение
Итак, это оставляет нам обходной путь, который заключается в том, чтобы просто избегать использования этого множества общих аргументов. На самом деле существует множество причин избежать использования Tuple таким образом, поскольку использование пользовательского класса модели позволит вам назвать связанные с ним свойства и даст вам большую гибкость при добавлении свойств (с кортежем вам необходимо обновить контроллер и Просмотрите, когда вы хотите добавить "свойство" к вашему кортежу). Сказав это, мы следим за этой проблемой и смотрим, как мы можем улучшить работу с этим после 4.0. И теперь, когда мы с открытым исходным кодом, мы будем рады услышать ваши предложения и даже принять ваш код!
Пожалуйста, не стесняйтесь обращаться ко мне (электронная почта находится в моем профиле SO) или продолжать обсуждать это в комментариях, если у вас есть вопросы. Я просто хотел дать вам фоновый контекст, который вы заслуживаете за то, что поставили так много отличной работы в отслеживание этого!
-Андрю-медсестра (Dev on Razor parser)
Ответ 3
Сегодня я столкнулся с этой проблемой. Я возвращал некоторую информацию об активности пользователя и попытался использовать определение модели
@model List<Tuple<string,bool,DateTime,DateTime,DateTime>>
@* Name, Online, Created, Login, Active *@
Причина в том, что я устал делать одноразовые классы для моделей viewmodels, поэтому я делаю это для простого использования. Я получил ту же ошибку, что и вы. Я попытался обойти ошибку, используя разные комбинации кортежей в @model
, но безрезультатно.
В итоге работа была простой: ViewBag
. Следует отметить, что model
хранится в ViewBag
в любом случае, поэтому в этой емкости нет никаких проблем.
В моем методе actionresult я просто присвоил список кортежей значению viewbag
ViewBag.listTuple = listOfTuples;
а затем в представлении я отбрасываю назад
@{
List<Tuple<string,bool,DateTime,DateTime,DateTime>> tuples = ViewBag.listTuple;
}
И это было так. Просто отлично. Я не говорю, что это идеальное решение, но это рабочий обход.
Ответ 4
Для всех, кто может приземлиться здесь, для поиска ошибки CS1003 в представлении Razor, ошибка CS1003 является общим симптомом сбоя MVC для синтаксиса синтаксиса Razor. Это может быть вызвано многими вещами, включая общую проблему с кортежем выше.
Одна вещь, которую я заметил, заключается в том, что эта проблема возникнет, если вы попытаетесь использовать однострочный комментарий в стиле С# для аннотации объявления модели в представлении Razor:
BAD: @model Int32 // user ID
GOOD: @* user ID *@
@model Int32
Подсветка синтаксиса Visual Studio не будет помечать ее как проблему, но она будет терпеть неудачу во время выполнения.
Ответ 5
Просто хотел указать, что если вы удалите директиву @model, у вас не будет intellisense, который на самом деле кого это волнует, но представление отлично работает с любым количеством аргументов кортежа.
Ответ 6
Я столкнулся с этой проблемой, потому что использовал Tuple
Tuples
:
@model Tuple<Tuple<IEnumerable<int>, int, int>, Tuple<float, float, float>>
Излишне говорить, что Бритву это не понравилось. Во всяком случае, мне кажется, что интересно, как анализы разбиваются на общее количество типов, а не на количество типов объектов верхнего уровня.
Мое обходное решение
Я закончил с моделью представления, что простые созданные заполнители для внутренних кортежей:
public class ViewModel
{
public Tuple<IEnumerable<int>, int, int> Models { get; set; }
public Tuple<float, float, float> Selected { get; set; }
}
Я счастлив, что сделал модель взгляда. Это более читаемо и (слегка) легче манипулировать и рассуждать внутри представления.