web-dev-qa-db-ja.com

asp.net mvc3かみそりビュー->強く型付けされたタプル問題のリスト

Asp.netMVCレイザービューで奇妙な問題が発生しています。

モデルをList<Tuple<string, int, int, int, int>>にしたいのですが、これは他のc#メソッドで完全に有効です。しかし、それを@model宣言に貼り付けると、タプルの文字列部分しか選択されていないようです。だから私にはintがありません。 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.netmvcプロジェクトを作成します。新しいビューを作成します。次のコードを過ぎてください。

@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>
}

代わりに、データを保持するクラスまたは構造体を作成できることはわかっています。好奇心から尋ねるだけ

編集:ここで解決してMVCチームに報告しました http://aspnet.codeplex.com/workitem/8652

36
Oskar Kjellin

編集ここで進行中のコメントの一部を削除しました-履歴を表示するだけです。

したがって、これを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);    
}
_

つまり、4パラメータバージョンは合格しますが、5パラメータバージョンは合格しません。

そして、実際の値は_Tuple<T_であると推測します。つまり、切り捨てられたジェネリック型名は、コードで観察したのとまったく同じ方法で切り捨てられます

標準のRazorパーサーとMvcRazorパーサーはどちらも、_@inherits_または_@model_キーワードのいずれかを解析するときにCodeTypeReferenceCollectionタイプを使用します。コード生成中の_@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)である必要があります。なぜ壊れているのかを理解するのに十分な時間がありませんが、それはマイクロソフトの開発者の仕事だと思います:) 以下の更新-抵抗できませんでした。それは間違っています

結論

Razor _@inherits_または_@model_ステートメントのいずれかで4つを超えるパラメーターを持つジェネリックを使用することはできません(少なくともC#では-VBについてはわかりません)。 RazorパーサーがCodeTypeReferenceタイプを誤って使用しているようです。

最終更新-または、私は私の歯の間に少しありました:)

CodeTypeReferenceが行うことの1つは、メソッドCodeTypeReference.RipOffAssemblyInformationFromTypeName(string typeName)を呼び出して、渡された型名からアセンブリ名情報を取り除くことです。

