我想在我的 ASP.NET MVC 3 应用程序中拥有一个由多个 Controller 提供服务的模型和 View 。
我正在实现一个与用户的在线日历交互的系统,并且我支持 Exchange、Google、Hotmail、Yahoo、Apple 等……它们中的每一个都有完全不同的日历 API 实现,但我可以用我的自己的模型。我认为通过在 Controller 级别实现多态性,我将能够干净地处理不同的 API 和身份验证问题。
我有一个很好的干净模型和 View ,到目前为止我已经实现了两个 Controller ,证明我可以读取/查询/写入/更新到 Exchange 和 Google: ExchangeController.cs
和 GoogleController.cs
。
我有 /Views/Calendar
包含我的 View 代码。我也有 /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
,这个工厂代码就可以工作并创建正确的 Controller !这工作得很好,我在没有真正理解我在做什么的情况下做到了。现在我想我明白了,但我想确保我没有遗漏什么。我真的花时间寻找这样的东西,但没有找到任何东西。
我担心的一件事是,我认为我可以在
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
这让我很沮丧,因为我想在基础上添加一些通用功能,现在我想我将不得不使用另一种方式。
这是为一个 View /模型设置多个 Controller 的正确方法吗?我还要考虑什么?
编辑:有关我的 impl 的更多详细信息
根据下面的回复(谢谢!)我认为我需要显示更多代码以确保你们看到我正在尝试做的事情。我的模型实际上只是一个数据模型。它从这个开始:
/// <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 { }
}
}
然后我的 Controller 有以下方法:
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 在实现上显然是相似的。
我的模型(日历)和相关类(例如约会)是从 Controller 传递到 View 的。我不希望我的 View 看到正在使用的底层在线服务的详细信息。我不明白如何用接口(interface)(或抽象基类)实现 Calendar 类会给我我正在寻找的多态性。
在某些地方,我必须根据用户选择要使用的实现。
我可以这样做:
下面的回复提到了“服务层”。我认为这也许是我偏离轨道的地方。如果您查看 MVC 与数据库正常完成的方式,dbContext 代表“服务层”,对吗?所以也许你们建议的是我可以做间接的第四个地方?例如上面的
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);
}
这是正确的方法吗?
抱歉,这已经变得如此冗长,但这是我知道如何获得足够上下文的唯一方法......
结束编辑
最佳答案
我不会这样做。正如 Jonas 指出的那样, Controller 应该非常简单,旨在协调用于响应请求的各种“服务”。请求流真的因日历而异吗?或者获取该数据所需的数据调用是否不同。
做到这一点的一种方法是将日历分解为公共(public)日历接口(interface)(或抽象基类),然后通过构造函数参数将日历接受到 Controller 中。
public interface ICalendar {
// All your calendar methods
}
public abstract class Calendar {
}
public class GoogleCalendar : Calendar {}
public class ExchangeCalendar : Calendar {}
然后在您的 CalendarController 中,
public class CalendarController {
public CalendarController(ICalendar calendar) {}
}
这在默认情况下不起作用,除非您注册依赖解析器。一种快速的方法是使用 NuGet 安装一个安装包。例如:
Install-Package Ninject.Mvc3
我认为这将是一个更好的架构。但假设你不同意,让我回答你原来的问题。
你得到这个模棱两可的异常(exception)的原因是你有两个公共(public)
Index
没有通过属性区分的方法,该属性指示一个应该响应 GET,一个响应 POST。 Controller 的所有公共(public)方法都是 Action 方法。如果
CalendarController
不打算直接实例化(即它将始终被继承),那么我将在该类 protected virtual
上创建 Index 方法然后在派生类中覆盖它。如果
CalendarController
意味着要自行实例化,而其他派生类只是它的“ flavor ”,那么您需要制作 Index
方法public virtual
然后让每个派生类覆盖 Index
方法。如果他们不覆盖它,他们将添加另一个 Index
方法(C# 规则,不是我们的),并且为了 MVC 的缘故,您需要区分它们。
关于asp.net - 多个 Controller 、一个 View 和一个模型 ASP.NET MVC 3,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8288028/