Использование фабрик с внедрением зависимостей для оптимизации создания объектов в веб-запросе - PullRequest
1 голос
/ 09 апреля 2019

Короче говоря, это тип контроллеров, которые я вижу в каждой кодовой базе профессионально:

    //All in One Service interfaces
    public class DiController : ControllerBase
    {
        private readonly IDiService _diService;

        public DiController(IDiService diService)
        {
            _diService = diService;
        }

        [HttpGet]
        public IActionResult GetA()
        {
            return Ok(_diService.GetA());
        }

        [HttpGet]
        public IActionResult GetB()
        {
            return Ok(_diService.GetB());
        }
    }

    //Task-based interfaces 
    public class DiController : ControllerBase
    {
        private readonly IAService _aService;
        private readonly IBService _bService;

        public DiController(IAService aService, IBService bService)
        {
            _aService = aService;
            _bService = bService;
        }

        [HttpGet]
        public IActionResult GetA()
        {
           return Ok(_aService.Handle());
        }

        [HttpGet]
        public IActionResult GetB()
        {
            return Ok(_bService.Handle());
        }
    }

Теперь, чтобы сохранить небольшой размер поста, представьте, что у вас есть один репозиторий для A и другой для B, который будет использоваться сервисами. Каждый компонент здесь Scoped (один и тот же объект для каждого запроса). Неважно, что вы выбираете, вы в конечном итоге в такой ситуации:

enter image description here

Это относится к интерфейсу на основе задач, и, как вы можете видеть, вы не используете службу B, но получаете один экземпляр независимо. В другом случае вам будут созданы два репозитория.

Чтобы решить эту проблему, я использовал следующие фабрики:

1 - Абстрактный класс, который содержит контейнер и свойство, которое предоставляет экземпляр типа.

    public interface IGenericFactory<out T>
    {
        T Service { get; }
    }

    public abstract class GenericFactory<T> : IGenericFactory<T> where T : class
    {
        private readonly Container _container;
        public T Service => _container.GetInstance<T>();

        protected GenericFactory(Container container)
        {
            _container = container;
        }
    }

2 - интерфейс для регистрации в контейнере

public interface IAServiceFactory : IGenericFactory<IAService>
{
}

3- Класс для регистрации в контейнере

public class AServiceFactory : GenericFactory<IAService>, IAServiceFactory
{
    public AServiceFactory(Container container) : base(container)
    {

    }
}

4- Зарегистрируйте фабрики как Singletons, а сервисы как Scoped / Transient (в зависимости от варианта использования). Вот пример (в Simple Injector) того, как выполняется регистрация для контроллера интерфейса на основе задач:

_container.Register<IAService, AService>(Lifestyle.Scoped);
_container.Register<IBService, BService>(Lifestyle.Scoped);

_container.Register<IAServiceFactory, AServiceFactory>(Lifestyle.Singleton);
_container.Register<IBServiceFactory, BServiceFactory>(Lifestyle.Singleton);

Конечный продукт будет таким:

enter image description here

Экземпляр A, а не B.

Это правильно? Я участвовал в проектах, в которых у вас было бы дюжина Сервисов или Репозитариев, и только один из них использовался бы в каждом вызове.

Спасибо.

1 Ответ

1 голос
/ 09 апреля 2019

Вы можете ввести в действие:

public IActionResult About([FromServices] IDateTime dateTime)
{
    ViewData["Message"] = $"Current server time: {dateTime.Now}";

    return View();
}

https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/dependency-injection?view=aspnetcore-2.2#action-injection-with-fromservices

...