2013-03-26 54 views
10

的大图:通子类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>MyBarType遗址BaseBarType继承。

我曾尝试:

我非剃刀地尝试了这一点,看看是否也是如此,它是。我不得不在视图中使用模板参数来使其工作。这里是一个非Razor视图:

public class FooView<T> 
    where T : BaseBarType 
{ 
    BaseFooModel<T> Model; 
    public FooView(BaseFooModel<T> model) 
    { 
     Model = model; 
    } 
} 

采用该结构,下面确实工作

new FooView<MyBarType>(new MyFooModel()); 

我的问题:

我怎么能做到这一点与剃刀?如何通过FooView这样的类型?
我不能,但没有解决这个办法吗?我能否以某种方式达到同样的体系结构?

让我知道我是否可以提供更多信息。我使用.NET 4.0和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 
{ 
    // ... 
} 
+0

往往我找到我已经打完了问题,或者至少后不久的时间回答。这次不行...... – lbstr 2013-03-26 20:46:30

+1

什么是你BaseFooModel - 它有什么在页面上做(插入,只是观看,是它改变等)?这是相关的(一些示例代码) – NSGaga 2013-03-28 21:29:30

+0

这是一个很好的点,但我认为,因为此信息在视图中使用它应该只得到读取。任何更新都应该在控制器操作方法中进行。 – 2013-03-28 21:34:04

回答

17

您需要引入一个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> 
+0

谢谢Ben!到目前为止,这工作得非常好。在接受之前,我会坚持下去,直到我确信它会适用于我的情况。 +1 – lbstr 2013-03-28 22:07:17

+0

非常感谢!我的代码中有类似的问题,所以我很高兴能够使用它来帮助别人。如果您遇到任何问题,请发布一些更多信息,如果可以的话,我会尽力提供帮助。 – 2013-03-28 22:09:55

+0

+1我并没有试图破坏你 - 我不是那么饿 - 例如,乔恩是:) – NSGaga 2013-03-28 22:37:56

0

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

然而,随着.NET 4中,介绍了contravariance and covariance,这听起来像你正在寻找的东西的能力。这只是一个.NET 4特性,而且我真的不知道.NET 4中的Razor是否使用它。

+0

谢谢Brian的反馈。你是对的;我的确看到了与类相同的东西,这正是我在上面用'FooView '位解释的东西。添加一个模板参数是我能够实现它的唯一方法,这显然不会转化为Razor。你可以解释一下你说的是什么意思,列表不是'IEnumerable ',而是'IEnumerable '。什么名单? – lbstr 2013-03-26 23:06:01

+0

什么'IEnumerable'?该模型应该只是一个'MyFooModel',而不是一个集合。 – lbstr 2013-03-27 15:39:26

+0

错误,我真的关闭了,我的意思是BaseFooModel,所以我通过上面的帖子更新。 – 2013-03-27 16:34:21

1

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

You can see here

MVC团队:

拥有基于接口的模型并不是我们鼓励的(也没有考虑到错误修复带来的限制可以切实支持)。切换到抽象基类将解决问题。

“斯科特Hanselman的”