Dependency injection 对具有许多依赖项的类使用依赖项注入框架
我一直在研究各种.NET依赖注入框架,因为我觉得我正在从事的项目将从中受益匪浅。虽然我认为我已经很好地掌握了这些框架的功能,但我仍然不清楚如何最好地将它们引入到一个大型系统中。大多数演示(可以理解)往往是具有一个或两个依赖项的非常简单的类 我有三个问题 首先,您如何处理那些常见但无趣的依赖关系,例如ILog、IApplicationSettings、IPermissions、IAudit。对于每个类来说,将这些作为构造函数中的参数似乎都有些过分。在需要时,使用DI容器的静态实例来获取它们是否更好Dependency injection 对具有许多依赖项的类使用依赖项注入框架,dependency-injection,Dependency Injection,我一直在研究各种.NET依赖注入框架,因为我觉得我正在从事的项目将从中受益匪浅。虽然我认为我已经很好地掌握了这些框架的功能,但我仍然不清楚如何最好地将它们引入到一个大型系统中。大多数演示(可以理解)往往是具有一个或两个依赖项的非常简单的类 我有三个问题 首先,您如何处理那些常见但无趣的依赖关系,例如ILog、IApplicationSettings、IPermissions、IAudit。对于每个类来说,将这些作为构造函数中的参数似乎都有些过分。在需要时,使用DI容器的静态实例来获取它们是否更好
MyClass(ILog log, IAudit audit, IPermissions permissions, IApplicationSettings settings)
// ... versus ...
ILog log = DIContainer.Get<ILog>();
MyClass(ILog日志、IAudit审核、IPermissions权限、IAApplicationSettings设置)
// ... 对
ILog log=DIContainer.Get();
第二,如何处理可能使用但创建成本可能较高的依赖项。示例-类可能依赖于ICDBurner接口,但不希望创建具体的实现,除非实际使用了CD刻录功能。您是将接口传递给构造器中的工厂(例如ICDBurnerFactory),还是再次采用某种静态方式直接访问DI容器,并在需要时请求它
第三个,假设您有一个大型Windows窗体应用程序,其中顶级GUI组件(例如MainForm)是数百个子面板或模式窗体的父级,每个子面板或模式窗体可能具有多个依赖项。这是否意味着MainForm应该设置为将其子级的所有依赖项的超集作为依赖项?如果你这样做了,这会不会导致一个巨大的自我膨胀的怪物,在你创建MainForm的那一刻构造它可能需要的每一个类,在这个过程中浪费时间和内存?首先: 您可以通过创建一个容器来保存您的“无趣”依赖项(ILog、ICache、IAApplicationSettings等),并使用构造函数注入来注入该依赖项,然后在构造函数内部,从container.Resolve()中生成服务字段?我不确定我是否愿意,但是,嗯,这是可能的 或者,您可能希望使用新的IServiceLocator公共接口()而不是注入依赖项 秒:
您可以对可选/按需依赖项使用setter注入吗?我想我会选择注射工厂,然后按需新建。首先: 您可以在需要时将这些对象作为成员而不是构造函数中的成员注入。这样,您就不必随着使用情况的变化而对构造函数进行更改,也不需要使用静态构造函数 秒: 通过某种建筑商或工厂 第三名:
任何类都应该只具有其自身所需的依赖项。子类应该注入它们自己特定的依赖项。首先:根据需要将简单的依赖项添加到构造函数中。没有必要将每个类型添加到每个构造函数中,只需添加所需的类型即可。需要另一个,只需展开构造函数。性能不应该是一件大事,因为这些类型中的大多数可能是单例的,所以在第一次调用之后就已经创建了。不要使用静态DI容器来创建其他对象。而是将DI容器添加到自身中,以便它可以将自身解析为依赖项。类似这样的事情(假设目前是统一的)
IUnityContainer container=newunitycontainer();
容器。注册表状态(容器);
通过这种方式,您只需在IUnityContainer上添加一个依赖项,并使用它来创建昂贵或很少需要的对象。主要的优点是,当单元测试时,由于没有静态依赖关系,所以更容易进行
第二:无需通过工厂课程。使用上述技术,您可以在需要时使用DI容器本身来创建昂贵的对象
Three:将DI容器和轻单例依赖项添加到主窗体,并根据需要通过DI容器创建其余依赖项。需要更多的代码,但正如您所说,如果您在启动时创建所有内容,则主窗体的启动成本和内存消耗将大大增加。首先要部分回答我的问题,我刚刚找到Jeremy Miller的,显示如何使用结构映射和setter注入自动填充对象的公共属性。他以伊洛格为例:
var container = new Container(r =>
{
r.FillAllPropertiesOfType<ILogger>().TheDefault.Is
.ConstructedBy(context => new Logger(context.ParentType));
});
将在构造时自动设置其记录器属性:
container.GetInstance<ClassWithLogger>();
container.GetInstance();
好吧,虽然您可以像其他答案中描述的那样做,但我相信关于您的示例还有更重要的问题需要回答,那就是您可能违反了SRP原则,因为类具有许多依赖项
<> P>我在你的例子中考虑的是,在两个更连贯的课堂中用关注的焦点打破课堂,这样他们的依赖性的数量就会下降。
尼古拉SRP和DI定律
“任何超过3节课的班级
依赖性应该被质疑
SRP违规”
(为了避免冗长的回答,我在博客上发布了详细的回答)我有一个类似的案例,与“创建和使用成本高昂”有关,在我自己的IoC实施中,我为工厂服务添加了automagic支持 基本上,不是这样:
public SomeService(ICDBurner burner)
{
}
您可以这样做:
public SomeService(IServiceFactory<ICDBurner> burnerFactory)
{
}
ICDBurner burner = burnerFactory.Create();
公共服务(IServiceFactory burnerFactory)
{
}
ICDBurner burner=burnerFactory.Create();
这有两个好处:
- 贝希
public SomeService(ICDBurner burner) { }
public SomeService(IServiceFactory<ICDBurner> burnerFactory) { } ICDBurner burner = burnerFactory.Create();
using System; using System.Collections.Generic; using System.Linq; using System.Text; using LVK.IoC.Interfaces; using System.Diagnostics; namespace LVK.IoC { /// <summary> /// This class is used to implement <see cref="IServiceFactory{T}"/> for all /// services automatically. /// </summary> [DebuggerDisplay("AutoServiceFactory (Type={typeof(T)}, Policy={Policy})")] internal class AutoServiceFactory<T> : ServiceBase, IServiceFactory<T> { #region Private Fields [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly String _Policy; #endregion #region Construction & Destruction /// <summary> /// Initializes a new instance of the <see cref="AutoServiceFactory<T>"/> class. /// </summary> /// <param name="serviceContainer">The service container involved.</param> /// <param name="policy">The policy to use when resolving the service.</param> /// <exception cref="ArgumentNullException"><paramref name="serviceContainer"/> is <c>null</c>.</exception> public AutoServiceFactory(IServiceContainer serviceContainer, String policy) : base(serviceContainer) { _Policy = policy; } /// <summary> /// Initializes a new instance of the <see cref="AutoServiceFactory<T>"/> class. /// </summary> /// <param name="serviceContainer">The service container involved.</param> /// <exception cref="ArgumentNullException"><paramref name="serviceContainer"/> is <c>null</c>.</exception> public AutoServiceFactory(IServiceContainer serviceContainer) : this(serviceContainer, null) { // Do nothing here } #endregion #region Public Properties /// <summary> /// Gets the policy that will be used when the service is resolved. /// </summary> public String Policy { get { return _Policy; } } #endregion #region IServiceFactory<T> Members /// <summary> /// Constructs a new service of the correct type and returns it. /// </summary> /// <returns>The created service.</returns> public IService<T> Create() { return MyServiceContainer.Resolve<T>(_Policy); } #endregion } }
var builder = new ServiceContainerBuilder(); builder.Register<ISomeService>() .From.ConcreteType<SomeService>(); using (var container = builder.Build()) { using (var factory = container.Resolve<IServiceFactory<ISomeService>>()) { using (var service = factory.Instance.Create()) { service.Instance.DoSomethingAwesomeHere(); } } }
var builder = new ServiceContainerBuilder(); builder.Register<ICDBurner>() .From.ConcreteType<CDBurner>(); builder.Register<ISomeService>() .From.ConcreteType<SomeService>(); // constructor used in the top of answer using (var container = builder.Build()) { using (var service = container.Resolve<ISomeService>()) { service.Instance.DoSomethingHere(); } }
using (var service1 = container.Resolve<ISomeService>()) using (var service2 = container.Resolve<ISomeService>()) { service1.Instance.DoSomethingHere(); service2.Instance.DoSomethingHere(); }
using (var service = container.Resolve<ISomeService>()) { service.Instance.DoSomethingHere(); } using (var service = container.Resolve<ISomeService>()) { service.Instance.DoSomethingElseHere(); }