Зарегистрировать сервис, чтобы Resolve (args) передавал аргументы как тупые позиционные аргументы? - PullRequest
1 голос
/ 16 октября 2019

У меня есть IFormFactory в моем проекте Windows Forms с параметризованными методами, которые возвращают конкретные Form подклассы. Раньше я использовал Ninject.Extensions.Factory Bind<IFormFactory>().ToFactory() для привязки фабрики к динамически сгенерированной прокси-реализации. (При вызове метода в интерфейсе Ninject автоматически вызывает конструктор с наиболее подходящими аргументами.)

Я пытаюсь повторить эту настройку в DryIoc, но у меня возникают проблемы с передачей аргументов вконструктор форм. DryIoc обрабатывает аргументы Resolve(args) как «замещающие» зависимости, но я хочу строго использовать эти аргументы как «немые» аргументы конструктора. Пример:

public interface IFormFactory
{
    MyForm MyForm(string word, int number);
    MyForm MyForm(string word, int number, bool flag);
}

public class FormFactory : IFormFactory
{
    private readonly IResolver m_resolver;
    private readonly MethodInfo m_method1;
    private readonly MethodInfo m_method2;

    public FormFactory(IContainer container)
    {
        m_resolver = container;

        // I reflect over the methods on IFormFactory
        m_method1 = typeof(IFormFactory).GetMethod(nameof(IFormFactory.MyForm), new Type[] { typeof(string), typeof(int) });
        m_method2 = typeof(IFormFactory).GetMethod(nameof(IFormFactory.MyForm), new Type[] { typeof(string), typeof(int), typeof(bool) });

        // I do this dynamically, by counting parameters
        var constructor1 = m_method1.ReturnType.GetConstructor(new Type[] { typeof(IService), typeof(string), typeof(int) });
        var constructor2 = m_method2.ReturnType.GetConstructor(new Type[] { typeof(IService), typeof(string), typeof(int), typeof(bool) });

        // make a registration for each method that calls the selected constructor
        container.Register(
            m_method1.ReturnType,
            made: Made.Of(FactoryMethod.Of(constructor1)),
            serviceKey: m_method1);
        container.Register(
            m_method2.ReturnType,
            made: Made.Of(FactoryMethod.Of(constructor2)),
            serviceKey: m_method2);
    }

    public MyForm MyForm(string word, int number)
    {
        return (MyForm)m_resolver.Resolve(m_method1.ReturnType, new object[] { word, number }, serviceKey: m_method1);
    }

    public MyForm MyForm(string word, int number, bool flag)
    {
        return (MyForm)m_resolver.Resolve(m_method2.ReturnType, new object[] { word, number, flag }, serviceKey: m_method2);
    }
}

public interface IService { }
public class ServiceImpl : IService { }

public class MyForm
{
    public MyForm(IService service, string word, int number) { Console.WriteLine("constructor1"); }
    public MyForm(IService service, string word, int number, bool flag) { Console.WriteLine("constructor2"); }
}

void Main()
{
    using (var container = new Container())
    {
        container.Register<IService, ServiceImpl>();
        container.Register<IFormFactory, FormFactory>(reuse: Reuse.Singleton);

        var factory = container.Resolve<IFormFactory>();

        // this works because DryIoc uses these parameters as "dependencies" for the constructor
        factory.MyForm("word", 1);

        // this throws
        factory.MyForm(null, 2, true);
    }
}

Передача null любому из аргументов throws, потому что DryIoc хочет предоставить «зависимость».

ContainerException: Unable to resolve String as parameter "word" with passed arguments [null]
  in MyForm {ServiceKey=MyForm MyForm(System.String, Int32, Boolean)} FactoryId=46 with passed arguments [null] IsResolutionCall
  from Container without Scope
Where no service registrations found
  and no dynamic registrations found in 0 of Rules.DynamicServiceProviders
  and nothing found in 0 of Rules.UnknownServiceResolvers

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

    public MyForm MyForm(string word, int number, bool flag)
    {
        return new MyForm(
            m_resolver.Resolve<IService>(),
            word,
            number,
            flag);
    }

Возможно ли создать регистрацию, которая будет передавать args в качестве глупых аргументов, а не обрабатывать их как зависимости

1 Ответ

1 голос
/ 16 октября 2019

Вы можете разрешить как Func внутри ваших методов MyForm:

public MyForm MyForm(string word, int number) { 
    return m_resolver.Resolve<Func<string, int, MyForm>>(serviceKey: m_method1)
    .Invoke(word, number);
}

Более того, вы можете внедрить и сохранить эти Funcs в ctor FormFactory и просто вызывать их из фабричных методов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...