Search code examples
c#dependency-injectioninversion-of-controlinstantiationstructuremap

StructureMap Supply string Parameter to Constructor when Requesting an Instance


Hi in Autofac I am abble to do this:

public class Service1_Client
{
    private const string ServiceName = "Service1";
    Func<string, RabitMqClient> _clientFunc;

    public Service1_Client(Func<string, RabitMqClient> clientFunc) 
    { 
         _clientFunc = clientFunc;
    }

    public IList<object> GetData()
    {
        return _clientFunc(this.ServiceName).Get<IList<object>>();               
    }
}

public class Service2_Client
{
    private const string ServiceName = "Service2";
    Func<string, RabitMqClient> _clientFunc;

    public Service2_Client(Func<string, RabitMqClient> clientFunc) 
    { 
         _clientFunc = clientFunc;
    }

    public IList<object> GetData()
    {
        return _clientFunc(this.ServiceName).Get<IList<object>>();               
    }
}

public class RabitMqClient
{
    private IConnectionFactory _connectionFactory;
    private string _baseServiceName;
    public RabitMqClient(IConnectionFactory connectionFactory, string baseServiceName)
    {
        _connectionFactory = connectionFactory;
        _baseServiceName = baseServiceName;
    }

    public TResult Get<TResult>()
    {
        string topicName = this.GetTopicName();
        ///Connect to the channel that uses the topic and ask for data...
    }

    private string GetTopicNAme()
    {
        /// this should be an algoritam for getting the topic from the speccified baseServiceName (I have omitted the details)
        return this._baseServiceName;
    }     
}

Now the explanation: Basically I have a class (RabitMqClient) that depends on 2 parameters:

  1. connectionFactory (IConnectionFactory )
  2. baseServiceName (string)

Those parameters are used to connect to the RabbitMq server. Now I use Topics in RabbitMq to diversify services, and to get the correct Topic, the string parameter baseServiceName is used. It is expected the caller (Service1_Client, Service2_Client) to supply the string parameter baseServiceName.

In Autofac I can register this class like this:

builder.Register((context, parameters) =>
{
    var param = parameters.First() as Autofac.TypedParameter;
    return new HcRabbitMqClient(
        context.Resolve<IConnectionFactory>(),param.Value.ToString());
}).AsSelf().InstancePerDependency();

This tells Autofac, that when requesting this class, let the Autofac resolve the first parameter but the Caller will supply the second (string) parameter at the call site. This string parameter will then be put in the parameters value. The usage of this can be seen in the code above (Service1_Client, Service2_Client) GetData() methods;

How to do this in StructureMap?


Solution

  • What about extending that factory pattern?

    public interface IRabitMqClientFactory
    {
        IRabitMqClientFactory Create(string baseServiceName);
    }
    
    public class RabitMqClientFactory : IRabitMqClientFactory
    {
        public RabitMqClientFactory(IConnectionFactory connectionFactory){
            _connectionFactory = connectionFactory;
        }
    
        public IRabitMqClientFactory Create(string baseServiceName)
        {
            _baseServiceName = baseServiceName;
        }
    }
    

    and then simply

    public class Service1_Client
    {
        private const string ServiceName = "Service1";
        private RabbitMqClient _rabbitMqClient;
    
        public Service1_Client(IRabitMqClientFactory rabitMqClientFactory) 
        { 
             _rabbitMqClient = rabitMqClientFactory.Create(ServiceName);
        }
    
        public IList<object> GetData()
        {
            return _rabbitMqClient.Get<IList<object>>();               
        }
    }
    

    it's also a bit more readable than having complex logic inside your IoC.