Подтвердить что ты не робот

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

4b9b3361

Ответ 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; }
}

Я счастлив, что сделал модель взгляда. Это более читаемо и (слегка) легче манипулировать и рассуждать внутри представления.