0

I ran out of ideas, so I decided to ask for some in here. Currenty I am workig on a .Net class library, and I decided to split the library class into a service classes too (it uses IO, config).

Inside of the library I am using Autofac IoC for DI. But anyway, as it turned out, after adding the library into my project, where the project has his own Autofac IoC, I am getting exceptions from my ViewModelLocator:

Cannot resolve parameter 'G_Hoover.Services.Logging.IFileService fileService' of constructor 'Void .ctor (...)

NOTE: IFileService is a dependency of the library, not application project. Inside of the lib I am registering:

public static IContainer BootStrap()
        {
            var builder = new ContainerBuilder();

            builder.RegisterType<FileService>()
              .As<IFileService>().SingleInstance();

            //other services

            return builder.Build();
        }

What means, that IoC of the project wants to resolve dependencies from my class library.

After doing some research, I found this andswer, where was suggested to resolve dependencies internally in my library, where the project does not know about them. But how to do it?

Currently my approach is, that from application project I am calling exposed initial property of the library, according to this article about async init, and with this property I am trigerring RunConfigAsync() in my library, where I try to resolve my library dependencies:

private async Task RunConfigAsync()
        {
            IContainer container = BootStrapper.BootStrap();
            container.Resolve<IConfigService>();
            container.Resolve<IFileService>();
            container.Resolve<IStringService>();

            ConfigModel config = _configService.GetConfig();

            //do some other config and init stuff
        }

BUT the problem is, as I belive, that my ViewModelLocator in application project is calling my ViewModel, which has my library as a dependency, before dependencies in my library are resolved. So I tryed to resolve my library at the beginning in VMLocator, like this:

public class ViewModelLocator
    {
        IContainer _container;

        public ViewModelLocator()
        {
            _container = BootStrapper.BootStrap();

            _container.Resolve<IParamsLogger>();
        }

        public BrowserViewModel BrowserViewModel
        {
            get
            {
                return _container.Resolve<BrowserViewModel>();
            }
        }
        // other viewmodels below
    }

Unfortunatelly I am still getting the same exception like before. Right now I have no idea how to resolve dependencies internally inside of a library, when using Autofac (or other IoC framework), and we arrived to the point, where I need to ask more experienced devs for an advice.

bakunet
  • 3
  • 5
  • Please note that only the end application should depend on a DI Container; not its class libraries. Familiarize yourself with the concept of [the Composition Root](https://freecontent.manning.com/dependency-injection-in-net-2nd-edition-understanding-the-composition-root/). For reusable class libraries (e.g. NuGet packages), a similar advice holds. See: [DI-Friendly library](https://blog.ploeh.dk/2014/05/19/di-friendly-library/). – Steven Sep 01 '19 at 13:30
  • 1
    Might be good if your question title had more to do with your question (eg, mention the fact there's a view model and such). This isn't just "resolving a type internally." Better question titles can get more appropriate eyes on the question and get you better help. – Travis Illig Sep 01 '19 at 17:17
  • @TravisIllig actually the library has not anyviewmodel or a view. ViewModelLocator is in a application project and this project just uses the library. – bakunet Sep 02 '19 at 05:24
  • @Steven why I can not treat `Initialize` method as a `Composition root` in my library? Anyway, if I should not use DI in my library, what to use instead, if I want to test the lib, and/or if the lib is using dependencies that I need to mock, for example IO? – bakunet Sep 02 '19 at 05:52
  • Just trying to help you get more folks looking at your question. The title doesn't seem to describe what you're asking. – Travis Illig Sep 02 '19 at 16:42
  • @TravisIllig I appreciate this. But the only I am asking, is that having Autofac IoC in my library project is a good practice, if not, how to replace it? Or get rid of DI completly and place all in one class? – bakunet Sep 03 '19 at 09:25
  • Fair enough... But if that's what you're asking, that's not what your question title says. – Travis Illig Sep 03 '19 at 13:05
  • Ok, I updated my question, a bit. Actually it changed during comments appering and my research. – bakunet Sep 03 '19 at 23:42

1 Answers1

0

UPDATE

Inspired with this article and this SO answer I made up kind of solution. Please correct me if I am wrong.

All here is about hiding library dependencies from the sight of application project. Otherwise application project will cry with exceptions that libraries dependencies were not registered in his IoC. But they are registered already in libraries IoC and we do not want to do it again. Right? Currently the problem was, that they were injected into libraries public constructor and were seen also by application project.

As someone adviced, facade pattern comes here for rescue. We supposed to hide dependencies inside, and Resolve facade in library class somewhere at initialization. Simple example I made up below, showing how I registered library services in librarys IoC as a singletons:

public class Booter
    {
        public static IContainer BootStrap()
        {
            var builder = new ContainerBuilder();

            builder.RegisterType<FilesService>()
              .As<IFilesService>().SingleInstance();

            builder.RegisterType<ControlsService>()
              .As<IControlsService>().SingleInstance();

            builder.RegisterType<Facade>()
              .AsSelf().SingleInstance();

            return builder.Build();
        }
    }

Into facade we are injecting library dependencies:

public class Facade
    {
        IFilesService _fileService;
        IControlsService _controlsService;
        public Facade(IFilesService filesService, IControlsService controlsService)
        {
            _fileService = filesService;
            _controlsService = controlsService;

            FilesService = _fileService;
            ControlsService = _controlsService;
        }

        public IFilesService FilesService { get; }

        public IControlsService ControlsService { get; }
    }

Next, in our lib, at first need to call Initialize, and Resolve inside facade, and to interfaces we declare facade properties that they are actually injected services:

public class MainService : IMainService
    {
        IFilesService _fileService;
        IControlsService _controlsService;
        IContainer _container;
        public MainService()
        {
            Initialize();
        }

        public void Initialize()
        {
            _container = Booter.BootStrap();

            var aggregator = _container.Resolve<Facade>();

            _fileService = aggregator.FilesService;
            _controlsService = aggregator.ControlsService;
        }

        public UiControlsModel SwitchOff()
        {
            return _controlsService.SwitchOff();
        }

        public UiControlsModel SwitchOn()
        {
            return _controlsService.SwitchOn();
        }
    }

I really hope that it is not any anti-pattern.

bakunet
  • 3
  • 5
  • The constructor of a form create lots of object in the intializer as well after the constructor in called. So not all code can be put in a form constructor and must be put into the Load Method. – jdweng Aug 31 '19 at 12:58
  • @jdweng what **form** and what **Load Method** do you mean? Do you mean facade and Initialize? – bakunet Sep 02 '19 at 05:19
  • The initialize method is usually part of a form method so I assume there was a form. Still if you do not have a form you have to be careful to make sure all object are constructed before you call any methods. The form constructor on exit from Initialize method executes code in the base class. I suspect in your case you can't set the fileService property until the base class code is executed. – jdweng Sep 02 '19 at 08:58
  • @jdweng Actually yes, viewmodel, that I inject the library to, may run Initialization of the library and awaits it, so everything in the library is resolved before the main application is proceeding. – bakunet Sep 03 '19 at 09:51
  • As I said, you may have to move the code from the Initialize() method to the main thread. There is code in the class constructor that is run on the return from Initialize() that may need to be run before your custom code. – jdweng Sep 03 '19 at 09:58