將通用模型的子類傳遞給剃刀視圖
大圖:
我發現了 Razor 的一個限制,我很難想出一個好的方法來解決它。
玩家們:
假設我有一個這樣的模型:
public abstract class BaseFooModel<T> where T : BaseBarType { public abstract string Title { get; } // ACCESSED BY VIEW public abstract Table<T> BuildTable(); protected Table<T> _Table; public Table<T> Table // ACCESSED BY VIEW { get { if (_Table == null) { _Table = BuildTable(); } return _Table; } } }還有一個像這樣的子類:
public class MyFooModel : BaseFooModel<MyBarType> { // ... } public class MyBarType : BaseBarType { // ... }我希望能夠傳遞
MyFooModel到這樣定義的剃刀視圖:// FooView.cshtml @model BaseFooModel<BaseBarType>但是,這是行不通的。我收到一個執行時錯誤,說
FooView期望BaseFooModel<BaseBarType>但得到MyFooModel. 回想一下,MyFooModel在繼承自BaseFooModel<MyBarType>和MyBarType繼承自BaseBarType。我試過的:
我在非剃須刀的土地上嘗試過這個,看看是否也是如此,它是。我必須在視圖中使用模板參數才能使其工作。這是非剃刀視圖:
public class FooView<T> where T : BaseBarType { BaseFooModel<T> Model; public FooView(BaseFooModel<T> model) { Model = model; } }使用該結構,以下操作確實有效:
new FooView<MyBarType>(new MyFooModel());我的問題:
**我怎麼能用 Razor 做到這一點?**我怎樣才能傳遞我正在使用的類型
FooView?我不能,但是有什麼辦法解決這個問題嗎?我能以某種方式實現相同的架構嗎?
讓我知道我是否可以提供更多資訊。我正在使用 .NET 4 和 MVC 3。
編輯:
現在,我只是為
BaseFooModel<BaseBarType>. 我對此並不感到興奮,因為我不想每次添加新模型時都必須創建新視圖。另一種選擇是利用這樣一個事實,即我能夠在沒有剃刀的情況下在正常 c# 類中使用它。我可以讓我的剃刀查看
@inheritsc# 視圖,然後呼叫一些渲染方法。我不喜歡這個選項,因為我不喜歡有兩種呈現 html 的方式。還有其他想法嗎?我知道當我用
Fooand給出類名時很難理解問題的上下文Bar,但我不能提供太多資訊,因為它有點敏感。對此我深表歉意。到目前為止,我使用本傑明的回答:
public interface IFooModel<out T> where T : BaseBarModel { string Title { get; } Table<T> Table { get; } // this causes an error: // Invalid variance: The type parameter 'T' must be // invariantly valid on IFooModel<T>.Table. // 'T' is covariant. } public abstract class BaseFooModel<T> : IFooModel<T> where T : BaseBarModel { // ... }什麼最終起作用:
public interface IFooModel<out T> where T : BaseBarModel { string Title { get; } BaseModule Table { get; } // Table<T> inherits from BaseModule // And I only need methods from BaseModule // in my view. } public abstract class BaseFooModel<T> : IFooModel<T> where T : BaseBarModel { // ... }
您需要在類層次結構中引入一個帶有協變泛型類型參數的介面:
public interface IFooModel<out T> where T : BaseBarType { }並從上述介面派生您的 BaseFooModel 。
public abstract class BaseFooModel<T> : IFooModel<T> where T : BaseBarType { }在您的控制器中:
[HttpGet] public ActionResult Index() { return View(new MyFooModel()); }最後,將視圖的模型參數更新為:
@model IFooModel<BaseBarType>
在 ASP.NET MVC 2 和 MVC 3 之間使用基於介面的模型是故意改變的。
MVC 團隊:
我們不鼓勵使用基於介面的模型(考慮到錯誤修復所施加的限制,我們也不可以實際支持)。切換到抽象基類可以解決這個問題。
《斯科特·漢塞爾曼》