Я хочу, чтобы в моем приложении ASP.NET MVC 3 была одна модель и представление, обслуживаемое несколькими контроллерами.
Я внедряю систему, которая взаимодействует с онлайн-календарем пользователей, и я поддерживаю Exchange, Google, Hotmail, Yahoo, Apple и т. Д. Каждая из них имеет совершенно разные реализации API-интерфейсов календаря, но я могу абстрагироваться от этого. покончить с моей собственной моделью. Я думаю, что, реализовав полиморфизм на уровне контроллера, я смогу чисто разобраться с различными API и проблемами аутентификации.
У меня хорошая чистая модель и представление, и я реализовал два контроллера, которые доказывают, что я могу читать / запрашивать / писать / обновлять как для Exchange, так и для Google: ExchangeController.cs
и GoogleController.cs
.
У меня есть /Views/Calendar
, который содержит мой код просмотра. У меня также есть /Models/CalendarModel.cs
, который включает мою модель.
Я хочу, чтобы в моем ControllerFactory
проходил тест, для которого система календаря использует пользователь. Я реализовал это так:
public class CustomControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == typeof(CalendarController))
{
if(MvcApplication.IsExchange) // hack for now
return new ExchangeController();
else
return new GoogleController();
}
return base.GetControllerInstance(requestContext, controllerType);
}
}
а по моему Application_Start
:
ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
Это работает. Если я попал на http://.../Calendar
, этот заводской код работает и правильный контроллер создан!
Это работало прекрасно, и я делал это, не понимая, что я делаю. Теперь я думаю, что получил это, но я хочу удостовериться, что я ничего не пропускаю. Я действительно потратил время на поиски чего-то подобного и ничего не нашел.
Одна вещь, которая меня беспокоит, это то, что я подумал, что смогу иметь наследственные отношения между CalendarController
и ExchangeController
/ GoogleController
, как это:
public class ExchangeController : CalendarController
{
Но если я сделаю это, я получу:
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
Что меня раздражает, потому что я хотел поставить некоторые общие функции на базу, и теперь я думаю, что мне придется использовать другой способ.
Это правильный способ иметь несколько контроллеров для одного вида / модели? Что еще я должен рассмотреть?
РЕДАКТИРОВАТЬ: Более подробную информацию о моей импл
Основываясь на ответах ниже (спасибо!), Я думаю, мне нужно показать еще немного кода, чтобы убедиться, что вы, ребята, видите, что я пытаюсь сделать. Моя модель действительно просто модель данных. Это начинается с этого:
/// <summary>
/// Represents a user's calendar across a date range.
/// </summary>
public class Calendar
{
private List<Appointment> appointments = null;
/// <summary>
/// Date of the start of the calendar.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// Date of the end of the calendar
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// List of all appointments on the calendar
/// </summary>
public List<Appointment> Appointments
{
get
{
if (appointments == null)
appointments = new List<Appointment>();
return appointments;
}
set { }
}
}
Тогда мой контроллер имеет следующие методы:
public class ExchangeController : Controller
{
//
// GET: /Exchange/
public ViewResult Index(DateTime startDate, DateTime endDate)
{
// Exchange specific gunk. The MvcApplication._service thing is a temporary hack
CalendarFolder calendar = (CalendarFolder)Folder.Bind(MvcApplication._service, WellKnownFolderName.Calendar);
Models.Calendar cal = new Models.Calendar();
cal.StartDate = startDate;
cal.EndDate = endDate;
// Copy the data from the exchange object to the model
foreach (Microsoft.Exchange.WebServices.Data.Appointment exAppt in findResults.Items)
{
Microsoft.Exchange.WebServices.Data.Appointment a = Microsoft.Exchange.WebServices.Data.Appointment.Bind(MvcApplication._service, exAppt.Id);
Models.Appointment appt = new Models.Appointment();
appt.End = a.End;
appt.Id = a.Id.ToString();
...
}
return View(cal);
}
//
// GET: /Exchange/Details/5
public ViewResult Details(string id)
{
...
Models.Appointment appt = new Models.Appointment();
...
return View(appt);
}
//
// GET: /Exchange/Edit/5
public ActionResult Edit(string id)
{
return Details(id);
}
//
// POST: /Exchange/Edit/5
[HttpPost]
public ActionResult Edit(MileLogr.Models.Appointment appointment)
{
if (ModelState.IsValid)
{
Microsoft.Exchange.WebServices.Data.Appointment a = Microsoft.Exchange.WebServices.Data.Appointment.Bind(MvcApplication._service, new ItemId(appointment.Id));
// copy stuff from the model (appointment)
// to the service (a)
a.Subject = appointment.Subject
...
a.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
return RedirectToAction("Index");
}
return View(appointment);
}
//
// GET: /Exchange/Delete/5
public ActionResult Delete(string id)
{
return Details(id);
}
//
// POST: /Exchange/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(string id)
{
Microsoft.Exchange.WebServices.Data.Appointment a = Microsoft.Exchange.WebServices.Data.Appointment.Bind(MvcApplication._service, new ItemId(id));
a.Delete(DeleteMode.MoveToDeletedItems);
return RedirectToAction("Index");
}
Так что это в основном типичные вещи CRUD. Я предоставил образец из версии ExchangeCalendar.cs. GoogleCalendar.cs явно похож на реализацию.
Моя модель (Календарь) и связанные с ней классы (например, Назначение) - это то, что передается от контроллера к представлению. Я не хочу, чтобы мой взгляд видел детали того, какой основной онлайн-сервис используется. Я не понимаю, как реализация класса Calendar с интерфейсом (или абстрактным базовым классом) даст мне полиморфизм, который я ищу.
ГДЕ-ТО Я должен выбрать, какую реализацию использовать, основываясь на пользователе.
Я могу сделать это:
- В моей модели. Я не хочу этого делать, потому что тогда моя модель становится слишком грубой с кодом, специфичным для сервиса.
- В контроллере. Например. запустить каждый метод контроллера с чем-то, что перенаправляет к правильной реализации
- Под контроллером. Например. как я предлагаю выше с новой фабрикой контроллеров.
В ответах ниже упоминается "уровень обслуживания". Я думаю, что это, возможно, где я с рельсов. Если вы посмотрите, как MVC обычно выполняется с базой данных, dbContext представляет «уровень обслуживания», верно? Так, может быть, вы, ребята, предлагаете, это 4-е место, где я могу сделать косвенное обращение? Например, Edit
выше будет выглядеть примерно так:
private CalendarService svc = new CalendarService( e.g. 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);
}
Это правильный способ сделать это?
Извините, это стало настолько многословно, но это единственный способ, которым я знаю, как получить достаточно контекста ...
КОНЕЦ РЕДАКТИРОВАНИЯ