Ninject Factory Extension

时间:2016-02-04 12:38:31

标签: c# dependency-injection ninject ninject.web.mvc ninject-extensions

我在Ninject Factory Extension周围缠头时遇到了一些麻烦。

我有以下类结构

 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属性实现基于约定的过滤,如show here。所以我实现了以下界面。

 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根据它将它传递给基础构造函数的构造函数中的[Named]为Banner或MetaData提供正确的IResourceLoader。

我在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? */);

        ...
    }
}

由于

1 个答案:

答案 0 :(得分:2)

让我尝试回答你的问题,我不是100%确定我已正确理解你的问题,所以如果我没有,请给我反馈。

现在,您的基本问题是您要注入IResourceLoader - 但根据您注入的内容,它应该是XMLLoaderPNGLoader

您已正确识别命名绑定作为选择适当IResourceLoader的一种可能解决方案。

但是,您不需要组合NamedLikeFactory[Named] - 属性模式来实现您想要的,其中一个就足够了,这里[Named] - 属性可能是两者中更好的选择(我将在后面讨论第三种)。

所以这就是你所做的:

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)
    {
    ...
    }
}

无需使用工厂。除非您需要为每个请求实例化BannerMetaData实例,否则您将创建工厂接口:

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,那么这个替代方案就有意义了。或者,如果您无法向ctor添加[Named] - 属性(例如,因为它是第三方库...)。

另一个替代方案是给Ninject提供有关如何构造类型的更多信息。例如:

Bind<Banner>().ToSelf()
    .WithConstructorArgument(
        typeof(IResourceLoader),
        ctx => ctx.Kernel.Get<XmlLoader>());