Dependency injection 对具有许多依赖项的类使用依赖项注入框架

Dependency injection 对具有许多依赖项的类使用依赖项注入框架,dependency-injection,Dependency Injection,我一直在研究各种.NET依赖注入框架,因为我觉得我正在从事的项目将从中受益匪浅。虽然我认为我已经很好地掌握了这些框架的功能,但我仍然不清楚如何最好地将它们引入到一个大型系统中。大多数演示(可以理解)往往是具有一个或两个依赖项的非常简单的类 我有三个问题 首先,您如何处理那些常见但无趣的依赖关系,例如ILog、IApplicationSettings、IPermissions、IAudit。对于每个类来说,将这些作为构造函数中的参数似乎都有些过分。在需要时,使用DI容器的静态实例来获取它们是否更好

我一直在研究各种.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&lt;T&gt;"/> 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&lt;T&gt;"/> 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();
    }