StructureMap и Lazy Initialization через конструктор с интерфейсами - PullRequest
0 голосов
/ 07 сентября 2018

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

 private readonly IFirstService _firstService;
 private readonly ISecondService _secondService;

 private readonly ILog _log;

 public ProductController(IFirstService firstService, ISecondService secondService, ILog log)
 {
       _firstService = firstService;
       _secondService = secondService;

       _log = log;
 }

 [Route("Default")]
 public ActionResult First()
 {
       var model = _firstService.DoIt();
       return View("~/Views/First/index.cshtml", model);
 }

 [Route("Default")]
 public ActionResult Second()
 {
       var model = _secondService.DoIt();
       return View("~/Views/Second/index.cshtml", model);
 }

Основная проблема этого решения заключается в том, что я вызываю контроллер, затем он создает 2 экземпляра (один для firstService и второй для secondService), но эти службы я вызываю для конкретного метода страницы контроллера.

Например, вызов службы в интерфейсе конструктора для фабрики, загрузчик журналов и репозитория, что означает, что когда я вызываю конструктор контроллера, я загружаю все репозитории из обеих служб-

  • Могу ли я использовать C # .NET Lazy (T) или Func?
  • Можно ли использовать интерфейс в качестве параметра в методе для выбранной страницы?
  • Можно ли использовать кэш для данных только для чтения из хранилища?
  • Другое решение?

Когда я использую Lazy, я получил сообщение, тогда вызываемая процедура не определена

Я ищу лучшее архитектурное решение, пробую Lazy, а также оптимизирую код, но я всегда сталкивался с проблемой

Edit:

Регистрация контейнера StructureMap

Scan(
    scan =>
    {
           scan.TheCallingAssembly();
           scan.WithDefaultConventions();
           scan.With(new ControllerConvention());
    });

For<ILog>().Use(c => LogManager.GetLogger(GetType())).Singleton();

For<IFirstService>().Use<FirstService>().Singleton();
For<ISecondService>().Use<SecondService>().Singleton();

Мое решение:

  • Можно ли использовать интерфейс в качестве параметра в методе для выбранной страницы?

В конструкторе класса я использую интерфейс StructureMap

private readonly IContainer _container;
private readonly ILog _log;

public ProductController(IContainer container, ILog log)
{
       _container = container;
       _log = log;
}

А в методе я использую

var model = _container.GetInstance<IFirstService>().DoIt();
  • Можно ли использовать кэш для данных только для чтения из хранилища?

Я использую .NET lib с использованием статического System.Web.HttpRuntime; и использую приведенный ниже код в методе, вызываемом в конструкторе класса репозитория

if (!(Cache[_cacheName] is IEnumerable<YourObject> result)) // Cache is empty
            {
                _log.Info("-- Loading from DB --");
                lock (CacheLockObject)
                {
                    result = Cache[_cacheName] as IEnumerable<YourObject>;
                    if (result == null)
                    {
                        result = LoadAll(); // load data from DB
                        Cache.Insert(_cacheName, result, null,
                            DateTime.Now.AddMinutes(10), TimeSpan.Zero);
                    }

                    return result;
                }
            }

            _log.Info("-- Loading from Cache --");
            return result;

Спасибо

1 Ответ

0 голосов
/ 11 сентября 2018

Текущее решение для внедрения зависимостей использует анти-шаблон сервисного локатора. Контейнер не должен передаваться как зависимость. Это является четким показателем сервисного локатора.

Вы можете отложить инициализацию, используя Lazy<T> или Func<T>

Например, следующие варианты использования Func<T>

private readonly Func<IFirstService> _firstService;
private readonly Func<ISecondService> _secondService;    
private readonly ILog _log;

public ProductController(Func<IFirstService> firstService, Func<ISecondService> secondService, ILog log) {
    _firstService = firstService;
    _secondService = secondService;    
    _log = log;
 }

[Route("Default")]
public ActionResult First() {
    IFirstService service = _firstService();//invoke delegate to get service
    var model = service.DoIt();
    return View("~/Views/First/index.cshtml", model);
}

[Route("Default")]
public ActionResult Second() {
    ISecondService service = _secondService();
    var model = service.DoIt();
    return View("~/Views/Second/index.cshtml", model);
}

Func действует как фабричный делегат, который задерживает инициализацию / активацию зависимостей только тогда, когда они необходимы.

Таким образом, в приведенном выше примере, если запрашивается First(), то для этого запроса вызывается только делегат _firstService(), а не обе службы.

Вы можете сделать то же самое с Lazy<T>

private readonly Lazy<IFirstService> _firstService;
private readonly Lazy<ISecondService> _secondService;    
private readonly ILog _log;

public ProductController(Lazy<IFirstService> firstService, Lazy<ISecondService> secondService, ILog log) {
    _firstService = firstService;
    _secondService = secondService;    
    _log = log;
 }

[Route("Default")]
public ActionResult First() {
    IFirstService service = _firstService.Value;//lazy load service
    var model = service.DoIt();
    return View("~/Views/First/index.cshtml", model);
}

[Route("Default")]
public ActionResult Second() {
    ISecondService service = _secondService.Value;
    var model = service.DoIt();        
    return View("~/Views/Second/index.cshtml", model);
}

Документация StructureMap: отложенное разрешение

StructureMap имеет некоторые встроенные функциональные возможности для "отложенных" разрешенных зависимостей, так что вместо того, чтобы ваша прикладная служба получала прямую зависимость от IExpensiveToBuildService, которая может не потребоваться, вы могли бы вместо этого заставить StructureMap выполнять зависимость от Lazy<IExpensiveToBuildService> или Func<IExpensiveToBuildService>, который можно использовать для извлечения этого дорогостоящего сервиса только тогда, когда он необходим из любого Контейнера, изначально создавшего родительский объект.

...