Generic Controller Model Issue and Polymorphism

Setup:

I have designed a Wizard (based initially on Steve Sanderson's wizard in Pro ASP.NET MVC 2).

Essentially, the base wizard controller is declared as follows:

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

To implement my wizard, therefore, I need to declare my Wizard controller as follows:

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

Where MyViewModel implements IWizardModel .

So far so good. The wizard works beautifully and I am right chuffed with it, so don't get hung up on that.

The issue follows:

Issue:

The only issue is that my Wizard uses partial views for each step, that get "stitched" together with a view ( Wizard.cshtml ).

Wizard.cshtml is ALWAYS the same for ANY wizard EXCEPT for the @model declaration at the top, in my example:

@model MyApp.Models.MySpecificWizardModel

As a result, in an app with 20 wizards, I have the very same file appearing 20 times.

Definitely not DRY.

Question:

I would like to put a file in ~/Views/Shared/Wizard.cshtml and use that for all my wizards. The reason I can't do that is because I only know in advance that my wizard viewmodels will inherit from IWizardModel .

And I can't do this:

@model IWizardModel

So what is the best way to do this, or is it just not possible?

I suppose I could have a base Wizard viewmodel that all my wizard viewmodels inherit from (instead of directly implementing IWizardModel ).

Would this work?

EDIT (With the benefit of hindsight):

For the record, my problem was that I was misguided in thinking that using an interface as my model, ie, @model IWizardModel , wouldn't work. As Iain Galloway pointed out in his answer, it does and that solved my problem.

The reason I thought I couldn't use an interface as my model is that I did the following:

@model IWizardModel // MyNamespace.MySpecificWizardModel

The problem is the comment to the right (I commented out the old model).

For some reason, the comment generated an error (something to do with the ViewEngine ). In my haste, I just presumed you couldn't use interfaces for the model.

As olde Will said: Great is thy power, oh delay.

See Iain Galloway's answer and my Addendum to it.


I've just tested the following:-

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" });
    }
}

Along with GenericView.cshtml:-

@model MvcApplication1.Models.IModel

<h2>@Model.Value</h2>

This appears to be working for me. Am I missing something important in your requirements?


Addendum to Iain Galloway's answer:

Iain Galloway's answer is correct, and there is no problem with using the interface in the view.

Thinking it through, the process is as follows:

The Model:

Lets say I have an interface ISomeInterface :

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

And I have a model class MyModel that implements ISomeInterface :

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

Controller and Action:

You can pass the model to the view as follows:

return View("MyView", MyModel);

View:

As MyModel implements ISomeInterface , I can then write in the view:

@model ISomeInterface

Polymorphism applies and so the view has access to all properties on the MyModel that was passed in by the code in the action, not just the ISomeInterface properties of the view declaration.

This is what I wanted. It works and so Iain Galloway's answer is marked as correct.

In the case of my Wizard:

As using @model ISomeInterface works in the general case, it also works for my wizard.

For clarity, however, in the partial views (and only in the partial views) that hold the markup for each step, I have left the declaration for the specific model, not just the interface it implements. Ie:

@model MyModel

As a result of polymorphism working with @model, I can now move the wizard.cshtml file to the shared folder which is a great result for the overall DRYness of my wizards.

I still have the flexibility to further customise my wizard.cshtml for each wizard by including an implementation in the folder for the controller. I might want to do this if, for example, I had a captcha control on my wizard, and it requires specific javascript and style files.


To my knowledge, you can't do this. The reason is that the view engine statically compiles the strongly typed view. There's probably some highly advanced things you could do by implementing your own view engine, but it's unlikely you would want to go to that much work to save a few lines of template code.

链接地址: http://www.djcxy.com/p/59758.html

上一篇: 无法在ASP.NET MVC中使用Razor有条件地呈现HTML

下一篇: 通用控制器模型问题和多态性