I want to have one model and view that are served by multiple controllers in my ASP.NET MVC 3 application.
I implement a system that interacts with the users online calendar, and I support Exchange, Google, Hotmail, Yahoo, Apple, etc ... Each of them has completely different implementations of the calendar APIs, but I can abstract it with my own model. I think that by implementing polymorphism at the controller level, I can clearly understand the various APIs and authentication issues.
I have a nice clean model and view, and so far I have implemented two controllers that prove that I can read / request / write / update both Exchange and Google: ExchangeController.cs and GoogleController.cs .
I have /Views/Calendar that contains my view code. I also have /Models/CalendarModel.cs , which includes my model.
I want to check which calendar system the user is using in my ControllerFactory . I implemented it as follows:
public class CustomControllerFactory : DefaultControllerFactory { protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType == typeof(CalendarController)) { if(MvcApplication.IsExchange)
and in my Application_Start :
ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
It works. If I got to http://.../Calendar , this factory code works and the right controller is created!
It worked beautifully, and I did it without realizing what I was doing. Now I think I have it, but I want to make sure that I don’t miss anything. I really spent time looking for something like that and did not find anything.
One thing that concerns me is that I decided that I could have an inheritance relationship between CalendarController and ExchangeController / GoogleController as follows:
public class ExchangeController : CalendarController {
But if I do this, I get:
The current request for action 'Index' on controller type 'GoogleController' is ambiguous between the following action methods: System.Web.Mvc.ViewResult Index(System.DateTime, System.DateTime) on type Controllers.GoogleController System.Web.Mvc.ActionResult Index() on type Controllers.CalendarController
Which drives me away because I wanted to put some common functions on the base, and now, I think, I have to use a different way.
Is this the right way to make multiple controllers for the same view / model? What else will I need to consider?
EDIT: Read more about my issues.
Based on the answers below (thanks!) I think I need to show some more codes to make sure you guys see what I'm trying to do. My model is really a data model. It starts with this:
Then my controller has the following methods:
public class ExchangeController : Controller {
So this is basically typical CRUD stuff. I have provided a sample from the version of ExchangeCalendar.cs. GoogleCalendar.cs is clearly similar to an implementation.
My model (Calendar) and related classes (e.g. Appointment) is what is passed from the controller for viewing. I do not want my opinion to reflect the details of using the basic online service. I don’t understand how implementing the Calendar class with an interface (or an abstract base class) will give me the polymorphism I'm looking for.
SOMEWHERE I need to choose which implementation to use based on the user.
I can either do this:
- In my model. I do not want to do this, because then my model gets cooler using a special service code.
- In the controller. For instance. run each controller method with something redirecting to the correct implementation.
- Below the controller. For instance. as I suggest above with the new factory controller.
The answers below mention "service level." I think this is probably where I am off the rails. If you look at how MVC typically runs with a database, dbContext is a "service level", right? So maybe what you guys suggest is the 4th place where I can do an indirect attitude? For example, the Edit above would look something like this:
private CalendarService svc = new CalendarService( eg Exchange or Google ); // // POST: /Calendar/Edit/5 [HttpPost] public ActionResult Edit(MileLogr.Models.Appointment appointment) { if (ModelState.IsValid) { svc.Update(appointment); return RedirectToAction("Index"); } return View(appointment); }
Is this right to do?
Sorry this got so long, but this is the only way I know how to get enough context through ... END EDIT