【问题标题】:Base controller implementation in ASP.NET MVC 2ASP.NET MVC 2 中的基本控制器实现
【发布时间】:2010-12-17 12:34:25
【问题描述】:

我正在使用 ASP.NET MVC 2。我从 Darin(似乎回答了所有 MVC 问题的那个人)那里得到了示例应用程序。我不确定其他人是否有他创建的这个示例项目?他的应用程序中有一个基本控制器,代码如下所示:

public abstract class BaseController<TRepository> : Controller, IModelMapperController
{
   protected BaseController(TRepository repository, IMapper mapper)
   {
      Repository = repository;
      ModelMapper = mapper;
   }

   public TRepository Repository { get; private set; }
   public IMapper ModelMapper { get; private set; }
}

我对这段代码的疑问是 BaseController 是什么意思?

如果我需要指定多个存储库,BaseController 构造函数代码会是什么样子?

每个控制器只有一个存储库是最佳实践吗?我问的原因是因为在我的 ApplicationController 类中,我使用 Create() 操作方法从自定义视图模型填充我的视图。在这个视图模型中,我需要填充 2 个不同的下拉列表。银行名称和账户类型。每个都有自己的存储库。 BankRepository 有一个名为 GetBanks() 的方法,AccountTypeRepository 有一个名为 GetAccountTpes() 的方法。所以在我的应用程序控制器中,我必须调用这两种方法来填充下拉列表。所以应用程序控制器实现了基本控制器。如果我必须传递多个存储库,基本控制器会是什么样子?

请有人对此有所了解。

@Darin:感谢您提供的示例应用程序,我已经从中学到了很多东西。

【问题讨论】:

  • 这不是您问题的答案...但是您为什么还要设计这样的控制器?如果存在多个存储库的可能性,则 BaseController 基本上是无用的。更糟糕的是,通过设计一个简单地设置属性的控制器,你并没有真正为自己节省任何东西。就个人而言,我认为您正在为自己设置一团糟......
  • 这不是我,我正在处理 Darin 发布的示例项目。然后我对此有疑问。你会怎么做?有一个接受特定类型的 I 存储库的基本构造函数吗?
  • 每个控制器一个存储库适用于简单场景。这完全取决于您需要做什么。例如,如果您的每个存储库类型的人具有 1:1 的根,则将两个聚合根一起使用的聚合视图将需要多个存储库。 -- 顺便说一句,要小心坚持你不完全理解的模式。我的经验是它创建的代码比完全不了解模式更糟糕。 ;)
  • 我同意 zowens 和 jfar。我要进一步说,在大多数情况下,我的控制器无论如何都不会直接调用存储库(同样,除了在最简单的情况下),b/c 如果他们这样做了,那么您会发现自己将更多业务逻辑滑入控制器,控制器的主要关注点应该是将请求路由到视图和重定向。当我第一次接触 MVC 时,我走上了 Darin 似乎提倡的道路,正如 jfar 所说,发现它很快变得站不住脚,将控制器强类型化到存储库或单个实体的好处太少了。跨度>

标签: c# asp.net-mvc asp.net-mvc-2 dependency-injection autofac


【解决方案1】:

因此您的每个控制器都可以指向不同的存储库。像这样:

public class UsersController : BaseController<UserRepository>
{
    public UsersController() 
        : base(new UserRepository(), new UserModelMapper())
    {
        // do stuff
    }

    public ActionResult Index()
    {
        // now you can use syntax like, since "Repository" is type "UserRepository"
        return View(Respository.GetAllUsers());
    }

    public ActionResult Details(int id)
    {
        return View(Respository.GetUser(id));
    }
}

更新地址

public class AddressesController : BaseController<AddressRepository>
{
    public AddressesController() 
        : base(new AddressRepository(), new AddressModelMapper())
    {
    }

    public ActionResult Addresses(int id)
    {
        return View(Respository.GetAllByUserId(id));
    }
}

工厂更新

public static class RepositoryFactory
{
    internal static Hashtable Repositories = new Hashtable();

    internal static T GetRepository<T>() where T : class, new()
    {
        if (Repositories[typeof(T)] as T == null)
        {
            Repositories[typeof(T)] = new T();
        }
        return Repositories[typeof(T)] as T;
    }

    public static AccountTypeRepository AccountTypeRepository
    {
        get { return GetRepository<AccountTypeRepository>(); } 
    }

