Использование Ninject для привязки интерфейса к нескольким реализациям, неизвестным во время компиляции - PullRequest
2 голосов
/ 26 ноября 2011

Я только недавно начал использовать Ninject (v2.2.0.0) в своем приложении ASP.NET MVC 3.Пока что я в восторге от этого, но столкнулся с ситуацией, которую, похоже, не могу понять.

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

Вот пример:

public class SomeInterfaceFactory<T> where T: ISomeInterface, new()
{
    public T CreateInstance()
    {
        // Activation and initialization logic here
    }
}

public interface ISomeInterface 
{
}

public class SomeImplementationA : ISomeInterface
{
    public string PropertyA { get; set; }
}

public class SomeImplementationB : ISomeInterface
{
    public string PropertyB { get; set; }
}


public class Foo 
{
    public Foo(SomeImplementationA implA) 
    {
        Console.WriteLine(implA.PropertyA);
    }
}

public class Bar
{
    public Bar(SomeImplementationB implB)
    {
        Console.WriteLine(implB.PropertyB);
    }
}

В другом месте я бы хотел связать, используя толькоинтерфейс:

kernel.Bind<Foo>().ToSelf();
kernel.Bind<Bar>().ToSelf();
kernel.Bind(typeof(SomeInterfaceFactory<>)).ToSelf();
kernel.Bind<ISomeInterface>().To ...something that will create and use the factory

Затем при запросе экземпляра Foo из Ninject он увидит, что один из параметров конструкторов реализует связанный интерфейс, извлекает фабрику и создает экземпляр правильного конкретного типа (SomeImplementationA)и передать его конструктору Foo.

Причина этого в том, что у меня будет много реализаций ISomeInterface, и я бы предпочел избегать привязки каждой из них по отдельности.Некоторые из этих реализаций могут быть неизвестны во время компиляции.

Я пытался использовать:

kernel.Bind<ISomeInterface>().ToProvider<SomeProvider>();

Поставщик извлекает фабрику на основе запрошенного типа сервиса, затем вызывает его метод CreateInstance, возвращаяКонкретный тип:

public class SomeProvider : Provider<ISomeInterface>
{
    protected override ISomeInterface CreateInstance(IContext context)
    {
        var factory = context.Kernel.Get(typeof(SomeInterfaceFactory<>)
            .MakeGenericType(context.Request.Service));
        var method = factory.GetType().GetMethod("CreateInstance");
        return (ISomeInterface)method.Invoke();
    }
}

Однако мой провайдер никогда не вызывался.

Мне интересно, может ли Ninject поддержать эту ситуацию и, если да, то как мне решить эту проблему.

Надеюсь, этого достаточно, чтобы объяснить мою ситуацию.Пожалуйста, дайте мне знать, если мне нужно уточнить.

Спасибо!

1 Ответ

1 голос
/ 26 ноября 2011

Кажется, вы неправильно поняли, как работает ninject. В случае, если вы создаете Foo, он видит, что ему требуется SomeImplementationA, и попытается создать для него экземпляр. Поэтому вам нужно определить привязку для SomeImplementationA, а не для ISomeInterface.

Также, скорее всего, ваша реализация нарушает принцип инверсии зависимости, потому что вы полагаетесь на конкретные экземпляры, а не на абстракции.

Решение для одновременной регистрации всех похожих типов (и предпочтительный способ настройки контейнеров IoC) заключается в использовании конфигурации в соответствии с соглашениями. См. Расширение Ninject.Extensions.Conventions.

...