asp.net - 多个 Controller 、一个 View 和一个模型 ASP.NET MVC 3

标签 asp.net asp.net-mvc-3

我想在我的 ASP.NET MVC 3 应用程序中拥有一个由多个 Controller 提供服务的模型和 View 。

我正在实现一个与用户的在线日历交互的系统,并且我支持 Exchange、Google、Hotmail、Yahoo、Apple 等……它们中的每一个都有完全不同的日历 API 实现,但我可以用我的自己的模型。我认为通过在 Controller 级别实现多态性,我将能够干净地处理不同的 API 和身份验证问题。

我有一个很好的干净模型和 View ,到目前为止我已经实现了两个 Controller ,证明我可以读取/查询/写入/更新到 Exchange 和 Google: ExchangeController.csGoogleController.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 !

这工作得很好,我在没有真正理解我在做什么的情况下做到了。现在我想我明白了,但我想确保我没有遗漏什么。我真的花时间寻找这样的东西,但没有找到任何东西。

我担心的一件事是,我认为我可以在 CalendarControllerExchangeController/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 类会给我我正在寻找的多态性。

在某些地方,我必须根据用户选择要使用的实现。

我可以这样做:
  • 在我的模型中。我不想这样做,因为这样我的模型就会因服务特定的代码而变得很糟糕。
  • 在 Controller 中。例如。使用重定向到正确实现
  • 的内容启动每个 Controller 方法
  • 在 Controller 下方。例如。正如我在上面建议的那样,使用新的 Controller 工厂。

  • 下面的回复提到了“服务层”。我认为这也许是我偏离轨道的地方。如果您查看 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/

    相关文章:

    asp.net-mvc-3 - 我可以添加到 ASP.NET MVC 3 中的 Display/EditorTemplates 搜索路径吗?

    asp.net-mvc-3 - Razor View 引擎,如何在 html 之间写入?

    asp.net-mvc-3 - T4MVC 和 HTTPS 未显示在 URL 中

    asp.net - 使用事件处理错误,检查是否发生错误并做出相应 react

    javascript - ASP.net MVC session 超时持续时间

    c# - Windows Azure、EF 和 MVC3 的验证问题

    asp.net-mvc - 我的日志框架永远与我的应用程序绑定(bind)在一起!

    .net - 复选框 Gridview 启用和禁用

    asp.net - session cookie 的生命周期是多长?

    c# - 如何避免在服务器中自动附加 returnURL?