    public static BankRepository BankRepository
    {
        get { return GetRepository<BankRepository>(); } 
    }
    /* repeat as needed or change accessibility and call GetRepository<> directly */

现在,您甚至可以不使用 BaseController 来编写以下代码:

public class ApplicationModel
{
    public Application Application { get; set; }
    public IList<Bank> Banks { get; set; }
    public IList<AccountType> AccountTypes { get; set; }
}

public class ApplicationController : Controller
{
    public ActionResult Index()
    {
        ApplicationListModel model = new ApplicationListModel()
        {
            Applications = RespositoryFactory.ApplicationRepository.GetAll();
        }
        return View(model);
    }

    public ActionResult Details(int id)
    {
        ApplicationModel model = new ApplicationModel()
        {
            Application = RespositoryFactory.ApplicationRepository.Get(id),
            Banks = RespositoryFactory.BankRepository.GetAll(),
            AccountTypes = RespositoryFactory.AccountTypeRepository.GetAll()
        }
        return View(model);

    }
}

【讨论】:

  • @hunter。我有这样的代码,但这不是我问的。我问如果你的控制器需要多个存储库会发生什么,base 会是什么样子?我还问了其他几个问题。不是一个体面的答案。
  • brendan,我通常倾向于为每个实体考虑一个控制器,因此,我认为我对此的“想法”不符合您想要实现的目标。您的“视图”是否不会由从相应控制器中获取每个部分的部分视图组成。另外,有点苛刻地给这个家伙一点点,只是为了让你清楚地展示如何根据示例实现它(并且严重缺乏幽默感:))。
  • @brendan,对不起,我误解了你的问题。就像吉姆说的,你最好创建一个地址控制器来处理与地址和地址存储库相关的操作。除非您创建了一些混合存储库类来处理 UserRepository 和 AddressRepository 的操作,否则您可能希望为每个存储库绑定控制器创建一个单独的控制器
  • 如果您希望能够从一个控制器访问多个存储库,我建议使用与 Darin 建议的模式不同的模式
  • Brendan - 很难简单解释,但如果您使用服务层,那么每个模型实体都可以有一个通过其 IRepository 接口公开的类。然后,如果需要,您可以在用户内部实例化地址服务类的实例。这是一个众所周知的模式。事实上,您实际上会更上一层楼,并在服务层之上有一个任务层,它执行通用存储库集合之外的职责。
【解决方案2】:

我不确定我是否会回答你所有的问题,但这里是……

我也使用BaseController,但我没有按照您的示例进行操作。这是我的代码的样子(我的应用程序也使用 DI 作为构造函数...):

public class BaseController : Controller {
    private readonly IProvider AddressProvider = null;
    private readonly IProvider EmailProvider = null;
    private readonly IProvider PhoneProvider = null;

    [Inject] // Using Ninject for DI
    public BaseController(
        AddressProvider AddressProvider,
        EmailProvider EmailProvider,
        PhoneProvider PhoneProvider) {
        this.AddressProvider = AddressProvider;
        this.EmailProvider = EmailProvider;
        this.PhoneProvider = PhoneProvider;
    }
}

这是我的AdministrationController,它继承自BaseController

public class AdministrationController : BaseController {
    private readonly CustomerProvider CustomerProvider = null;
    private readonly EmployeeProvider EmployeeProvider = null;

    [Inject]
    public AdministrationController(
        CustomerProvider CustomerProvider,
        EmployeeProvider EmployeeProvider,
        AddressProvider AddressProvider,
        EmailProvider EmailProvider,
        PhoneProvider PhoneProvider) : base(AddressProvider, EmailProvider, PhoneProvider) {
        this.CustomerProvider = CustomerProvider;
        this.EmployeeProvider = EmployeeProvider;
    }
}

我的AdministrationController 只关心CustomerProviderEmployeeProvider,它会将AddressProviderEmailProviderPhoneProvider 传递给BaseController

AddressProviderEmailProviderPhoneProviderBaseController 中,因为我认为AddressEmailPhone低级 对象。我这样做的原因是因为它们可以链接到CustomerEmployee 或就数据库而言的任何其他内容。所以,我没有让CustomerEmployee 与它们的每个对象交互的多种方法,我只有一个。例如:

public class BaseController : Controller {
    //  GET: /Addresses/{AddressId}/Delete
    public void DeleteAddress(
        int AddressId) {
        this.AddressProvider.DeleteAndSave(AddressId);

        Response.Redirect(Request.UrlReferrer.AbsoluteUri);
    }

    //  GET: /Emails/{EmailId}/Delete
    public void DeleteEmail(
        int EmaildId) {
        this.EmailProvider.DeleteAndSave(EmailId);

        Response.Redirect(Request.UrlReferrer.AbsoluteUri);
    }

