Замок Виндзор в библиотеке пакетов NuGet - PullRequest
2 голосов
/ 05 августа 2020

Это основано на концепциях, обсуждаемых в этом вопросе Как управлять зависимостями Castle Windsor в библиотеке nuget .

Я работаю над проектом, который использует Castle.Windsor для DI и ссылается на несколько пользовательские пакеты NuGet. Я установил Castle.Windsor в одну из библиотек классов (назовем ее MyNugetLib), на которую ссылается NuGet, и я определил метод расширения в IWindsorContainer в этой библиотеке, который регистрирует локальные службы.

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

проект, который ссылается на этот пакет NuGet (назовем его MyProj), также использует Windsor и в установщике вызывает метод расширения, определенный в пакете NuGet. Все это работает. Мой вопрос: как передать ссылку на контейнер Windsor в MyNugetLib из MyProj? Я пробовал внедрить IWindsorContainer в конструктор класса, где он мне нужен, но этот класс использует ленивую инстанциацию следующим образом:

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

public static MyClass Instance => LazyInstance.Value;

Из MyProj это вызывается следующим образом:

Lib.MyClass.Instance

Единственный способ, которым мне удалось выполнить эту работу, - это раскрыть свойство publi c в MyClass, которое используется в MyProj для установки WindsorContainer

public IWindsorContainer DIContainer { get; set; }

в MyProj

Lib.MyClass.DIContainer = MyProj.WindsorContainer

Мне это не особенно нравится. Есть ли лучший способ сделать это?

Обновление: (благодаря предложениям insane_developer)

На самом деле вопрос заключается в следующем: как внедрить зависимости в ленивый конструктор? Если бы я мог это сделать, то я мог бы удалить зависимость от Windsor из MyNugetLib.

Так что у меня было бы

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(???));

Как мне написать забаву c выше, чтобы он вводил зависимости в моем конструкторе?

Ответы [ 2 ]

1 голос
/ 05 августа 2020

Я никогда не использовал Castle.Windsor, но предполагаю, что он будет похож на другие контейнеры. Вам действительно не нужно передавать ссылку на контейнер в вашу библиотеку. Вы должны иметь возможность настраивать все свои сопоставления в основном приложении, которое имеет ссылку на вашу библиотеку. Если вы сделаете то, что предлагаете, у вас будет конкретная зависимость от Castle.Windsor, что не очень хорошая идея.

0 голосов
/ 07 августа 2020

Благодаря предложениям @ insane_developer, я смог сделать это намного чище. Проблема заключалась в том, что MyNugetLib использовал stati c синглтоны, созданные с использованием Lazy<T>. Зависимости не могли быть введены в эти конструкторы, поэтому я пытался найти хороший способ передать ссылку на контейнер DI. Как бы то ни было, это может быть сделано, оно вводит прямую зависимость от пакета DI в MyNugetLib, что не очень хорошо. Лучшим решением является последовательное использование внедрения зависимостей в MyNugetLib, рефакторинг классов с помощью stati c singleletons, чтобы иметь нормальные конструкторы с внедренными зависимостями, и регистрировать эти зависимости из основного приложения (MyProj). Для этого мне пришлось реорганизовать способ, которым MyProj использует некоторые классы MyNugetLib, чтобы он больше не ссылался на синглтоны через свойства stati c, а через внедренные зависимости, которые зарегистрированы в основном контейнере DI как синглтоны. и введены как ленивые зависимости. Это то, что я сделал (концептуально).

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();
        }
    }
}

Затем в 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
        }
    }
}

Используя IMyNugetLibSingleton и IMyNugetLibTransient, я могу зарегистрировать все классы, реализующие это с одной единственной заявкой register. Теперь я переместил "лень" из MyNugetLib в MyProj. Внедренная зависимость myNugetLibLazyService разрешается только при необходимости. Зависимости MyNugetLib теперь могут быть разрешены с помощью любого DI contanier по выбору потребителя.

Я доволен этим решением, но все же открыт для предложений, если у кого-то есть лучшие идеи.

...