繁体   English   中英

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

[英]Consolidating ASP.NET MVC Controller Dependencies (StructureMap)

我正在看我网站上的控制器,他们的大多数构造器看起来像这样:

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可以为我做的事情,还是仅仅是我自己编写基本代码?

记录

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

SOLID OO中,解决跨领域问题的适当方法是使用装饰器可以推广到AOP )。 但是,ASP.NET MVC Controller操作方法不是任何接口的一部分,因此这是一个不太理想的解决方案。

相反,MVC框架为拦截目的提供了动作过滤器。 如果您想实现一个松散耦合的过滤器,请帮自己一个忙,并将其实现为全局过滤器而不是属性

其他依赖项

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

我知道我不久前接受了@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签名更小,更易读,并且还使单元测试更容易。

我看到几个选项:

  1. 像@ 32bitkid这样的Fascade服务提到了。
  2. 将控制器分解为具有更多常见依赖关系的更细粒度的操作方法组。
  3. 静态切入点。 (我知道很多人不喜欢它们,但我发现它们对我的核心服务非常有用,即使使用DI也不会改变。)这是一个使用Common Service Locator的例子。

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

用法: Logger.Instance().Log(message);

测试: Logger.Instance = () => new TestLogger();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM