6

I have a controller that's inheriting from a base controller, and I'm wondering how I can utilize all of the logic from the base controller, but return a different view than the base controller uses.

The base controller populates a model object and passes that model object to its view, but I'm not sure how I can access that model object in the child controller so that I can pass it to the child controller's view.

1
  • 1
    Did You try just to inherit from base controller and prepare views? Commented Oct 14, 2009 at 15:31

5 Answers 5

7

A couple points. You can type your return value as ViewResult if you know that's all you're going to return. Then you can interrogate that value from the overridden implementation. More importantly, according to the MVC v1 source, calling View(object) simply sets the ViewData.Model on the controller, then constructs a ViewResult.

Controller.cs:440

protected internal ViewResult View(object model) { return View(null /* viewName */, null /* masterName */, model); } 

Controller.cs:456

protected internal virtual ViewResult View(string viewName, string masterName, object model) { if (model != null) { ViewData.Model = model; } return new ViewResult { ViewName = viewName, MasterName = masterName, ViewData = ViewData, TempData = TempData }; } 

So all you need to do is call the base method and call View(string).

namespace BaseControllers { public class CoolController { public virtual ViewResult Get() { var awesomeModel = new object(); return View(awesomeModel); } } } public class CoolController : BaseControllers.CoolController { public override ViewResult Get() { var ignoredResult = base.Get(); // ViewData.Model now refers to awesomeModel return View("NotGet"); } } 

Of course you waste CPU cycles constructing the ViewResult that you ignore. So instead you can do this:

public class CoolController : BaseControllers.CoolController { public override ViewResult Get() { var baseResult = base.Get(); baseResult.ViewName = "NotGet"; return baseResult; } } 

If your base controller returns ActionResult, you'll have to cast it to ViewResult before changing the ViewName.

Sign up to request clarification or add additional context in comments.

Comments

5

Sample from my app:

Base class:

public abstract class BaseTableController<T,TU> : BaseController where TU : IGenericService<T>,IModelWrapperService { protected readonly TU _service; public BaseTableController(TU service) { _service = service; _service.ModelWrapper = new ControllerModelStateWrapper(ModelState); } public ActionResult Index() { return View(_service.List()); } 

Inherited:

public class SeverityController : BaseTableController<Severity, ISeverityService> { public SeverityController(ISeverityService service) : base(service) { } //NO CODE INSIDE } 

SeverityController.Index() leads to Views/Severity/Index.aspx. Just had to prepare view. Severity is one of dictionared in my bug tracking application. Every dictionary has similar logic, so I could share some code.

Comments

1

Based on the feedback given on this thread, I've implemented a solution like the one proposed by Antony Koch.

Instead of using an abstract method, I used a concrete, virtual GetIndex method so that I could put logic in it for the base controller.

public class SalesController : Controller { // Index view method and model public virtual ActionResult GetIndex() { return View("Index", IndexModel); } protected TestModel IndexModel { get; set; } public virtual ActionResult Index() { ViewData["test"] = "Set in base."; IndexModel = new TestModel(); IndexModel.Text = "123"; return GetIndex(); } [AcceptVerbs(HttpVerbs.Post)] public virtual ActionResult Index(TestModel data, FormCollection form) { TryUpdateModel(data, form.ToValueProvider()); IndexModel = data; return GetIndex(); } } // This class will need to be in a different namespace or named differently than the // parent controller public class SalesController : MyApp.Controllers.BaseControllers.SalesController { // Index view method and model public override ActionResult GetIndex() { return View("ClientIndex", IndexModel); } public override ActionResult Index() { return base.Index(); } [AcceptVerbs(HttpVerbs.Post)] public override ActionResult Index(TestModel data, FormCollection form) { return base.Index(data, form); } } 

2 Comments

I am facing same kind of issue, Can any one help me ? My Question
Nowadays you can just create a new folder for the child controller and the child's action methods will look there instead.
0
public class BaseController : Controller { protected BaseController() {} public ActionResult Index() { return GetIndex(); } public abstract ActionResult GetIndex(); } public class MyController : BaseController { public MyController() {} public override GetIndex() { return RedirectToAction("Cakes","Pies"); } 

}

Just use abstraction to call the bits you need from the sub-classes.

1 Comment

I did something similar. See my new answer. I was hoping to avoid having to create secondary methods for each controller action, but I guess it's the best way.
0

I ended up just putting an extra parameter on the base Controller -- viewName.

Seems to work just fine.

Am I missing any major downsides?

public class SalesController : Controller { public virtual ActionResult Index(string viewName) { ViewData["test"] = "Set in base."; TestModel model = new TestModel(); model.Text = "123"; return String.IsNullOrEmpty(viewName) ? View(model) : View(viewName, model); } [AcceptVerbs(HttpVerbs.Post)] public virtual ActionResult Index(TestModel data, FormCollection form, string viewName) { TryUpdateModel(data, form.ToValueProvider()); return String.IsNullOrEmpty(viewName) ? View(data) : View(viewName, data); } } public class SalesController : MyApp.Controllers.BaseControllers.SalesController { public override ActionResult Index(string viewName) { return base.Index("ClientIndex"); } [AcceptVerbs(HttpVerbs.Post)] public override ActionResult Index(TestModel data, FormCollection form, string viewName) { return base.Index(data, form, "ClientIndex"); } } 

3 Comments

A concern I have is that a user can send the parameters to an ActionResult in a controller via http. You run the risk of a user being able call index on a controller that does not override "Index" being able to pass a viewName with unexpected results. Unlikely but still possible.
I don't like the solution. You don't have to specify view name. Imagine You have one BaseController and two inheriting - ChildOneController and ChildTwoController. If You call ~/ChildOne/Index, it takes view from ~/ChildOne/Index/Index.aspx. If You call /ChildTwo/Index, it takes view from ~/ChildTwo/Index/Index.aspx. View name is the same, but it is taken from different place.
I am facing same kind of issue, Can any one help me ? My Question

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.