user5326354
user5326354

Reputation:

Autofac resolve in deep layer

I'm having hard time integrating autofac in my applcation..

My application integrates with other applications, when a connection to another application is made its being maintained by a fitting "Protocol" object

// This class is inherited by a few other classes
public abstract class Protocol

I have a layer that is run by it's own thread and handles connection requests. For each kind of request a different kind of protocol is created (a different protocol object)

// For example lets say every protocol (Inhertiance of Protocol abstract object) 
// takes in ctor these 3 services + runtime configuration object:
public Protocol1(IFramingAgent, IFramingAlgorithm, IFramingParser, configObject configuration)

My protocol objects are keyed registered as Protocol. Also, Each service is registered with key because each protocol uses a different kind that inherits from the same interface. Of course, all of these are registered as PerDependency (although I don't really understand the difference between this lifeCycle against PerScope, would really appreciate an explanation)

And here is my terrible class:

public class ProtocolsLayer : Layer
{
    private IFrameworkDependencyResolver _resolver;
    private IConfigurationService _configService;

    public ProtocolsLayer(IFrameworkDependencyResolver resolver, IConfigurationService configurationService)
    {
        _resolver = resolver;
        _configService = configurationService;
    }

    void HandleConnection1()
    {
        // What I have at the moment (terrible):

        // Resolve the fitting services (All keyed - key is received by the type, Resolve and ResolveWithParameters used here are my wrappers)
        var agent = _resolver.Resolve<IFramingAgent>(typeof(Protocol1FramingAgent));

        var algo = _resolver.Resolve<IFramingAlgorithm>(typeof(Protocol1FramingAlgorith));

        var parser = _resolver.Resolve<IFramingParser>(typeof(Protocol1FramingParser));

        // A parameter I get and pass to each protocol at runtime
        var protocolConfig = _configService.GetConfig<Protocol1Configuration>();

        // Finally resolve the protocol with it's parameters:

        protocol = _resolver.ResolveWithParameters<IProtocol>(typeof(Protocol1), new List<object>{
            agent, resolver, parser, protocolConfig 
        });

        //...

        // Theres gotta be a better way!! 
    }

    void HandleConntection2()
    {
        // Same as in protocol1
    }

    void HandleConnection3()
    {
        // Same as in protocol1
    }
}

Take in mind that I don't want references to autofac, meaning I can't use IIndex<> which I heard off.

Thanks!

Upvotes: 2

Views: 378

Answers (1)

Cyril Durand
Cyril Durand

Reputation: 16187

You should let the dependency injection framework manage instanciation.

You use a ResolveWithParameter method where these parameters has been resolved by the dependency injection framework. It is not required and you can let the framework find the dependency for you :

If Protocol1 needs a named parameter you can specify it during registration process.

builder.Register(c => c.Resolve<IConfigurationService>()
                       .GetConfig<Protocol1Configuration>())
       .As<Protocol1Configuration>(); 

builder.RegisterType<Protocol1>()
       .Named<IProtocol1>(nameof(Protocol1))
       .WithParameter((pi, c) => pi.ParameterType == typeof(IFramingAgent), 
                      (pi, c) => c.ResolveNamed<IFramingAgent>(nameof(Protocol1))
       .WithParameter((pi, c) => pi.ParameterType == typeof(IFramingAlgorithm), 
                      (pi, c) => c.ResolveNamed<IFramingAlgorithm>(nameof(Protocol1));
builder.RegisterType<FramingAgentProtocol1>()
       .Named<IFramingAgent>(nameof(Protocol1));
builder.RegisterType<FramingAlgorithmProtocol1>()
       .Named<IFramingAlgorithm>(nameof(Protocol1));

Then ProtocolsLayer only need dependency on IIndex<String, Func<IProtocol>> (or Func<Owned<Protocol1>>)

public class ProtocolsLayer 
{
    public ProtocolsLayer(IIndex<String, Func<IProtocol>> index)
    {
        this._index = index; 
    }

    private readonly IIndex<String, Func<IProtocol>> _index;

    public void HandleConnection1()
    {
        IProtocol protocol = this._index[nameof(Protocol1)](); 
    }
}

If you don't want to introduce dependency on IIndex<,> for your whole application you can introduce a IProtocolFactory that will be defined in your runtime and create the implementation only for the registration project.

In your runtime project :

public interface IProtocolFactory
{
    IProtocol Create(String protocolName)
}

In your registration project :

public class ProtocolFactory : IProtocolFactory
{

    public ProtocolFactory(IIndex<String, IProtocol> index)
    {
        this._index = index; 
    }

    private readonly IIndex<String, IProtocol> _index; 

    public IProtocol Create(String protocolName)
    {
        return this._index[typeof(TProtocol).Name]; 
    }
}    

Then you ProtocolsLayer class will look like this :

public class ProtocolsLayer 
{
    public ProtocolsLayer(IProtocolFactory protocolFactory)
    {
        this._protocolFactory = protocolFactory; 
    }

    private readonly IProtocolFactory _protocolFactory;

    public void HandleConnection1()
    {
        IProtocol protocol = this._protocolFactory.Create("Protocol1"); 
    }
}

You can also register a Func<String, IProtocol> which will named resolve IProtocol

ProtocolsLayer will look like this :

public class ProtocolsLayer 
{
    public ProtocolsLayer(Func<String, IProtocol> protocolFactory)
    {
        this._protocolFactory = protocolFactory; 
    }

    private readonly Func<String, IProtocol> _protocolFactory;

    public void HandleConnection1()
    {
        IProtocol protocol = this._protocolFactory("Protocol1"); 
    }
}

and the registration like this :

builder.Register(c => (String namedProtocol) => c.ResolveNamed<IProtocol>(namedProtocol)
       .As<Func<String, IProtocol>>(); 

But I won't recommend this solution because the intent of Func<String, IProtocol> protocolFactory dependency is not clear. Having a IProtocolFactory interface makes dependency goal easy to understand.

Upvotes: 1

Related Questions