AggregateService应该用作存储公共依赖项的容器吗?

时间:2012-01-26 00:42:00

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

我有一个基本控制器类,它使用autofac中的聚合服务来注入其依赖项。在从基本控制器派生的类中,我需要访问作为聚合容器的一部分的服务。哪一种更好?

public Interface ICommonControllerDependencies
{
    ICookieService CookieService{ get; }
    IAuthenticationService AuthenticationService{ get; }
}

public abstract class BaseController : Controller
{
    protected readonly ICommonControllerDependencies Dependencies;

    protected BaseController(ICommonControllerDependencies dependencies)
    {
        this.Dependencies = dependencies;
    }

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.Controller.ViewData[UserName] = Dependencies.AuthenticationService.UserName;

        base.OnActionExecuting(filterContext);            
    }
     protected ActionResult RedirectToDefaultPage()
    {
        var page = Dependencies.CookieService.GetDefaultPageCookie(Request, RouteData);
        //Do Something
    }  

}

public class BaseReportController : BaseController
{
    public BaseReportController(ICommonControllerDependencies dependencies)
        : base(dependencies)
   {
        _cookieService = dependencies.CookieService;
   }
}

OR

public class BaseReportController : BaseController
{
    public BaseReportController(
        ICookieService cookieService, 
        ICommonControllerDependencies dependencies)
        : base(dependencies)
    {
        _cookieService = cookieService;
    }
}

3 个答案:

答案 0 :(得分:2)

AggregateService旨在将您的子类与超类中的更改隔离开来,并在不破坏子类的情况下启用超类中的更改。 如果你选择选项1,你将子类耦合到你的超类,从而失去了预期的隔离。

在不了解您的设计的情况下,我会选择选项2.我认为我不能期望ServiceA永远在那里。明确依赖于子类中的IServiceA,该类可以快乐地忽略超类内部的变化。

答案 1 :(得分:1)

当您需要聚合服务时(如Autofac文档所述),这可能是违反Single Responsibility Principle的标志,它表明类应该做一件事,而且只做一件事。

换句话说,聚合服务模式是code smell *。

还有big base classes is a code smell,因为基类往往会增长并增长到大而复杂的类,它包含许多不是所有子类型都使用的代码。相反,一般建议是赞成composition over inheritance

然而,对于ASP.NET MVC等UI框架,这并不总是容易的,因为大多数框架本身都会促进继承。

尝试提取基类的逻辑以分离依赖关系,尤其是当所有子类型不使用基类中的代码时。例如,您可以将RedirectToDefaultPage方法重构为以下内容:

public class DefaultPageRedirector
{
    private readonly ICookieService cookieService;

    public DefaultPageRedirector(ICookieService cookieService)
    {
        this.cookieService = cookieService;
    }

    public ActionResult RedirectToDefaultPage(
        Controller controller)
    {
        var page = this.cookieService.GetDefaultPageCookie(
            controller.Request, controller.RouteData);

        //Do Something
    }
}

通过这种方式,您只能将DefaultPageRedirector注入实际需要的Controller类型。

对于OnActionExecuting它是不同的,因为它被调用每个子类型。但是,ViewData["UserName"]属性可能不会被系统中的每个View使用,在这种情况下,您应该考虑将UserName作为(静态类型){{1}的一部分返回对象。如果大多数视图使用它,您可能需要考虑使用局部视图,因为您的视图中可能有一些重复的代码(DRY原则不仅适用于代码,而且适用于系统中的每个部分)

这可能会消除基类中的大多数代码,这可能也会删除基类中的大部分依赖项(如果不是全部的话)。

*请注意,代码气味并不意味着总是出现问题。引用维基百科:“代码气味是程序源代码中的任何症状,可能表示更深层次的问题。”

答案 2 :(得分:0)

我认为这取决于您是否在容器中注册了IserviceA,以及您如何预测您的设计在不断发展。

就个人而言,如果您不希望从ICommonControllerDependencies接口中删除serviceA,我会使用第一个选项。注入单个接口似乎更简单。如果您认为可能会在某个时候删除serviceA,我会考虑使用第二个选项,因为它会删除BaseReportController中服务和控制器依赖关系之间的耦合。我认为第二种选择更紧密地遵循Law of Demeter