Using Dependency Injection frameworks for classes with many dependencies

后端 未结 6 411
无人及你
无人及你 2021-01-30 07:21

I have been looking at various dependency injection frameworks for .NET as I feel the project I am working on would greatly benefit from it. While I think I have a good grasp of

6条回答
  •  北恋
    北恋 (楼主)
    2021-01-30 07:33

    I have a similar case related to the "expensive to create and might be used", where in my own IoC implementation, I'm adding automagic support for factory services.

    Basically, instead of this:

    public SomeService(ICDBurner burner)
    {
    }
    

    you would do this:

    public SomeService(IServiceFactory burnerFactory)
    {
    }
    
    ICDBurner burner = burnerFactory.Create();
    

    This has two advantages:

    • Behind the scenes, the service container that resolved your service is also used to resolve the burner, if and when it is requested
    • This alleviates the concerns I've seen before in this kind of case where the typical way would be to inject the service container itself as a parameter to your service, basically saying "This service requires other services, but I'm not going to easily tell you which ones"

    The factory object is rather easy to make, and solves a lot of problems.

    Here's my factory class:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using LVK.IoC.Interfaces;
    using System.Diagnostics;
    
    namespace LVK.IoC
    {
        /// 
        /// This class is used to implement  for all
        /// services automatically.
        /// 
        [DebuggerDisplay("AutoServiceFactory (Type={typeof(T)}, Policy={Policy})")]
        internal class AutoServiceFactory : ServiceBase, IServiceFactory
        {
            #region Private Fields
    
            [DebuggerBrowsable(DebuggerBrowsableState.Never)]
            private readonly String _Policy;
    
            #endregion
    
            #region Construction & Destruction
    
            /// 
            /// Initializes a new instance of the  class.
            /// 
            /// The service container involved.
            /// The policy to use when resolving the service.
            ///  is null.
            public AutoServiceFactory(IServiceContainer serviceContainer, String policy)
                : base(serviceContainer)
            {
                _Policy = policy;
            }
    
            /// 
            /// Initializes a new instance of the  class.
            /// 
            /// The service container involved.
            ///  is null.
            public AutoServiceFactory(IServiceContainer serviceContainer)
                : this(serviceContainer, null)
            {
                // Do nothing here
            }
    
            #endregion
    
            #region Public Properties
    
            /// 
            /// Gets the policy that will be used when the service is resolved.
            /// 
            public String Policy
            {
                get
                {
                    return _Policy;
                }
            }
    
            #endregion
    
            #region IServiceFactory Members
    
            /// 
            /// Constructs a new service of the correct type and returns it.
            /// 
            /// The created service.
            public IService Create()
            {
                return MyServiceContainer.Resolve(_Policy);
            }
    
            #endregion
        }
    }
    

    Basically, when I build the service container from my service container builder class, all service registrations are automatically given another co-service, implementing IServiceFactory for that service, unless the programmer has explicitly registered on him/her-self for that service. The above service is then used, with one parameter specifying the policy (which can be null if policies aren't used).

    This allows me to do this:

    var builder = new ServiceContainerBuilder();
    builder.Register()
        .From.ConcreteType();
    
    using (var container = builder.Build())
    {
        using (var factory = container.Resolve>())
        {
            using (var service = factory.Instance.Create())
            {
                service.Instance.DoSomethingAwesomeHere();
            }
        }
    }
    

    Of course, a more typical use would be with your CD Burner object. In the above code I would resolve the service instead of course, but it's an illustration of what happens.

    So with your cd burner service instead:

    var builder = new ServiceContainerBuilder();
    builder.Register()
        .From.ConcreteType();
    builder.Register()
        .From.ConcreteType(); // constructor used in the top of answer
    
    using (var container = builder.Build())
    {
        using (var service = container.Resolve())
        {
            service.Instance.DoSomethingHere();
        }
    }
    

    inside the service, you could now have a service, a factory service, which knows how to resolve your cd burner service upon request. This is useful for the following reasons:

    • You might want to resolve more than one service at the same time (burn two discs simultaneously?)
    • You might not need it, and it could be costly to create, so you only resolve it if needed
    • You might need to resolve, dispose, resolve, dispose, multiple times, instead of hoping/trying to clean up an existing service instance
    • You're also flagging in your constructor which services you need and which ones you might need

    Here's two at the same time:

    using (var service1 = container.Resolve())
    using (var service2 = container.Resolve())
    {
        service1.Instance.DoSomethingHere();
        service2.Instance.DoSomethingHere();
    }
    

    Here's two after each other, not reusing the same service:

    using (var service = container.Resolve())
    {
        service.Instance.DoSomethingHere();
    }
    using (var service = container.Resolve())
    {
        service.Instance.DoSomethingElseHere();
    }
    

提交回复
热议问题