将泛型模型的子类传递给剃刀视图

时间:2013-03-26 20:31:51

标签: asp.net asp.net-mvc-3 oop razor

大图:

我发现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。回想一下,来自MyFooModelBaseFooModel<MyBarType>的herits中的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#类中工作的事实。我可以让我的剃刀视图@inherits c#视图,然后调用一些渲染方法。我不喜欢这个选项,因为我不喜欢有两种渲染html的方法。

还有其他想法吗?当我用FooBar给出类名时,我知道很难理解问题的上下文,但由于它有点敏感,所以我无法提供太多信息。我为此道歉。


到目前为止,使用本杰明的回答:

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
{
    // ...
}

3 个答案:

答案 0 :(得分:19)

您需要在类层次结构中引入一个带有covariant泛型类型参数的接口:

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>

答案 1 :(得分:1)

在ASP.NET MVC 2和MVC 3之间故意改变使用基于接口的模型。

You can see here

MVC团队:

基于接口的模型不是我们鼓励的(也没有,鉴于错误修复所施加的限制,可以实际支持)。切换到抽象基类可以解决问题。

“Scott Hanselman”

答案 2 :(得分:0)

您遇到的问题不是Razor错误,而是C#错误。尝试用类来做,你会得到同样的错误。这是因为模型不是BaseFooModel<BaseBarType>,而是BaseFooModel<MyFooModel>,并且两者之间不会发生隐式转换。通常,在程序中,您必须进行转换才能执行此操作。

然而,在.NET 4中,引入了contravariance and covariance,这听起来像是你正在寻找的能力。这只是一个.NET 4功能,老实说我不知道​​.NET 4中的Razor是否使用它。