2

This builds on concepts discussed in this question How to manage Castle Windsor dependencies in a nuget library.

I am working on a project which uses Castle.Windsor for DI and references several custom NuGet packages. I have installed Castle.Windsor in one of the class libraries (let's call it MyNugetLib) referenced via NuGet and I've defined an extension method on IWindsorContainer in this library which registers the local services.

public static class WindsorContainerExtensions
{
    public static void RegisterMyNugetLibServices(this IWindsorContainer container)
    {
        container.RegisterLocalServices();
    }
    
    private static void RegisterLocalServices(this IWindsorContainer container)
    {
        container.Register(...)
    }
}

The project that references this NuGet package (let's call it MyProj) uses Windsor as well and in the installer it calls the extension method defined in the NuGet package. This all works. My question is how do I pass the Windsor container reference to MyNugetLib from MyProj? I have tried injecting IWindsorContainer in the constructor of a class where I need it, but this class uses Lazy instantiation as follows:

private static readonly Lazy<MyClass> LazyInstance = new Lazy<MyClass>(() => new MyClass());
    
private MyClass() {}

public static MyClass Instance => LazyInstance.Value;

From MyProj this is called as follows:

Lib.MyClass.Instance

The only way in which I've managed to make this work is exposing a public property in MyClass which is used in MyProj to set the WindsorContainer

public IWindsorContainer DIContainer { get; set; }

in MyProj

Lib.MyClass.DIContainer = MyProj.WindsorContainer

I don't particularly like this. Is there a better way of doing this?


Update: (thanks to insane_developer's suggestions)

The question really is: how do you inject dependencies in a Lazy constructor? If I could do this, then I could remove the dependency on Windsor from MyNugetLib.

So I would have

private IService1 service1;
private IService2 service2;

private MyClass(IService1 myService1, IService2 myService2) 
{
    this.service1 = myService1;
    this.service2 = myService2
}

public static MyClass Instance => LazyInstance.Value;

private static readonly Lazy<MyClass> LazyInstance = new Lazy<MyClass>(() => new MyClass(???));

How do I write the func above so it injects dependencies in my constructor?

erionpc
  • 368
  • 3
  • 15
  • 1
    Just to point out, having a public constructor in a singleton class completely defeats the purpose of it being singleton. – insane_developer Aug 05 '20 at 13:49
  • you're right. I'll change that. thanks – erionpc Aug 05 '20 at 13:50
  • I don't get what's the purpose of `Lib.MyClass.DIContainer`, can you explain it again? – Matteo Umili Aug 05 '20 at 13:59
  • It's used in Lib.MyClass to resolve local services. E.g. [code]internal IMyLibService TheService { get { if (this.myLibService == null) { this.myLibService = this.DIContainer.Resolve(); } return this.myLibService; } }[/code] – erionpc Aug 05 '20 at 15:34
  • @erionpc Then I think that you can inject the container in your `MyClass` (by adding a parameter to the constructor. Castle Windsor will automatically inject the correct container) – Matteo Umili Aug 06 '20 at 07:05
  • Thanks @MatteoUmili. The LazyInstance doesn't allow me to define parameters in the constructor, unfortunately. – erionpc Aug 06 '20 at 08:08
  • 1
    @erionpc Don't use a static field, instead create a class like MyClassProvider where you'll put Instance and LazyInstance as instance fields (so not static). Then register this class as singleton and pass the container to this class – Matteo Umili Aug 06 '20 at 09:40
  • Thanks @MatteoUmili. I thought of that. Essentially, not use Lazy in MyClass but use it in MyProj and remove dependencies on the container from MyNugetLib. In this way the container in MyProj would resolve MyClass as a singleton by configuration. The only drawback from this is that I'm going to need to change everything that uses MyNugetLib so that classes are injected instead of being retrieved via the ClassName.Instance static property. – erionpc Aug 06 '20 at 09:58
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219325/discussion-between-erionpc-and-matteo-umili). – erionpc Aug 06 '20 at 10:11

2 Answers2

2

I never used Castle.Windsor but I assume it would be like other containers. You don't really need to pass a reference to the container to your library. You should be able to configure all your mappings in the main application, which has a reference to your library. If you do what you suggest, you will have a concrete dependency on Castle.Windsor, which is not a good idea.

  • Ideally I would like MyNugetLib to be independent from MyProj, register its own services and not have a dependency on the DI container defined in MyProj, but I'm not sure if this is possible. Thanks – erionpc Aug 05 '20 at 15:36
  • @erionpc I really don't know how much of a good idea that is, but I guess it is possible to do. Maybe you are doing this for yourself only, but still not a good idea. – insane_developer Aug 05 '20 at 15:51
  • I'm open to suggestions. What would be the best approach in your opinion? If it's the one in your last answer, how would you resolve the dependencies in MyNugetLib if you register all your mappings in the main application (MyProj)? – erionpc Aug 05 '20 at 16:11
  • 1
    @erionpc if you are using constructor dependency injection, you don't have to do anything in the library. The container can register and resolve dependencies in referenced assemblies. If you need access to the container itself, then what would be the reason for it? I should be able to use your library with or without a container, and if I use a container, I should be able to use the container of my choice. – insane_developer Aug 05 '20 at 16:24
  • I like that idea. In the library I have classes which use lazy instantiation (see example above). I can't inject dependencies in the constructor in these classes so I need a reference to the container. How would I go about doing that following your suggestion? – erionpc Aug 05 '20 at 18:22
  • The whole point of a container is to manage dependency creation and its lifetime. I think you are designing a library with more of an application rather than reusability approach. Anyway, you may be able to specify how the instance is to be created, like by calling the Instance property, or get rid of that and mark is as singleton in the dependency's configuration. How to do it with Castle.Windsor? Can't help you there. – insane_developer Aug 05 '20 at 18:49
  • To give some more context, this is a very large and old application and I needed to extend the functionality of one of the referenced NuGet libraries. However, I wanted to use IoC in the library (it didn't use it before) and so I thought of using the same DI that was used in the main application. The library has some lazy initialisation in place which I can't change and I don't know of a way in which I can inject dependencies in these constructors. This is why I need a reference to the IoC container, and why I posted the question in the first place. Thanks for your suggestions. – erionpc Aug 05 '20 at 19:34
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219321/discussion-between-erionpc-and-insane-developer). – erionpc Aug 06 '20 at 08:26
0

Thanks to @insane_developer's suggestions I was able to do this in a much cleaner way. The problem was that MyNugetLib was using static singletons created using Lazy<T>. Dependencies couldn't be injected in those constructors, therefore I was trying to figure out a good way to pass a reference to the DI container. However this may be done, it introduces a direct dependency on the DI package within MyNugetLib, which isn't great. The better solution is to use dependency injection consistently within MyNugetLib, refactor the classes with static singletons to have normal constructors with injected dependencies and register those dependencies from the main application (MyProj). In order to do this I had to refactor the way in which MyProj uses some of MyNugetLib classes so that it doesn't refer to singletons via static properties anymore, but via injected dependencies which are registered within the main DI container as singletons and injected as lazy dependencies. This is what I did (conceptually).

namespace MyNugetLib.DI
{
    public interface IMyNugetLibSingleton {}

    public interface IMyNugetLibTransient {}
}

namespace MyNugetLib
{
    public interface IMyClass : IMyNugetLibSingleton 
    {
        MyType DoSomething();
    }

    public interface IMyService : IMyNugetLibTransient 
    {
        MyOtherType DoSomethingElse();
    }

    public MyService : IMyService 
    {
        MyOtherType DoSomethingElse() 
        {
            // do something important
        }
    }

    public MyClass : IMyClass 
    {
        private IMyService myService;

        public MyClass(IMyService myInjectedService) 
        {
            this.myService = myInjectedService;
        }

        MyType DoSomething() 
        {
            // doing something important using this.myService
            this.myService.DoSomethingElse();
        }
    }
}

Then, in MyProj

namespace MyProj
{
    public class WindsorInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            // enable registration of lazy services
            container.Register(Component.For<ILazyComponentLoader>().ImplementedBy<LazyOfTComponentLoader>());
            
            container.Register(Classes.FromAssemblyContaining(typeof(MyNugetLib.DI.IMyNugetLibSingleton)).BasedOn<MyNugetLib.DI.IMyNugetLibSingleton>().WithService.FromInterface().LifestyleSingleton());
            
            container.Register(Classes.FromAssemblyContaining(typeof(MyNugetLib.DI.IMyNugetLibTransient)).BasedOn<MyNugetLib.DI.IMyNugetLibTransient>().WithService.FromInterface().LifestyleTransient());
            
            // register local services
        }
    }
    
    public class MyLocalClass : IMyLocalClass
    {
        private Lazy<MyNugetLib.IMyClass> myNugetLibLazyService
        
        public MyLocalClass(Lazy<MyNugetLib.IMyClass> lazyService)
        {
            this.myNugetLibService = lazyService;
        }
        
        internal MyLocalType DoSomethingHere() 
        {
            this.myNugetLibService.Value.DoSomething();
            // etc
        }
    }
}

Using IMyNugetLibSingleton and IMyNugetLibTransient I'm able to register all the classes which implement these with one single register statement. I have now moved the "lazyness" from MyNugetLib to MyProj. The injected dependency myNugetLibLazyService is resolved only when needed. MyNugetLib's dependencies can now be resolved using any DI contanier, at the choice of the consumer.

I'm pleased with this solution, but still open to suggestions if someone has any better ideas.

erionpc
  • 368
  • 3
  • 15