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>