【问题标题】:Having trouble understanding ninject (or just IOC container in general) over factory DI?难以理解工厂 DI 的 ninject(或一般来说只是 IOC 容器)?
【发布时间】:2012-02-19 18:06:07
【问题描述】:

好的,所以最近我一直在阅读 ninject,但我无法理解是什么使它更好,而不是为什么他们在 wiki 页面上将 do 称为“穷人”的 DI。可悲的是,我浏览了他们在 wiki 上的所有页面,但仍然不明白 =(.

通常我会将我的服务类包装在一个工厂模式中,这样处理 DI:

public static class SomeTypeServiceFactory
{
    public static SomeTypeService GetService()
    {
        SomeTypeRepository someTypeRepository = new SomeTypeRepository();
        return = new SomeTypeService(someTypeRepository);

    }

}

在我看来很像模块:

public class WarriorModule : NinjectModule {
    public override void Load() {
      Bind<IWeapon>().To<Sword>();
      Bind<Samurai>().ToSelf().InSingletonScope();
    }
}

每个类都有它的关联模块,你将它的构造函数绑定到一个具体的实现。虽然 ninject 代码少了 1 行,但我只是没有看到优势,无论何时添加/删除构造函数或更改接口构造函数的实现,都必须像在工厂中那样更改模块不?所以在这里看不到优势。

然后我想我可以像这样想出一个基于通用约定的工厂:

 public static TServiceClass GetService<TServiceClass>()
        where TServiceClass : class
    {
        TServiceClass serviceClass = null;

        string repositoryName = typeof(TServiceClass).ToString().Replace("Service", "Repository");
        Type repositoryType = Type.GetType(repositoryName);

        if (repositoryType != null)
        {
            object repository = Activator.CreateInstance(repositoryType);
            serviceClass =  (TServiceClass)Activator.CreateInstance(typeof (TServiceClass), new[]{repository});
        }

        return serviceClass;
    }

但是,这很糟糕,原因有两个:1) 它严格依赖于命名约定,2) 它假设存储库永远不会有任何构造函数(不是真的),并且服务的唯一构造函数将是它对应的 repo(也不是真的)。有人告诉我“嘿,这是你应该使用 IoC 容器的地方,在这里会很棒!” 于是我的研究开始了……但我只是没有看到它,并且无法理解它...

ninject 是否可以通过某种方式自动解析类的构造函数而无需特定声明,以便在我的通用工厂中使用它会很棒(我也意识到我可以使用反射手动执行此操作,但这是性能损失和 ninject在他们的页面上说他们不使用反射)。

非常感谢您对此问题的启发和/或展示如何在我的通用工厂中使用它!

编辑:回答

所以感谢下面的解释,我能够完全理解 ninject 的厉害之处,我的通用工厂看起来像这样:

public static class EntityServiceFactory
{
    public static TServiceClass GetService<TServiceClass>()
        where TServiceClass : class
    {
        IKernel kernel = new StandardKernel();

        return kernel.Get<TServiceClass>();
    }
}

非常棒。由于具体类具有隐式绑定,因此一切都会自动处理。

【问题讨论】:

  • 通常的破纪录评论:运行到 [.NET 中的依赖注入书籍我的 Mark Seemann](http.commanning.com/seemann)。我无法想象你在拥有这本书的一周内对 DI(模式)和 DI 容器的原因和方式没有信心。

标签: c# inversion-of-control ninject ioc-container


【解决方案1】:

IoC 容器的优势随着项目规模的扩大而增长。对于小型项目,与像您的工厂这样的“穷人的 DI”相比,它们的好处是微乎其微的。想象一个有数千个类的大型项目,并且在许多类中使用了一些服务。在这种情况下,您只需说明这些服务是如何解决的。在工厂里,你必须为每一堂课一遍又一遍地做。

示例:如果您有一个服务MyService : IMyService 和一个需要IMyService 的类A,您必须告诉Ninject 它应该如何解决这些类型,就像在您的工厂中一样。这里的好处是微乎其微的。但是一旦你的项目增长并且你添加了一个类B,它也依赖于IMyService,你只需要告诉Ninject如何解决B。 Ninject 已经知道如何获取IMyService。另一方面,在工厂中,您必须再次定义 B 如何获取其IMyService

