整合 ASP.NET MVC 控制器依赖关系 (StructureMap)

发布于 2024-12-20 03:59:37 字数 880 浏览 1 评论 0原文

我正在查看我的网站中的控制器,它们的大多数构造函数看起来像这样:

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

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

有什么方法可以以某种方式将这些依赖项“分组”到多个存储桶之一中吗?

例如,每个控制器都需要 ILoggingService,执行空间操作的控制器需要 IGeospatialService,以及 IServiceOneIServiceTwo code> 仅在某些情况下才需要。

我希望看到这样的内容:

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

我认为引入一些 OO 技术会很好,例如拥有一个“基本”依赖类,它在其受保护的构造函数中采用 ILoggingService 。那么你可能有另一个继承的子依赖项,等等。

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

I'm looking at the controllers in my website, and most of their constructors look like this:

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

In other words, it's very hairy and makes refactoring difficult. Some controllers have around 8 dependencies.

Is there any way i can somehow "group" these dependencies into one of more buckets?

For example, ILoggingService is required in every controller, IGeospatialService is required by controllers who do spatial stuff, and IServiceOne and IServiceTwo is only required in certain cases.

I would like to see something like this:

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

I'm thinking it would be good to introduce some OO techniques, such as having a "base" depedency class, which takes a ILoggingService in it's protected ctor. Then you might have another child dependency which inherits, etc.

Has anyone done this before? Is this something StructureMap can do for me, or is it simply me rolling my own basic code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

落在眉间の轻吻 2024-12-27 03:59:37

日志记录

每个控制器中都需要依赖项时,就可以肯定地表明它不是“正常”依赖项,而是跨领域关注点< /强>。日志记录是横切关注点的典型示例,因此 ILoggingService 应该像任何其他横切关注点一样处理。

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

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

其他依赖项

对于其他依赖项,将它们重构为外观服务。这涉及识别相关服务的自然集群,因此具体如何完成取决于每个代码库。

Logging

When a dependency is required in each and every Controller it's a pretty certain indicator that it's not a 'normal' dependency, but rather a Cross-cutting Concern. Logging is the archetypical example of a Cross-cutting Concern, so ILoggingService should be dealt with like any other Cross-cutting Concern.

In SOLID OO the appropriate way to address a Cross-cutting concern would be to employ a Decorator (which can be generalized towards AOP). However, ASP.NET MVC Controller action methods aren't part of any interface, so that's a less ideal solution.

Instead, the MVC framework provides Action Filters for interception purposes. If you want to implement a loosely coupled filter, do yourself a favor and implement it as a global filter instead of an attribute.

Other dependencies

For other dependencies it makes sense to refactor them to Facade Services. This involves identifying natural clusters of related services, so exactly how this is done is specific to each code base.

晨与橙与城 2024-12-27 03:59:37

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

I know i accepted @Mark Seeman's answer a while ago, but i only finally got time to implement this now, so thought i'd share what i actually did, for other's benefit.

Basically, i created wrapper interfaces for the "groups" of dependencies in my application.

Example:

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

And the implementation:

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; } }
}

Pretty simple really.

Then i updated my controllers.

Example ctor Before:

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

After:

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

Nothing special really, just set of wrappers. Under the hood, the controllers still utilise the same dependencies, but the ctor signature is much smaller, more readable, and it also makes unit testing easier.

感情洁癖 2024-12-27 03:59:37

我看到了几个选项:

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

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

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

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

I see a few options:

  1. Fascade services like @32bitkid mentioned.
  2. Break your controllers into finer grained groups of action methods that have more common dependencies.
  3. Static entry points. (I know many people don't like them, but I find them quite useful for my core services that never change, even with DI.) Here's an example utilizing the Common Service Locator.

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

Usage: Logger.Instance().Log(message);

Testing: Logger.Instance = () => new TestLogger();

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文