asp.net mvc 3 просмотр бритвы -> строго типизированный список проблем с кортежем - PullRequest
36 голосов
/ 23 мая 2011

У меня странная проблема с бритвой asp.net MVC.

Я хочу, чтобы моя модель была List<Tuple<string, int, int, int, int>>, что вполне допустимо в других моих методах c #. Но когда я вставляю его в объявление @model, кажется, что он выделяет только строковую часть кортежа. Так что у меня нет целых. Только item1.

Эта проблема не возникает, если я сделаю привязку к кортежу вместо списка.

Похоже, сгенерированный код неправильный, так что, возможно, это ошибка в представлении бритвы?

Ошибка, которую я получаю при компиляции:

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

Ответы [ 6 ]

34 голосов
/ 23 мая 2011

РЕДАКТИРОВАТЬ Я обрезал некоторые текущие комментарии здесь - просто просмотрите историю, чтобы увидеть.

Так что вы можете сделать эту работу с 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);    
}

Итак - версия с четырьмя параметрами проходит, но не версия с пятью параметрами.

И угадайте, какое фактическое значение составляет 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 :) Обновление ниже - не удержался.Теперь я знаю, где это неправильно

Итог

Вы не можете использовать дженерики с более чем 4 параметрами в выражениях Razor @inherits или @model (вменьше всего в C # - не знаю о VB).Похоже, что синтаксический анализатор Razor неправильно использует тип CodeTypeReference.

Окончательное обновление - или у меня есть кусочек между зубами:)

Одна из вещей, которые CodeTypeReference делаетэто вырезать информацию об имени сборки из переданного имени типа с помощью вызова метода CodeTypeReference.RipOffAssemblyInformationFromTypeName(string typeName).

И, конечно, если вы подумаете об этом - Tuple<T,T,T,T,T> точно так же, как имя типа с учетом сборки:С именем типа = Tuple<T, Assembly = T, Version = T, Culture = T, PublicKeyToken = T (если вы пишете действительно BAD C # парсер!).

Конечно же - если вы передадите Tuple<T,T,T,T,T,T> в качестве имени типа - вы на самом деле получите Tuple<T,T>.

Если заглянуть глубже в код, то он получит не зависящее от языка имя типа (например, обрабатывает '[', но ничего для '<'), так что на самом деле команда MVC должна не просто передаватьИмя типа C # прямо из нашего источника. </p>

Команда MVC должна изменить способ генерации базового типа - они могут использовать конструктор public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments) для новогоссылку (вместо того, чтобы просто полагаться на .Add(span.BaseClass), создающую его), и анализировать общие параметры сами, поскольку они знают, что имя типа будет в стиле C # / VB, а не в нейтральном для языка стиле .Net с квадратными скобками и т. д.имя.

8 голосов
/ 30 марта 2012

Это было через несколько волн обсуждения внутри компании, и я боюсь, что в конечном итоге мы не сможем это исправить в Razor 2.0 (MVC 4). Позвольте мне немного рассказать об этом.

Во-первых, важно отметить, что анализатор Razor преднамеренно анализирует C # как можно меньше. Основная причина этого - дать вам свободу C #, которую вы хотите, и мы не будем мешать. В результате (и вы можете убедиться в этом сами, проверив код!) Мы не анализируем имена типов в директивах @inherits и @model, мы просто выполняем до конца строки. Мы также являемся механизмом синтаксического анализа редактора Razor, что означает, что мы должны поддерживать частично завершенные операторы, такие как @model Foo<Bar, что технически недопустимо, но если бы вы вводили @model Foo<Bar>, это был бы ожидаемый промежуточный шаг, поэтому мы должны иметь возможность справиться с этим.

