[英]asp.net mvc 3 razor view -> strongly typed List of tuple problem
我對asp.net MVC razor視圖有一個奇怪的問題。
我希望我的模型是List<Tuple<string, int, int, int, int>>
,這在我的其他c#方法中是完全有效的。 但是當我將它粘貼到@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>
}
我知道我可以創建一個類或一個結構來代替保存數據。 只是出於好奇而問
編輯:解決並向MVC團隊報告http://aspnet.codeplex.com/workitem/8652
編輯我已經刪除了一些正在進行中的評論 - 只是查看歷史記錄。
因此,您可以使用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
- 即截斷的泛型類型名稱截斷方式與您在代碼中觀察到的完全相同 。
在解析@inherits
或@model
關鍵字時,標准Razor解析器和Mvc Razor解析器都使用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
所做的事情之一是通過調用方法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#typename通過。
MVC團隊需要改變他們生成基類型的方式 - 他們可以使用public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments)
構造函數來獲取新的引用(而不是僅僅依賴.Add(span.BaseClass)
創建它),並解析泛型參數本身,因為他們知道類型名稱將是C#/ VB樣式 - 而不是語言中立.Net樣式與括號等作為實際名稱的一部分。
這已經在內部進行了一些討論,我擔心最終的產品是我們無法為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生成的代碼范圍,以及我們是否引入了額外的轉換層(如如果我們使用[]
甚至CodeTypeReference構造函數的其他重載,CodeDOM將會執行轉換,我們再也無法向編輯器做出這些保證,你會看到奇怪的行為
這樣就讓我們得到了解決方法,就是簡單地避免使用這么多泛型參數。 實際上有很多理由可以避免以這種方式使用Tuple,因為使用自定義模型類可以讓您命名所涉及的屬性,並且在添加屬性時會給您更大的靈活性(使用元組,您必須更新Controller和查看何時要為元組添加“屬性”。 話雖如此,我們仍然關注這個問題,看看我們如何在4.0之后做得更好。 既然我們是開源的,我們很樂意聽取您的建議,甚至接受您的代碼!
如果您有任何疑問,請隨時與我聯系(電子郵件在我的SO資料中)或繼續在評論中討論。 我只想給你一個你應得的背景背景,因為我已經把這么多優秀的工作用於跟蹤它!
-Andrew Nurse(Dev on Razor解析器)
我今天遇到了這個問題。 我正在返回有關用戶活動的一些信息,並試圖使用模型定義
@model List<Tuple<string,bool,DateTime,DateTime,DateTime>>
@* Name, Online, Created, Login, Active *@
原因是我厭倦了為視圖模型制作單一用法類,所以這就是我為簡單使用而這樣做的原因。 我收到了和你一樣的錯誤。 我試圖通過在@model
使用不同的元組組合來繞過錯誤,但無濟於事。
什么最終的工作是簡單地使用ViewBag
。 需要注意的是,無論如何ViewBag
將model
保存在ViewBag
,因此在此容量中使用它時沒有任何問題。
在我的actionresult方法中,我只是將元組列表分配給viewbag值
ViewBag.listTuple = listOfTuples;
然后在視圖中我投了回來
@{
List<Tuple<string,bool,DateTime,DateTime,DateTime>> tuples = ViewBag.listTuple;
}
就是這樣。 跑得很好。 我不是說這是一個完美的解決方案,但它是一個有效的解決方法 。
對於可能在Razor視圖中尋找CS1003錯誤源的其他人,CS1003錯誤是MVC解析Razor語法失敗的一般症狀。 它可能是由很多東西引起的,包括上面的通用元組問題。
我注意到的一件事是,如果您嘗試使用C#樣式的單行注釋來在Razor視圖中注釋模型聲明,則會出現此問題:
BAD: @model Int32 // user ID
GOOD: @* user ID *@
@model Int32
Visual Studio語法突出顯示不會將其標記為問題,但它將在運行時失敗。
只是想指出,如果你刪除了@model指令,你將不會有智能感知到哪個實際關心,但視圖與任意數量的元組參數完美配合。
我遇到了這個問題,因為我使用的是Tuple
Tuples
:
@model Tuple<Tuple<IEnumerable<int>, int, int>, Tuple<float, float, float>>
毋庸置疑,Razor並不喜歡這樣。 無論如何,我認為有趣的是,parses如何打破類型的總數,而不是頂層對象的類型數。
我最終得到了一個視圖模型,為內部元組創建了簡單的占位符:
public class ViewModel
{
public Tuple<IEnumerable<int>, int, int> Models { get; set; }
public Tuple<float, float, float> Selected { get; set; }
}
我很高興我制作了視圖模型。 它更具可讀性,並且(稍微)更容易在視圖內部進行操作和推理。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.