そしてもちろん、あなたがそれについて考えるならば-_Tuple<T,T,T,T,T>_はアセンブリ修飾型名のようなものです:型名= _Tuple<T_、Assembly = T、Version = T、Culture = T、PublicKeyToken = T(本当に悪いC#パーサーを作成した場合!)。

案の定、タイプ名として_Tuple<T,T,T,T,T,T>_を渡すと、実際には_Tuple<T,T>_が得られます。

コードを詳しく見ると、言語に依存しない型名を受け取る準備ができています(たとえば、「[」を処理しますが、「<」は何も処理しません)。したがって、実際には、MVCチームはソースからC#型名を直接渡すだけではいけません使って。

MVCチームは、基本型の生成方法を変更する必要があります -public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments)コンストラクターを使用できます新しい参照の場合(.Add(span.BaseClass)の作成に依存するのではなく)、型名が言語に依存しない.NetスタイルではなくC#/ VBスタイルになることがわかっているため、汎用パラメーター自体を解析します。実際の名前の一部としての括弧など。

35
Andras Zoltan

これは現在、社内でいくつかの議論の波を経ており、最終的な製品として、Razor 2.0(MVC 4)でこれを修正することはできないと思います。推論の背景を少し説明しましょう。

まず、Razorパーサーが意図的にC#を解析することができるだけ少ないことに注意することが重要です。これの主な理由は、私たちが邪魔することなく、必要なC#に自由を与えることです。その結果(そしてコードをチェックすることでこれを自分で確認できます!)、@inheritsおよび@modelディレクティブの型名は解析せず、行の終わりまで実行するだけです。また、Razorエディターの背後にある解析エンジンでもあります。つまり、技術的に無効な@model Foo<Barのような部分的に完全なステートメントをサポートする必要がありますが、@model Foo<Bar>と入力すると、これは予想される中間ステップになるため、それを処理できるはずです。

ここで、コードの生成方法を変更することにしたときに何が起こるかを検討する必要があります。 CodeTypeReferenceドキュメント にあるように、ジェネリックを定義するには1[[ ... ]]構文を使用する必要があります。ただし、ユーザーが入力したものは何でも挿入するだけなので、@model Foo<Bar>と入力すると、基本型はSystem.Web.Mvc.WebViewPage`1[[Foo<Bar>]]のようなものであることがCodeDOMに通知されます。ご覧のとおり、タイプ名には<>が含まれています。その結果、CodeDOMは一般に<>および(Of ...)(VB)構文について文句を言わないという事実を使用して、この問題を回避することにしました。

@model Foo<Bar, Bazのような不完全なステートメントを処理する必要があることを考えると、指定した型名全体を解析することさえ困難です。実際、エディターは、R​​azorテキストのどの範囲がC#/ VBで生成されたコードのどの範囲にマップされているかを正確に伝えることができ、追加の翻訳レイヤーを導入するかどうかに依存しているため、エディターは非常に脆弱になります( []またはCodeTypeReferenceコンストラクターの他のオーバーロードを使用した場合にCodeDOMの変換が行われるため、エディターに対してこれらの保証を行うことができなくなり、奇妙な動作が見られます。

そのため、回避策が残ります。これは、この多くの一般的な引数の使用を単純に回避することです。カスタムモデルクラスを使用すると、関連するプロパティに名前を付けることができ、プロパティを追加する際の柔軟性が向上するため、この方法でタプルを使用しないようにする理由は実際にはいくつかあります(タプルでは、​​コントローラーを更新する必要があります。タプルに「プロパティ」を追加する場合に表示します)。そうは言っても、私たちはこの問題に目を光らせており、4.0以降でこれを使ってより良い仕事をする方法を検討しています。そして今、私たちはオープンソースになっているので、あなたの提案を聞いて、あなたのコードを受け入れることさえ喜んでいます!

私に連絡することを躊躇しないでください(電子メールは私のSOプロファイルにあります)または質問がある場合はコメントでこれについて議論し続けてください。私はあなたが置くに値する背景の文脈をあなたに与えたかっただけですこれを追跡するための非常に優れた作業です!

-アンドリューナース(かみそりパーサーの開発者)

私は今日この問題に遭遇しました。ユーザーアクティビティに関する情報を返していましたが、次のモデル定義を使用しようとしました。

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

その理由は、ビューモデルのシングルユースクラスを作成するのに少しうんざりしているため、単純な使用のためにこれを行う理由です。私はあなたと同じエラーを受け取りました。 @modelでタプルのさまざまな組み合わせを使用してエラーを回避しようとしましたが、役に立ちませんでした。

didが機能するようになったのは、単にViewBagを使用することでした。とにかくmodelViewBagに保持されているので、この容量で使用しても問題はありません。

Actionresultメソッドでは、タプルのリストをビューバッグ値に割り当てるだけです。

ViewBag.listTuple = listOfTuples;

そして、ビューで私は私をキャストバックします

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

そしてそれはそれでした。うまく走った。これが完璧な解決策だと言っているわけではありませんが回避策です

1
Travis J

TupleTuplesを使用していたため、この問題が発生しました。

@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
seebiscuit

@modelディレクティブを削除すると、実際に誰が気にするかというインテリセンスがなくなりますが、ビューは任意の数のタプル引数で完全に機能することを指摘したいと思います。

0
Bart Calixto

RazorビューでCS1003エラーの原因を探してここにたどり着く可能性のある他の人にとって、CS1003エラーは、MVCがRazor構文を解析できないことの一般的な症状です。上記の一般的なタプルの問題など、さまざまな原因が考えられます。

私が気づいたことの1つは、C#スタイルの単一行コメントを使用してRazorビューでモデル宣言に注釈を付けようとすると、この問題が発生することです。

BAD:  @model Int32 // user ID

GOOD: @* user ID *@
      @model Int32

Visual Studioの構文の強調表示は問題としてフラグを立てませんが、実行時に失敗します。

0
Peter Gluck