IoC Initialize a service using hard work in the constructor, but avoiding the temporary Init () method

Hi I am using an IoC container, and I would like to initialize a service (part of which includes "hard work" talking to the database) inside the constructor.

This particular service stores information found by the injected IPluginToServiceProviderBridge service, this information is stored in the database through UnitOfWork .

Once everything is loaded, controllers with commands and services with handlers are used for all other interactions. All commands are wrapped within the validity period, so saving and deleting UnitOfWork is done by the handler, not the service (this is great for clean code).

The same accuracy and separation of problems for saving and transactions does not apply to the Initializer inside the service, since everything happens in the constructor:

 public PluginManagerService( IPluginToServiceProviderBridge serviceProvider, IUnitOfWork unitOfWork) { this.unitOfWork = unitOfWork; this.serviceProvider = serviceProvider; lock (threadLock) { if (initialised == false) { LinkPluginsWithDatabase(); initialised = true; } // I don't like this next line, but // not sure what else to do this.UnitOfWork.Save(); } } protected void LinkPluginsWithDatabase() { var plugins = this.serviceProvider.GetAllPlugins(); foreach (var plugin in plugins) { var db = new PluginRecord { interfaceType = plugin.InterfaceType; var id = plugin.Id; var version = plugin.Version; } // store in db via unit of work repository this.unitOfWork.PluginsRepository.Add(db); } } 

A few points:

Ideally, I want to avoid using a factory, since this complicates the processing of the validity of the region, I would be happy to reorganize for a better separation, if I knew how to do it.

I really want to avoid using a separate Init() method for the service, while it will allow transactions and save with a command / handler, a lot of validation code will be required, and I believe that this will also lead to temporary problems.

Given the above, is it permissible to call UnitOfWork.Save() in my constructor, or can I reorganize for cleaner code and better separation?

+6
source share
1 answer

Providing a service designer with more than just storing its dependencies in private fields is considered a bad practice when using dependency injection, because it allows the graphing of an object to fail, slows down the graphing and complicates unit testing.

What I read from your question is that you need to do some initialization when starting the application. This is fine, since it’s normal to have some initialization phase, but don’t do this inside the constructor. Just move this initialization to the end of the application launch code after setting up the container (and after optionally checking your configuration).

I assume that your code will look like this:

 public void Application_Start(object s, EventArgs e) { Container container = new Container(); Bootstrap(container); InitializeApplication(container); } private void InitializeApplication( Container container) { using (this.container.BeginLifetimeScope()) { var pluginManager = this.container .GetInstance<PluginManagerService>(); pluginManager.LinkPluginsWithDatabase(); var unitOfWork = container.GetInstance<IUnitOfWork>(); unitOfWork.Save(); } } 

You can even write a decorator for your PluginManagerService , but it might be a little designed, BUT ... it might look like this:

 public class InitializingPluginManagerServiceDecorator : IPluginManagerService { private static readonly object syncRoot = new object(); private static bool initialized; private IPluginManagerService decorated; private Container container; public InitializingPluginManagerServiceDecorator( IPluginManagerService decorated, Container container, IPluginToServiceProviderBridge serviceProvider) { this.pluginManagerService = pluginManagerService; this.container = container; this.serviceProvider = serviceProvider; } public void PluginManagerServiceMethod() { this.InitializeInLock(); this.decorated.PluginManagerServiceMethod(); } private void InitializeInLock() { if (!initialized) { lock (syncRoot) { if (!initialized) { this.InitializeInScope(); } } initialized = true; } } private void InitializeInScope() { using (this.container.BeginLifetimeScope()) { this.InitializeWithSave(); } } private void InitializeWithSave() { var uow = this.container.GetInstance<IUnitOfWork>() var initializer = this.container .GetInstance<PluginManagerServiceInitializer>(); initializer.Initialize(); uow.Save(); } } 

This decorator can be wrapped around an IPluginManagerService and ensures that the system is initialized immediately before the IPluginManagerService we used for the first time and ensures that it is initialized only once. The actual initialization logic is moved to a separate class (SRP), on which the decorator depends:

 public class PluginManagerServiceInitializer { private IUnitOfWork unitOfWork; private IPluginToServiceProviderBridge serviceProvider; public PluginManagerServiceInitializer( IUnitOfWork unitOfWork, IPluginToServiceProviderBridge serviceProvider) { this.unitOfWork = unitOfWork; this.serviceProvider = serviceProvider; } public void Initialize() { var plugins = from plugin in this.serviceProvider.GetAllPlugins() select new PluginRecord { interfaceType = plugin.InterfaceType; var id = plugin.Id; var version = plugin.Version; }; unitOfWork.PluginsRepository.AddRange(plugins); } } 
+5
source

Source: https://habr.com/ru/post/918715/


All Articles