ASP.NET MVC - 通用控制器模型问题和多态性

时间:2012-11-01 16:47:57

标签: asp.net-mvc asp.net-mvc-3 asp.net-mvc-2 asp.net-mvc-4 polymorphism

设定:

我设计了一个向导(最初基于Pro ASP.NET MVC 2中的Steve Sanderson的向导)。

基本上,基本向导控制器声明如下:

public abstract class WizardController<TModel> : Controller where TModel : class, IWizardModel, new()
{
   // Loads-n-loadsa-code
}

因此,要实现我的向导,我需要按如下方式声明我的向导控制器:

[WizardOptions(StartLabel="Edit >>")]
public partial class EditFeeEarnerController : WizardController<MyApp.Models.MySpecificWizardModel>
{
     // small amounts of highly intuitive code
}

MyViewModel实施IWizardModel

到目前为止一切顺利。这个向导工作得很漂亮,我很喜欢它,所以不要挂断它。

问题如下:

问题:

唯一的问题是我的向导使用每个步骤的部分视图,它与视图(Wizard.cshtml)一起“拼接”。

顶部Wizard.cshtml声明的 ANY 向导 EXCEPT

@model 始终相同我的例子:

@model MyApp.Models.MySpecificWizardModel

结果,在一个有20个向导的应用程序中,我有同样的文件出现了20次。

绝对不是DRY。

问题:

我想在~/Views/Shared/Wizard.cshtml中放一个文件,并将其用于我的所有向导。我不能这样做的原因是因为我事先只知道我的向导视图模型将继承自IWizardModel

我不能这样做:

@model IWizardModel

那么最好的方法是什么,或者这是不可能的?

我想我可以有一个基本向导视图模型,我的所有向导视图模型都继承自(而不是直接实现IWizardModel)。

这会有用吗?

编辑(事后看来):

为了记录,我的问题是我误以为使用接口作为我的模型,即@model IWizardModel,是行不通的。正如Iain Galloway在他的回答中指出的那样,它确实解决了我的问题。

我认为我无法使用界面作为我的模型的原因是我做了以下事情:

@model IWizardModel // MyNamespace.MySpecificWizardModel

问题是右边的评论(我评论了旧模型)。

由于某种原因,评论产生了错误(与ViewEngine有关)。在我的匆忙中,我只是假设你不能使用模型的接口。

正如奥尔德威尔所说:你的力量是伟大的,哦,延迟。

见Iain Galloway的回答和我对它的补遗。

4 个答案:

答案 0 :(得分:2)

我刚刚测试了以下内容: -

public interface IModel
{
    string Value { get; }
}

public class Foo : IModel
{
    public string Value { get; set; }
}

public class HomeController : Controller
{
    public ActionResult GenericView()
    {
        return View(new Foo() { Value = "Foo" });
    }
}

与GenericView.cshtml一起使用: -

@model MvcApplication1.Models.IModel

<h2>@Model.Value</h2>

这似乎对我有用。我错过了一些重要的要求吗?

答案 1 :(得分:1)

Iain Galloway回答的附录:

Iain Galloway的回答是正确的,在视图中使用界面没有问题。

通过思考,过程如下:

模特:

假设我有一个界面ISomeInterface

public interface ISomeInterface
{
    public int Step { get;set; }
}

我有一个实现MyModel的模型类ISomeInterface

public class MyModel : ISomeInterface
{
    public int Step { get;set; }
    public string Name { get;set; }
    public string Address { get;set; }
}

控制器与行动:

您可以按如下方式将模型传递给视图:

return View("MyView", MyModel);

查看:

MyModel实现ISomeInterface时,我可以在视图中写入:

@model ISomeInterface

多态性适用,因此视图可以访问操作中代码传递的MyModel上的所有属性,而不仅仅是视图声明的ISomeInterface属性。

这就是我想要的。它有效,因此Iain Galloway的答案被标记为正确。

对于我的向导:

由于使用@model ISomeInterface在一般情况下工作,它也适用于我的向导。

为清楚起见,在部分视图(并且只在部分视图中)保存每个步骤的标记,我已经留下了特定模型的声明,而不仅仅是界面实现。即:

@model MyModel

由于使用@model的多态性,我现在可以将wizard.cshtml文件移动到共享文件夹,这对我的向导的整体DRYness来说是一个很好的结果。

通过在控制器的文件夹中包含一个实现,我仍然可以灵活地为每个向导进一步自定义wizard.cshtml。我可能想要这样做,例如,我在我的向导上有一个验证码控件,它需要特定的javascript和样式文件。

答案 2 :(得分:0)

据我所知,你不能这样做。原因是视图引擎静态编译强类型视图。你可以通过实现自己的视图引擎来做一些非常高级的事情,但你不太可能想要花那么多工作来保存几行模板代码。

答案 3 :(得分:0)

你可以用技巧做你想做的事: 您可以创建实现WizardModel并包含IWizardModel类型的属性InheritedWizardModel的包装器模型IWizardModel

public class WizardModel: IWizardModel
{
    private IWizardModel _inheritedWizardModel{get;set;}

    //implementation IWizardModel
    public SomeType SomeIWizardProperty
    {
        get{return _inheritedWizardModel.SomeIWizardProperty;} 
        set{_inheritedWizardModel.SomeIWizardProperty = value;}
    }
}

并使用它