C# 在理解工厂DI上的ninject(或者仅仅是IOC容器)有困难吗?
好吧,最近我一直在读ninject,但我很难理解是什么让它比为什么他们在维基页面上称do为“穷人”DI更好。可悲的是,我浏览了他们在维基上的所有页面,但仍然没有得到它=( 通常,我会将我的服务类包装在一个工厂模式中,这样处理DI:C# 在理解工厂DI上的ninject(或者仅仅是IOC容器)有困难吗?,c#,inversion-of-control,ninject,ioc-container,C#,Inversion Of Control,Ninject,Ioc Container,好吧,最近我一直在读ninject,但我很难理解是什么让它比为什么他们在维基页面上称do为“穷人”DI更好。可悲的是,我浏览了他们在维基上的所有页面,但仍然没有得到它=( 通常,我会将我的服务类包装在一个工厂模式中,这样处理DI: public static class SomeTypeServiceFactory { public static SomeTypeService GetService() { SomeTypeRepository someTypeR
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();
}
}
public类WarriorModule:ninject模块{
公共覆盖无效负载(){
绑定()到();
Bind().ToSelf().InSingletonScope();
}
}
当ninject代码少了1行时,我没有看到它的优点,无论何时添加/删除构造函数或更改接口构造函数的实现,您都必须以与您相同的方式更改模块工厂里的ld没有?所以没有看到这里的优势
然后我想我可以想出一个基于通用惯例的工厂,就像这样:
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;
}
publicstatictserviceclass GetService()
其中TServiceClass:class
{
TServiceClass serviceClass=null;
字符串repositoryName=typeof(TServiceClass).ToString().Replace(“服务”、“存储库”);
Type repositoryType=Type.GetType(repositoryName);
if(repositoryType!=null)
{
对象存储库=Activator.CreateInstance(repositoryType);
serviceClass=(TServiceClass)Activator.CreateInstance(typeof(TServiceClass),新[]{repository});
}
返回服务类;
}
然而,这有两个原因: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>();
}
}
公共静态类EntityServiceFactory
{
公共静态TServiceClass GetService()
其中TServiceClass:class
{
IKernel kernel=新的标准内核();
返回kernel.Get();
}
}
非常棒。由于具体类具有隐式绑定,所以所有内容都会自动处理。要完全分析,您的工厂代码应为:
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>();
}
}
public类WarriorModule:ninject模块{
公共覆盖无效负载(){
绑定()到();
}
}
公共类TestingWarriorModule:NinjectModule{
公共覆盖无效负载(){
绑定()到();
}
}
在这里,您可以声明性地定义依赖项,确保在安装阶段只包含测试代码和生产代码之间的差异
IoC的优点并不是每次接口或构造函数更改时都不必更改模块,而是可以声明依赖项,并且可以为不同的目的插入和播放不同的模块。IoC容器的好处随着项目的大小而增加。对于小项目来说,与像你们工厂这样的“穷人的DI”相比,他们的利益是微乎其微的。想象一下,一个有数千个类的大型项目,许多类中使用了一些服务。在这种情况下,您只需说一次这些服务是如何解决的。在工厂里,你必须为每一个班级做一次又一次 示例:如果您有一个服务
MyService:IMyService
和一个需要IMyService
的类a
,您必须告诉Ninject如何解决这些类型,就像在您的工厂中一样。这方面的好处微乎其微。但是,一旦您的项目增长并添加了一个类B
,它也依赖于IMyService
,您只需告诉Ninject如何解决B
。Ninject已经知道如何获得IMyService
。另一方面,在工厂中,您必须再次定义B如何获得其IMyService
更进一步。在大多数情况下,不应该逐个定义绑定。而是使用基于约定的配置(Ninject.Extension.Conventions
)。使用此功能,您可以将类(服务、存储库、控制器、演示者、视图等)组合在一起,并以相同的方式配置它们。例如,告诉Ninject以服务结尾的所有类都应该是单例,并发布它们的所有接口。这样,您只有一个配置,并且在添加另一个服务时不需要更改
此外,IoC容器不仅仅是工厂。还有很多。例如,生命周期管理、拦截等
kernel.Bind(
x => x.FromThisAssembly()
.SelectAllClasses()
.InNamespace("Services")
.BindToAllInterfaces()
.Configure(b => b.InSingletonScope()));
kernel.Bind(
x => x.FromThisAssembly()
.SelectAllClasses()
.InNamespace("Repositories")
.BindToAllInterfaces());
我明白了,那么ninject真的不会帮我实现工厂的通用实现吗?或者在我没有声明依赖项的情况下自动解决依赖项?我明白了(和其他答案的问题相同),那么ninject真的不会帮助我实现工厂的通用实现吗?或者自动解决依赖关系而无需我声明