12

大图:

我发现了 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
{
    // ...
}
4

3 回答 3

21

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

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>
于 2013-03-28T20:44:50.183 回答
2

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

你可以在这里看到

MVC 团队:

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

《斯科特·汉塞尔曼》

于 2013-03-31T08:40:25.910 回答
0

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

但是,在 .NET 4 中,引入了逆变和协变,这听起来像是您正在寻找的能力。这只是 .NET 4 的一项功能,老实说,我不知道 .NET 4 中的 Razor 是否使用它。

于 2013-03-26T20:56:02.437 回答