簡體   English   中英

asp.net mvc 3 razor視圖 - >強類型列表的元組問題

[英]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.BaseTypesCodeTypeReferenceCollection - 而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 需要注意的是,無論如何ViewBagmodel保存在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM