Ich möchte ein Modell & Ansicht haben, die von mehreren Controllern in meinem ASP.NET MVC 3 app bedient wird.
Ich implementiere ein System, das mit dem Online-Kalender des Benutzers interagiert, und ich unterstütze Exchange, Google, Hotmail, Yahoo, Apple, ect. Jeder von ihnen hat wild unterschiedliche Implementierungen von Kalender-APIs, aber ich kann abstrahieren, dass weg mit meinem eigenen Modell. Ich denke, dass ich durch die Implementierung der Polymorphie auf Controller-Ebene in der Lage sein werde, die verschiedenen APIs und Authentifizierungsprobleme sauber zu behandeln.
Ich habe ein schönes sauberes Modell und Ansicht und ich habe zwei Controller so weit implementiert, die beweisen, dass ich lesen/abfragen/schreiben/aktualisieren, um sowohl Exchange und Google: ExchangeController.cs
y GoogleController.cs
.
Ich habe /Views/Calendar
die meinen Ansichtscode enthält. Ich habe auch /Models/CalendarModel.cs
die mein Modell einschließt.
Ich möchte, dass der Test, welches Kalendersystem der Benutzer verwendet, in meinem ControllerFactory
. Ich habe es folgendermaßen umgesetzt:
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);
}
}
und in meinem Application_Start
:
ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
Das funktioniert. Wenn ich zu http://.../Calendar
dieser Werkscode funktioniert und der richtige Controller wird erstellt!
Das hat wunderbar funktioniert, und ich habe es getan, ohne wirklich zu verstehen, was ich da tat. Jetzt denke ich, ich habe es, aber ich möchte sicherstellen, dass ich nicht etwas übersehen. Ich habe wirklich Zeit damit verbracht, nach etwas wie diesem zu suchen und habe nichts gefunden.
Eine Sache, die mich beunruhigt, ist, dass ich dachte, ich könnte eine Vererbungsbeziehung herstellen zwischen CalendarController
y ExchangeController
/ GoogleController
así:
public class ExchangeController : CalendarController
{
Aber wenn ich das tue, bekomme ich:
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
Das ärgert mich, denn ich wollte die Basis mit einer allgemeinen Funktionalität ausstatten, und jetzt muss ich wohl einen anderen Weg wählen.
Ist dies der richtige Weg zu tun, haben mehrere Controller für eine Ansicht/Modell? Was muss ich sonst noch beachten?
EDIT: Weitere Einzelheiten zu meinem Implantat
Auf der Grundlage der Antworten unten (danke!) Ich denke, ich muss etwas mehr Code zeigen, um sicherzustellen, dass Sie sehen, was ich zu tun versuche. Mein Modell ist eigentlich nur ein Datenmodell. Es beginnt mit diesem:
/// <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 { }
}
}
Dann hat mein Controller die folgenden Methoden:
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");
}
Es ist also im Grunde das typische CRUD-Zeug. Ich habe das Beispiel aus der ExchangeCalendar.cs-Version bereitgestellt. Die GoogleCalendar.cs ist offensichtlich ähnlich in der Umsetzung.
Mein Modell (Kalender) und die zugehörigen Klassen (z.B. Termin) sind das, was vom Controller an die Ansicht übergeben wird. Ich möchte nicht, dass meine Ansicht Details über den zugrunde liegenden Onlinedienst sieht, der verwendet wird. Ich verstehe nicht, wie die Implementierung der Klasse Calendar mit einer Schnittstelle (oder einer abstrakten Basisklasse) mir den gewünschten Polymorphismus bietet.
Irgendwo muss ich je nach Benutzer die zu verwendende Implementierung auswählen.
Ich kann dies entweder tun:
- In meinem Modell. Ich will nicht, dies zu tun, weil dann mein Modell bekommt alle crufty mit Service-spezifischen Code.
- Im Controller. Beginnen Sie z. B. jede Controller-Methode mit etwas, das auf die richtige Implementierung umleitet
- Unterhalb des Controllers. Z.B. wie ich oben vorschlage mit einer neuen Controller-Fabrik.
In den folgenden Antworten wird die "Dienstebene" erwähnt. Ich glaube, hier bin ich vielleicht auf dem Holzweg. Wenn man sich die Art und Weise ansieht, wie MVC normalerweise mit einer Datenbank durchgeführt wird, stellt der dbContext die "Serviceschicht" dar, richtig? Vielleicht ist das, was ihr vorschlagt, ein vierter Ort, an dem ich die Umleitung durchführen kann? Zum Beispiel Edit
würde in etwa so aussehen:
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);
}
Ist das der richtige Weg?
Entschuldigen Sie, dass dies so langatmig geworden ist, aber das ist die einzige Möglichkeit, die ich kenne, um genügend Kontext zu vermitteln... ENDE BEARBEITEN