合并ASP.NET MVC控制器依赖项(StructureMap)

时间:2011-12-08 04:23:27

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

我正在查看我网站上的控制器,他们的大多数构造函数都是这样的:

public SomeController(
   IServiceOne serviceOne, 
   IServiceTwo serviceTwo, 
   ILoggingService loggingService, 
   IGeospatialService geoSpatialService)
{
    // copy to class variables.
}

换句话说,它非常多毛,使重构变得困难。一些控制器有大约8个依赖项。

有什么方法可以将这些依赖关系“分组”到其中一个桶中?

例如,每个控制器都需要ILoggingService,执行空间内容的控制器需要IGeospatialService,而在某些情况下只需要IServiceOneIServiceTwo

我希望看到类似的内容:

public SomeController(
       ICoreServicesGroup coreGroup,
       ISomeNameForServicesGroup serviceGroup)
    {
        // copy to class variables.
    }

我认为引入一些OO技术会很好,例如有一个“基础”依赖类,它在其受保护的ctor中需要ILoggingService。然后你可能有另一个继承的子依赖等等。

以前有人这样做过吗?这是StructureMap可以为我做的事情,还是只是我自己编写基本代码?

3 个答案:

答案 0 :(得分:13)

<强>登录

每个控制器都需要依赖关系时,它是一个非常确定的指标,它不是“正常”依赖关系,而是交叉关注关注。记录是跨领域关注的典型例子,因此ILoggingService应该像任何其他跨领域关注一样处理。

SOLID OO中,解决跨领域问题的适当方法是使用Decoratorcan be generalized towards AOP)。但是,ASP.NET MVC Controller操作方法不是任何接口的一部分,因此这是一个不太理想的解决方案。

相反,MVC框架提供Action Filters用于拦截目的。如果你想实现一个松散耦合的过滤器,请自己帮个忙implement it as a global filter instead of an attribute

其他依赖

对于其他依赖项,它对refactor them to Facade Services有意义。这涉及识别相关服务的自然集群,因此具体如何完成此操作是针对每个代码库的。

答案 1 :(得分:3)

我知道我不久前接受了@Mark Seeman的回答,但我现在终于有时间实现了这个,所以我想分享我实际做的事情,为了其他人的利益。

基本上,我在应用程序中为依赖项的“组”创建了包装器接口。

示例:

public interface ICoreServicesDependencyGroup
{
   IUnitOfWork UnitOfWork { get; }
   IAspNetMvcLoggingService LoggingService { get; }
}

实施:

public class CoreServicesDependencyGroup : ICoreServicesDependencyGroup
{
   private readonly IAspNetMvcLoggingService _loggingService;
   private readonly IUnitOfWork _unitOfWork;

   public CoreServicesDependencyGroup(
      IAspNetMvcLoggingService loggingService, 
      IUnitOfWork unitOfWork)
   {
      Condition.Requires(loggingService).IsNotNull();
      Condition.Requires(unitOfWork).IsNotNull();
      _loggingService = loggingService;
      _unitOfWork = unitOfWork;
   }

   public IUnitOfWork UnitOfWork { get { return _unitOfWork; } }
   public IAspNetMvcLoggingService LoggingService { get { return _loggingService; } }
}

非常简单。

然后我更新了我的控制器。

示例ctor之前:

public LocationController(
    IUnitOfWork unitOfWork,
    IAspNetMvcLoggingService loggingService, 
    ILocationService locationService, 
    ICachedLocationService cachedLocationService)
{
    _unitOfWork = unitOfWork;
    _loggingService = loggingService;
    _locationService = locationService;
    _cachedLocationService = cachedLocationService;
}

后:

public LocationController(
    ICoreServicesDependencyGroup coreServicesDependencyGroup,
    ILocationDependencyGroup locationDependencyGroup)
{
    _unitOfWork = coreServicesDependencyGroup.UnitOfWork;
    _loggingService = coreServicesDependencyGroup.LoggingService;
    _locationService = locationDependencyGroup.Service;
    _cachedLocationService = locationDependencyGroup.CachedService;
}

真的没什么特别的,只是套装。在引擎盖下,控制器仍然使用相同的依赖关系,但是ctor签名更小,更易读,而且还使单元测试更容易。

答案 2 :(得分:0)

我看到了几个选项:

  1. 像@ 32bitkid提到的Fascade服务。
  2. 将您的控制器分解为具有更多常见依赖关系的更细粒度的操作方法组。
  3. 静态切入点。 (我知道很多人不喜欢它们,但我发现它们对我的核心服务非常有用,即使使用DI也不会改变。)这是一个使用公共服务定位器的例子。
  4. public class Logger
    {
        public static Func<ILoggerService> Instance = () => ServiceLocator.Current.GetInstance<ILoggerService>();
    }
    

    public class Logger { public static Func<ILoggerService> Instance = () => ServiceLocator.Current.GetInstance<ILoggerService>(); }

    用法:

    测试:Logger.Instance().Log(message);