Keyed delegate factories with runtime constructor parameters?

前端 未结 2 1836
别那么骄傲
别那么骄傲 2021-02-05 10:42

Lets say I have the following service and components:

public interface IService
{
    void DoWork();
}

public class ServiceA : IService
{
    private readonly s         


        
相关标签:
2条回答
  • 2021-02-05 11:22

    As you say, your two individual requirements are natively supported by AutoFac.

    • Runtime resolution of constructor parameters can be implemented using Parameterized Instantiation
    • Resolution of a particular service implementation can be implemented using Keyed Service Lookup

    However, there doesn't appear to be direct support for using these two constructs together. I.e. the following does not work:

    public enum ServiceType
    {
        ServiceA,
        ServiceB
    }
    
    public class MyComponent
    {
        public MyComponent(Func<string, IIndex<ServiceType, IService> factory)
        {
            var service = factory("some_string")[ServiceType.ServiceA];
        }
    }
    

    My work around for this has always been to move the resolution of the service to a factory per service implementation. This then works as follows:

    1. Components with a dependency on a specific service implementation take in an AutoFac factory delegate that resolves to the factory specific to the required service implementation
    2. In turn, the service factories have a dependency on an AutoFac factory delegate that knows how to create the specific service implementation from the (runtime) constructor parameters of the service
    3. This approach uses the native AutoFac constructs and doesn't have any dependencies on AutoFac outside of the container wiring.

    Heres a rough-and-ready example. Note the multiple factories could be reduced to a single generic factory - but I've left it as-is for clarity:

    Service implementations

    public enum ServiceType
    {
        NotSet,
        ServiceA,
        ServiceB
    }
    
    public interface IService
    {
        string DoWork();
    }
    
    public class ServiceA : IService
    {
        private readonly string _name;
    
        public ServiceA(string name)
        {          
            _name = name;
        }
    
        public string DoWork()
        {
            throw new NotImplementedException();
        }
    }
    
    public class ServiceB : IService
    {
        private readonly string _name;
    
        public ServiceB(string name)
        {           
            _name = name;
        }
    
        public string DoWork()
        {
            throw new NotImplementedException();
        }
    }
    

    Service factories

    public interface IServiceFactory
    {
        IService Create(string name);
    }
    
    public class ServiceAFactory : IServiceFactory
    {
        private readonly Func<string, ServiceA> _factory;
    
        public ServiceAFactory(Func<string, ServiceA> factory)
        {            
            _factory = factory;
        }
    
        public IService Create(string name)
        {
            return _factory(name);
        }
    }
    
    public class ServiceBFactory : IServiceFactory
    {
        private readonly Func<string, ServiceB> _factory;
    
        public ServiceBFactory(Func<string, ServiceB> factory)
        {            
            _factory = factory;
        }
    
        public IService Create(string name)
        {
            return _factory(name);
        }
    }
    

    Service registrations

    builder.RegisterType<ServiceA>().As<ServiceA>();
    builder.RegisterType<ServiceB>().As<ServiceB>();
    builder.RegisterType<ServiceAFactory>().Keyed<IServiceFactory>(ServiceType.ServiceA);
    builder.RegisterType<ServiceBFactory>().Keyed<IServiceFactory>(ServiceType.ServiceB);
    builder.RegisterType<ComponentWithServiceDependency>().As<ComponentWithServiceDependency>(); 
    

    Example usage

    public class ComponentWithServiceDependency
    {
        private readonly IService _service;
    
        public ComponentWithServiceDependency(IIndex<ServiceType, IServiceFactory> serviceFactories)
        {            
            // Resolve the ServiceB service implementation,
            // using the string "test" as its constructor dependency
            _service = serviceFactories[ServiceType.ServiceB].Create("test");
        }
    
        public string Test()
        {
            return _service.DoWork();
        }
    }
    
    0 讨论(0)
  • 2021-02-05 11:38

    You can use Interface Segregation.

    public interface IService
    {
        void DoWork();
    }
    
    public interface IServiceA : IService
    {
    }
    
    public interface IServiceB : IService
    {
    }
    
    public class ServiceA : IServiceA
    {
        private readonly string _name;
    
        public ServiceA(string name)
        {
            _name = name;
        }
    
        public void DoWork()
        {
            //ServiceA DoWork implementation
        }
    }
    
    public class ServiceB : IServiceB
    {
        private readonly string _name;
    
        public ServiceB(string name)
        {
            _name = name;
        }
    
        public void DoWork()
        {
            //ServiceB DoWork implementation
        }
    }
    

    Then, you can inject delegate factories like so:

    public class ClientA
    {
        public ClientA(Func<string, IServiceA> serviceAFactory, Func<string, IServiceB> serviceBFactory)
        {
            this.serviceAFactory = serviceAFactory;
            this.serviceBFactory = serviceBFactory;
        }
    
        public CreateServices()
        {
            var runTimeName = "runTimeName";
            var serviceA = this.serviceAFactory(runTimeName);
            var serviceB = this.ServiceBFactory(runTimeName);
        }
    }
    

    Autofac will generate a delegate factory for each interface you register:

    public class MyModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<ServiceA>()
                .As<IService>()
                .As<IServiceA>();
    
            builder.RegisterType<ServiceB>()
                .As<IService>()
                .As<IServiceB>();
        }
    }
    
    0 讨论(0)
提交回复
热议问题