更进一步。在大多数情况下,您不应该一一定义绑定。而是使用基于约定的配置 (Ninject.Extension.Conventions)。有了这个,您可以将类组合在一起(服务、存储库、控制器、演示者、视图......)并以相同的方式配置它们。例如。告诉 Ninject 所有以 Service 结尾的类都应该是单例并发布它们的所有接口。这样一来,您只有一个配置,并且在添加其他服务时无需进行任何更改。

此外,IoC 容器不仅仅是工厂。还有更多。例如。生命周期管理,拦截,....

kernel.Bind(
    x => x.FromThisAssembly()
          .SelectAllClasses()
          .InNamespace("Services")
          .BindToAllInterfaces()
          .Configure(b => b.InSingletonScope()));
kernel.Bind(
    x => x.FromThisAssembly()
          .SelectAllClasses()
          .InNamespace("Repositories")
          .BindToAllInterfaces());

【讨论】:

  • 我明白了(与其他答案相同的问题),所以真的 ninject 不会帮助我实现工厂的通用实现吗?或者以某种方式自动解决依赖关系而无需我声明它们?只要使用它来为每个服务类都没有模块/工厂。另外,如果我的 repos 和服务有不同数量的注入,那么不能真正使用约定,那可以吗?
  • 您似乎误解了 ninject 的工作原理。它查看构造函数,然后尝试从配置中获取正确的依赖项。因此,可以使用相同的约定轻松配置具有不同数量依赖项的类。
  • 嗯我认为我现在明白了。因此它不能帮助我的通用工厂,对吗?我仍然必须在某些时候以声明方式配置注入,这意味着我不能在这里真正做一个泛型。您是否有使用具有可变数量依赖项的约定的示例?很抱歉在这里太厚了=(。我很难将网站上的内容推断到我自己的场景中。
  • 再一次。依赖的数量无关紧要! Ninject 需要配置!它还应该从哪里知道哪些类是单例以及哪些瞬态?不幸的是,它还不能读懂你的想法。但是在不知道您的应用程序/问题的情况下几乎不可能帮助您。配置可以像告诉遵循某些约定的类是服务一样简单,因此单例和其他一切都是瞬态的。配置应用程序的两行代码。
  • 好吧,那么答案是 Ninject 无法为您建造这样的工厂。 Ninject 本身就是通用工厂。它完全符合您的描述。
【解决方案2】:

完全类似,您的工厂代码应为:

public static class SomeTypeServiceFactory
{
    public static ISomeTypeService GetService()
    {
        SomeTypeRepository someTypeRepository = new SomeTypeRepository();

        // Somewhere in here I need to figure out if i'm in testing mode 
        // and i have to do this in a scope which is not in the setup of my
            // unit tests

        return new SomeTypeService(someTypeRepository);
    }

    private static ISomeTypeService GetServiceForTesting()
    {
        SomeTypeRepository someTypeRepository = new SomeTypeRepository();
        return new SomeTestingTypeService(someTypeRepository);
    }
}

Ninject 中的等价物是:

public class WarriorModule : NinjectModule {
    public override void Load() {
      Bind<ISomeTypeService>().To<SomeTypeService>();
    }
}

public class TestingWarriorModule : NinjectModule {
    public override void Load() {
      Bind<ISomeTypeService>().To<SomeTestingTypeService>();
    }
}

在这里,您可以以声明方式定义依赖关系,确保测试和生产代码之间的唯一差异包含在设置阶段。

IoC 的优点不是每次接口或构造函数更改时您都不必更改模块,而是您可以声明式地声明依赖关系并且可以为不同目的插入和播放不同的模块.

【讨论】:

  • 我明白了,那么真的 ninject 不会帮助我实现工厂的通用实现吗?或者以某种方式自动解决依赖关系而无需我声明它们?只要使用它来为每个服务类都没有一个模块/工厂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多