Asp.net 将泛型模型的子类传递给razor视图
大局: 我发现了剃须刀的一个缺陷,我很难想出一个好办法来解决它 玩家: 假设我有一个这样的模型:Asp.net 将泛型模型的子类传递给razor视图,asp.net,asp.net-mvc-3,oop,razor,Asp.net,Asp.net Mvc 3,Oop,Razor,大局: 我发现了剃须刀的一个缺陷,我很难想出一个好办法来解决它 玩家: 假设我有一个这样的模型: public abstract class BaseFooModel<T> where T : BaseBarType { public abstract string Title { get; } // ACCESSED BY VIEW public abstract Table<T> BuildTable(); protected Tab
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
{
// ...
}
// FooView.cshtml
@model BaseFooModel<BaseBarType>
但是,这不起作用。我得到一个运行时错误,表示FooView
期望BaseFooModel
但得到MyFooModel
。回想一下,herits中的MyFooModel
和MyBarType
继承自BaseBarType
我尝试过的:
我在非剃须刀之地尝试过这一点,看看是否也是如此,事实确实如此。我必须在视图中使用模板参数才能使其工作。以下是非剃须刀视图:
public class FooView<T>
where T : BaseBarType
{
BaseFooModel<T> Model;
public FooView(BaseFooModel<T> model)
{
Model = model;
}
}
什么起作用了:
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
{
// ...
}
公共接口IFooModel
其中T:BaseBarModel
{
字符串标题{get;}
BaseModule表{get;}//表继承自BaseModule
//我只需要BaseModule中的方法
//在我看来。
}
公共抽象类BaseFooModel:IFooModel
其中T:BaseBarModel
{
// ...
}
您遇到的问题不是Razor错误,而是C#错误。在类中尝试这样做,您将得到相同的错误。这是因为该模型不是BaseFooModel
,而是BaseFooModel
,并且不能在两者之间进行隐式转换。通常,在程序中,您必须进行转换才能做到这一点
然而,随着.NET4的引入,was听起来像是您正在寻找的功能。这只是.NET 4的一项功能,我真的不知道.NET 4中的Razor是否使用了它。您需要在类层次结构中引入一个带有泛型类型参数的接口:
public interface IFooModel<out T> where T : BaseBarType
{
}
最后,将视图的模型参数更新为:
@model IFooModel<BaseBarType>
@model-IFooModel
使用基于接口的模型是在ASP.NET MVC 2和MVC 3之间故意更改的
MVC团队:
我们不鼓励使用基于接口的模型(考虑到bug修复所施加的限制,我们也不能实际支持)。切换到抽象基类可以解决这个问题
“Scott Hanselman”我经常在我输入问题时或至少在不久之后找到答案。这次不是……你的BaseFoodModel是什么?它在页面上做了什么(插入、只是查看、是否更改等)?这是相关的(一些示例代码),这是一个很好的观点,但我认为,因为这些信息是在视图中使用的,所以应该只被读取。任何更新都应该发生在控制器操作方法中。@NSGaga要回答您的问题,请参阅上面Benjamin的评论。他是对的。只是一些子类必须实现的getter和方法。@Benjamin您的评论甚至更好:)我认为(有意义)-答案值得赏金-但我正在寻找我们是否可以改进它。i、 e.细节与“差异”形成整体差异
IEnumerable
适用于简单情况(当然没有“标题”等)。如果基本接口必须支持任何东西——或者通过js或其他方式——那么这可能会失败。感谢您的反馈。你是对的;我确实在类中看到了同样的情况,这就是我在上面用FooView
位试图解释的。添加模板参数是我让它工作的唯一方法,这显然不能转化为Razor。你能解释一下你所说的列表不是IEnumerable
,而是IEnumerable
的意思吗。什么列表?什么IEnumerable
?模型应该只是一个MyFooModel
,而不是一个集合。呃,我真的不喜欢,我指的是BaseFooModel,所以我在上面的帖子中进行了更新。两个视图(razor和FooView
)都设置为作为模型接收BaseFooModel
。在这两种情况下,我都在传递MyFooModel
,这是一个BaseFooModel
。似乎唯一可行的方法是将MyBarType
作为模板参数传递给视图。但是,因为我不能在razor视图上定义模板参数,所以我被卡住了。对,razor视图不能灵活地使用BaseBarType,您必须在.NET 4中使用MyBarType,而不进行逆变换和协方差隐式转换(即使这样,我也不知道这是否可行)。这是一个不幸的限制。谢谢你,本!到目前为止,这是非常有效的。在接受之前,我会坚持下去,直到我确信它在我的情况下会起作用+很高兴听到这个!我的代码中也有类似的问题,所以我很高兴能够用它来帮助其他人。如果您遇到任何问题,请发布更多信息,如果可以的话,我会尽力帮助您。+1我没有试图破坏您-我没有那么饿-例如Jon:)我更新了我的问题,提供了更多关于BaseFooModel
内部内容的详细信息。我将string Title
和Table Table
移动到界面上,因为视图可以访问它们。我现在得到一个无效的方差错误('T'必须在IFooModel.Table
上保持不变有效)。你能解释一下原因吗?什么不能使其无效?您的接口是协变的,因此必须是只读的。确保接口只有只读属性(getter,但没有setter),并且没有接受T类型参数的方法。如果参数使用T参数,则参数也必须是只读的。
public abstract class BaseFooModel<T> : IFooModel<T> where T : BaseBarType
{
}
[HttpGet]
public ActionResult Index()
{
return View(new MyFooModel());
}
@model IFooModel<BaseBarType>