Теперь нам нужно подумать, что произойдет, мы решили изменить способ генерации кода. Как говорится в CodeTypeReference документации , нам нужно будет использовать синтаксис 1[[ ... ]] для определения универсального. Тем не менее, мы все равно будем просто вставлять то, что набрал пользователь, поэтому, если вы введете @model Foo<Bar>, мы скажем CodeDOM, что базовый тип был что-то вроде System.Web.Mvc.WebViewPage`1[[Foo<Bar>]]. Как вы можете видеть, мы все равно получаем <> в имени типа. В результате мы решили использовать тот факт, что CodeDOM, как правило, не жалуется на синтаксис <> и (Of ...) (в VB) для решения этой проблемы.

Даже анализ всего предоставленного вами имени типа будет затруднительным, учитывая, что нам придется обрабатывать неполные операторы, такие как @model Foo<Bar, Baz. Фактически, это также сделало бы редактор очень хрупким, так как редактор на самом деле зависит от того, сможем ли мы сказать им, какой именно диапазон текста Razor отображается в какой диапазон генерируемого кода на C # / VB, и если мы введем дополнительные слои перевода (например, как будет делать перевод CodeDOM, если мы используем [] или даже другие перегрузки конструктора CodeTypeReference), мы больше не можем давать такие гарантии редактору, и вы увидите странное поведение

Так что у нас остается обходной путь, который заключается в том, чтобы просто избегать использования такого количества общих аргументов. На самом деле существует ряд причин, по которым следует избегать использования Tuple таким образом, поскольку использование пользовательского класса модели позволит вам присвоить имена соответствующим свойствам и даст вам большую гибкость при добавлении свойств (с помощью кортежа вы должны обновить Controller и Посмотрите, когда вы хотите добавить «свойство» в ваш кортеж). Сказав это, мы следим за этой проблемой и смотрим, как мы можем лучше справиться с этим после 4.0. И теперь, когда мы с открытым исходным кодом, мы будем рады услышать ваши предложения и даже принять ваш код!

Пожалуйста, не стесняйтесь обращаться ко мне (адрес электронной почты указан в моем профиле SO) или продолжать обсуждать это в комментариях, если у вас есть вопросы. Я просто хотел дать вам фоновый контекст, который вы заслуживаете за то, что вложили так много отличной работы в отслеживание этого!

- Андрея Медсестра (анализатор Dev on Razor)

1 голос
/ 23 апреля 2016

Я столкнулся с этой проблемой сегодня. Я возвращал некоторую информацию о пользовательской активности и пытался использовать определение модели

@model List<Tuple<string,bool,DateTime,DateTime,DateTime>>
@* Name, Online, Created, Login, Active *@

Причина в том, что мне надоело делать одноразовые классы для моделей представления, поэтому я делаю это для простого использования. Я получил ту же ошибку, что и вы. Я попытался обойти ошибку, используя различные комбинации кортежей в @model, но безрезультатно.

То, что сделал , закончилось тем, что он просто использовал ViewBag. Отметим, что model в любом случае удерживается в ViewBag, поэтому нет никаких проблем с его использованием в этом качестве.

В моем методе actionresult я просто назначил список кортежей значению viewbag

ViewBag.listTuple = listOfTuples;

и затем в представлении, которое я отбрасываю назад

@{
    List<Tuple<string,bool,DateTime,DateTime,DateTime>> tuples = ViewBag.listTuple;
}

И это было так. Побежал просто отлично. Я не говорю, что это идеальное решение, но это рабочий обходной путь .

0 голосов
/ 11 мая 2017

Я столкнулся с этой проблемой, потому что использовал Tuple из Tuples:

@model Tuple<Tuple<IEnumerable<int>, int, int>, Tuple<float, float, float>>

Излишне говорить, что Razor это не понравилось.В любом случае, я думаю, что интересно, как анализ разбивается на общее количество типов, а не на количество типов в объекте верхнего уровня.

Мой обходной путь

В итоге я выбрал модель представления, которая просто создала заполнители для внутренних кортежей:

public class ViewModel
{
    public Tuple<IEnumerable<int>, int, int> Models { get; set; }
    public Tuple<float, float, float> Selected { get; set; }
}

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

0 голосов
/ 20 ноября 2013

Просто хотел отметить, что если вы удалите директиву @model, у вас не будет intellisense, который на самом деле заботится, но представление отлично работает с любым числом аргументов кортежа.

0 голосов
/ 13 июля 2012

Для любого другого, кто может приземлиться здесь в поисках источника ошибки CS1003 в представлении Razor, ошибка CS1003 является общим признаком сбоя MVC в синтаксическом анализе синтаксиса Razor.Это может быть вызвано многими причинами, в том числе общей проблемой кортежей, приведенной выше.

Одна вещь, которую я заметил, заключается в том, что эта проблема возникнет, если вы попытаетесь использовать однострочный комментарий в стиле C # для аннотирования объявления моделив представлении Razor:

BAD:  @model Int32 // user ID

GOOD: @* user ID *@
      @model Int32

Подсветка синтаксиса Visual Studio не помечает ее как проблему, но она завершится ошибкой во время выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...