Консолидация зависимостей контроллера ASP.NET MVC (StructureMap) - PullRequest
8 голосов
/ 08 декабря 2011

Я смотрю на контроллеры на моем веб-сайте, и большинство их конструкторов выглядят так:

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

Другими словами, это очень проблематично и затрудняет рефакторинг.Некоторые контроллеры имеют около 8 зависимостей.

Можно ли как-нибудь "сгруппировать" эти зависимости в одно или несколько сегментов?

Например, ILoggingService требуется в каждом контроллере, IGeospatialService требуется для контроллеров, которые выполняют пространственную работу, а IServiceOne и IServiceTwo требуется только в определенных случаях.

Я хотел бы видеть что-то вроде этого:

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

Я думаю, что было бы хорошо представить некоторые методы ОО, такие как наличие «базового» класса зависимостей, который занимает ILoggingService в защищенном ctor.Тогда у вас может быть другая дочерняя зависимость, которая наследуется и т. Д.

Кто-нибудь делал это раньше?Это что-то, что StructureMap может сделать для меня, или это просто мой собственный базовый код?

Ответы [ 3 ]

13 голосов
/ 08 декабря 2011

Вход

Когда требуется зависимость в каждом контроллере, это довольно определенный показатель того, что это не «нормальная» зависимость, а скорее сквозная проблема . Ведение журнала является архетипическим примером сквозной проблемы, поэтому с ILoggingService следует обращаться, как с любой другой сквозной проблемой.

В SOLID OO надлежащим способом решения сквозной проблемы будет использование Decorator (который можно обобщить для AOP ). Однако методы действия ASP.NET MVC Controller не являются частью какого-либо интерфейса, поэтому это менее идеальное решение.

Вместо этого платформа MVC предоставляет Фильтры действий для целей перехвата. Если вы хотите реализовать слабо связанный фильтр, сделайте себе одолжение и реализуйте его как глобальный фильтр вместо атрибута .

Другие зависимости

Для других зависимостей имеет смысл преобразовать их в Facade Services . Это включает в себя идентификацию естественных кластеров связанных служб, поэтому, как именно это делается, зависит от каждой кодовой базы.

3 голосов
/ 23 февраля 2012

Я знаю, что принял ответ @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 намного меньше, более читабельна, а также облегчает модульное тестирование.

0 голосов
/ 08 декабря 2011

вижу несколько вариантов:

  1. Фасадные сервисы, такие как @ 32bitkid.
  2. Разбейте ваши контроллеры на более мелкие группы методов действий, которые имеют более общие зависимости.
  3. Статические точки входа. (Я знаю, что многим они не нравятся, но я нахожу их весьма полезными для моих основных служб, которые никогда не меняются, даже с DI.) Вот пример использования Common Service Locator.

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

Использование: Logger.Instance().Log(message);

Тестирование: Logger.Instance = () => new TestLogger();

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...