Ninject - вопрос об обязательной родовой фабрике - PullRequest
0 голосов
/ 03 июня 2019

У меня есть интерфейс "IItemsChanger", который реализует разные классы.

public interface IItemsChanger
{
    void Change(int value)
}

Одна из возможных реализаций - это простой класс, который получает объект item, который может быть разным:

public class MyItemChanger : IItemChanger
{
    private readonly IEnumerable<MyItem> _items;

    public ArticoliStateChanger(IEnumerable<MyItem> items)
    {
        _items = items;
    }

    public void Change(int value)
    {
        ...
    }
}

Как вы можете видеть, конструктор имеет IEnumerable из MyItem;в другой реализации MyItem может быть заменен другим IEnumerable.

Я хочу, чтобы список MyItem внедрялся через конструктор в мою реализацию, поэтому мне нужно построить фабрику.Поскольку элементы могут иметь различный тип, я также должен сохранять фабрику как общую, например:

public interface IItemsChangerFactory<T>
{
    IItemsChanger Create(IEnumerable<T> items);
}

Если это имеет смысл, я настроил Ninject следующим образом:

Bind(typeof(IItemsChangerFactory<>)).ToFactory();
Bind<IItemsChanger>().To<MyItemChanger>();

Когда япопытайтесь получить реализацию, подобную этой:

var kernel = new StandardKernel(new MyModule());
var k = kernel.Get<IItemsChangerFactory<MyItem>>();

Я получаю ошибку:

"Невозможно привести объект типа 'Castle.Proxies.ObjectProxy' к типу 'MyNamespace.IItemsChangerFactory`1 [MyItem] '

Как это можно исправить?

1 Ответ

0 голосов
/ 04 июня 2019

Ни Ninject, ни Ninject.Extensions.Factory не поддерживают эту проблему "из коробки". Проблема заключается в том, что ни Ninject, ни фабрика не знают, как сопоставить параметр T Type IItemsChangerFactory<T> с конкретной реализацией IItemsChanger. Эта логика должна быть написана вами.

Теперь приходит следующая проблема. У вас не может быть конструкторов типа интерфейса, т.е. Вы не можете объявить конструктор MyItemChanger для реализации IItemsChangerFactory<MyItem>. Вы можете написать его как безопасный во время компиляции, но требующий некоторого кода котельной пластины, т. Е .:

class MyItemChangerFactory : IItemsChangerFactory<MyItem>
{
    public IItemsChanger Create(IEnumerable<T> items)
    {
        return new MyItemChanger(items);
    }
}

или вы можете использовать отражение - но потеряете проверки времени компиляции:

class ItemChangerFactory<TItem, TChanger> : IItemsChangerFactory<TItem>
{
    public IItemsChanger Create(IEnumerable<T> items)
    {
        return Activator.CreateInstance(typeof(TChanger), new object[] { items });
    }
}

Конечно, Activator можно заменить, делегировав это в ninject, что-то вроде: IResolutionRoot.Get(typeof(TChanger), new TypedConstructorArgument(typeof(IEnumerable<T>), items));

...