Расширение фабрики Ninject

У меня небольшие проблемы с пониманием расширения Ninject Factory.

У меня есть следующая структура класса

 public class Resource
 {
      public IResourceLoader ResourceLoader {get;set;}

      public Resource(IResourceLoader ResourceLoader)
      { 
            this.ResourceLoader  = ResourceLoader ; 
      }
 }


 public class Banner : Resource
 {
       public Banner([Named("pngLoader")] IResourceLoader ResourceLoader)
             :base(ResourceLoader)
       { }
 }

 public class MetaData : Resource
 {
       public MetaData ([Named("xmlLoader") IResourceLoader ResourceLoader)
             :base(ResourceLoader)
       { }
 }

 interface IResourceLoader
 {
       object resource {get;}
 }

 public class XMLLoader : IResourceLoader
 {
       public resource { return "this was sent by xml loader"; }
 }

 public class PNGLoader : IResourceLoader
 {
       public resource { return "this was sent by png loader"; }
 }

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

 interface IResourceLoaderFactory
 {
       IResourceLoader GetxmlLoader();
       IResourceLoader GetpngLoader()
 } 

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

kernel.Bind<IResourceLoader>().To<XMLLoader>().NamedLikeFactoryMethod((IResourceLoaderFactory f) => f.GetxmlLoader());
kernel.Bind<IResourceLoader>().To<PNGLoader>().NamedLikeFactoryMethod((IResourceLoaderFactory f) => f.GetpngLoader());

Предполагая, что вышеизложенное верно, я не знаю, как это сделать, чтобы Ninject предоставил Banner или MetaData правильный IResourceLoader на основе [Named] в конструкторе, который он передает в базовый конструктор.

Я использую все это в приложении mvc 5, например

public class HomeController
{
    public ActionResult Index(/* do banners and meta need to be asked for here? */)
    {
        /* or do I need to instantiate them here/? */

        Banner banner = new Banner(/* what to put here? */);
        Meta meta = new Meta(/* what to put here? */);

        ...
    }
}

Спасибо


person Raza Jamil    schedule 04.02.2016    source источник


Ответы (1)


Позвольте мне попробовать ответить на ваш вопрос, я не уверен на 100%, что правильно понял ваш вопрос, поэтому, пожалуйста, дайте мне отзыв, если я не понял.

Теперь ваша основная проблема заключается в том, что вы хотите ввести IResourceLoader, но в зависимости от того, во что вы его вводите, это должен быть либо XMLLoader, либо PNGLoader.

Вы правильно определили именованные привязки как одно из возможных решений для выбора подходящего IResourceLoader.

Однако вам не нужно комбинировать шаблоны NamedLikeFactory и [Named]-Attribute для достижения желаемого, достаточно одного из них, и здесь [Named]-Attribute, вероятно, является лучшей альтернативой из двух (есть третий, который я буду использовать). получить позже).

Итак, вот что вы делаете:

public const string XmlLoaderName = "XmlLoader";
public const string PngLoaderName = "PngLoader";

Bind<IResourceLoader>().To<XMLLoader>()
    .Named(XmlLoaderName);
Bind<IResourceLoader>().To<PNGLoader>()
    .Named(PngLoaderName);

И затем вы указываете соответствующий тип в ctor (как вы это сделали):

public class Banner : Resource
{
    public Banner([Named(pngLoaderName)] IResourceLoader ResourceLoader)
            :base(ResourceLoader)
    { }
}

public class MetaData : Resource
{
    public MetaData ([Named(xmlLoaderName) IResourceLoader ResourceLoader)
            :base(ResourceLoader)
    { }
}

и это в принципе!

теперь, чтобы использовать его в своем контроллере, все, что вам нужно сделать, это:

public class HomeController
{
    public HomeController(Banner baner, MetaData metaData)
    {
    ...
    }
}

нет необходимости использовать фабрику. За исключением случаев, когда вам нужно создать экземпляр Banner или MetaData для каждого запроса, и в этом случае вы должны создать фабричный интерфейс:

public interface IResourceFactory
{
    Banner CreateBanner();

    MetaData CreateMetaData();
}

который связан как:

Bind<IResourceFactory>().ToFactory(); 
// ToFactory is an extension method from Ninject.Extensions.Factory

который будет использоваться как:

public class HomeController
{
    private readonly IResourceFactory resourceFactory;

    public HomeController(IResourceFactory resourceFactory)
    {
        this.resourceFactory = resourceFactory;
    }

    public ActionResult Index()
    {
        var banner = this.resourceFactory.CreateBanner();
        ....
    }
}

Альтернатива привязке Named: вы также можете использовать условную привязку, например:

Bind<IResourceLoader>().To<XMLLoader>()
   .WhenInjectedInto<Banner>();

и соответствующий Banner ctor:

public Banner(IResourceLoader resourceLoader)
{
...
}

Эта альтернатива может иметь смысл, если существует очень ограниченный набор классов, в которые внедряется ResourceLoader. Или если вы не можете добавить [Named]-атрибут к ctor (например, потому что это сторонняя библиотека...).

Еще одна альтернатива — предоставить Ninject больше информации о том, как построить тип. Например:

Bind<Banner>().ToSelf()
    .WithConstructorArgument(
        typeof(IResourceLoader),
        ctx => ctx.Kernel.Get<XmlLoader>());
person BatteryBackupUnit    schedule 09.02.2016
comment
Это сработало как шарм! Раньше я пробовал первое решение, но оно почему-то не сработало. Мне больше нравится ваше последнее решение самопривязки и внедрения в аргументы конструктора, оно кажется более чистым и лаконичным. Спасибо, сэр! - person Raza Jamil; 09.02.2016