    //  GET: /Phones/{PhoneId}/Delete
    public void DeletePhone(
        int PhoneId) {
        this.PhoneProvider.DeleteAndSave(PhoneId);

        Response.Redirect(Request.UrlReferrer.AbsoluteUri);
    }
}

然后我会处理我的 低级 对象。请记住,在我的应用程序中,我还有其他方法可以根据需要进一步操作这些对象。

现在,在我的AdministrationController 中,我正在使用CustomerProviderEmployeeProvider。这些更专业,因为我认为CustomerEmployee高级 对象。话虽如此,他们的提供者比删除做了更多的工作。例如,它们还提供视图使用的视图模型(durp...):

public class AdministrationController : BaseController {
    public ActionResult Customer(
        int CustomerId) {
        return this.View(this.CustomerProvider.GetView(CustomerId));
    }

    public AciontResult Customers() {
        return this.Veiw(this.CustomerProvider.GetAllView(CustomerId));
    }

    public ActionResult CustomerAddresses(
        int CustomerId,
        Address Address) {
        if (ModelState.IsValid) {
            this.CustomerProvider.AddAddressAndSave(CustomerId, Address);
        };

        return this.RedirectToAction("Customer", new {
            CustomerId = CustomerId
        });
    }

    public ActionResult Employee(
        int EmployeeId) {
        return this.View(this.EmployeeProvider.GetView(EmployeeId));
    }

    public ActionResult Employees() {
        return this.View(this.EmployeeProvider.GetAllView());
        //  OR
        //  return this.View(this.EmployeeProvider.GetActiveView());
        //  OR
        //  return this.Veiw(this.EmployeeProvider.GetInactiveView());
        //  ETC...
        //  All of these return the exact same object, just filled with different data
    }

    public RedirectToRouteResult EmployeeAddresses(
        int EmployeeId,
        Address Address) {
        if (ModelState.IsValid) {
            this.EmployeeProvider.AddAddressAndSave(EmployeeId, Address);
            //  I also have AddAddress in case I want to queue up a couple of tasks
            //  before I commit all changes to the data context.
        };

        return this.RedirectToAction("Employee", new {
            EmployeeId = EmployeeId
        });
    }
}

每个控制器只有一个存储库是最佳实践吗?

我会说不,因为您的存储库仅适用于它们被实例化的对象。你不能拥有(好吧,你可以,但这太糟糕了......)一个同时处理 AddressEmailPhone 的存储库,因为你必须专门化 em> 只是为了让它按照你需要的方式工作。

我的AddressProviderEmailProviderPhoneProvider 本质上是相同的,因为它们实现了IProvider,但是它们都为它们正在使用的对象实例化了一个通用存储库 (Repository&lt;T&gt;)。

此外,您的控制器不应直接与存储库交互,而应通过提供程序间接交互。

我的CustomerProviderEmployeeProvider 每个实例都专门为CustomerEmployee 存储库(CustomerRepositoryEmployeeRepository),但它们也会实例化其他存储库,例如在构建视图时需要楷模。例如,他们将实例化一个StateRepository,即Repository&lt;State&gt;PhoneTypesRepository,即Repository&lt;PhoneType&gt;,他们将使用这些存储库将其他对象/集合传递给视图,以使用下拉菜单或任何。他们还将实例化其他提供程序以进一步帮助构建视图模型,例如 CookieProvider,他们使用该模型获取当前活动的 Cookie 并将其传递给视图模型。

总而言之,它是一个由独立/通用提供程序或存储库组成的网格,它们组合在一起以完成一项专门的任务。

我希望这可以通过另一种编写代码的方式为您提供一些启发,或者至少我希望它可以帮助您更好地理解。

附:如果您想知道Provider 是什么,大多数其他人都选择称他们为Service,但对我来说这个词被误用了,所以我只称他们为Providers,因为他们提供 根据需要具有专用功能或数据的控制器。

【讨论】:

    【解决方案3】:

    嗯,似乎提到的示例有点特殊或作者的口味。

    我建议不要把事情复杂化,并避免像基本控制器这样的层超类型,除非它显然是必要的。这种方法让您可以根据需要为每个 Controller 定义尽可能多的存储库(和其他依赖项),而不会强制您使用特殊情况的 BaseController 超类型。

    另一个建议是当您需要控制器之间的一些通用逻辑时,首选Composition over Inheritance

    所以,总而言之 - 是的,如果这是您的情况,在您的 Controller 中注入不同的存储库是正常的,对我来说,这似乎是比某种不必要的 BaseController 更好的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-08
      • 1970-01-01
      • 2011-04-28
      • 1970-01-01
      • 2017-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多