Search code examples
c#inversion-of-controlunity-containerfactory-pattern

Unity - Resolving the correct instance depending on the context


I have been using Unity as my IoC container for a while and I could not find the correct solution for a recurrent problem. I need to create some instances of AuditInsertLogWriter and AuditUpdateLogWriter (they implement IAuditOperationLogWriter interface) depending on the operation argument as you can see bellow:

public IAuditOperationLogWriter Create(Operation operation)
{
    switch (operation)
    {
        case Operation.Insert:
            return UnityContainer.Resolve<AuditInsertLogWriter>();
        case Operation.Update:
            return UnityContainer.Resolve<AuditUpdateLogWriter>();
        default:
            break;
    }
}

The thing is that those instances are complex to create because of their own dependencies. Moreover, I want to remove the dependency with Unity in this factory. So, my question is: how can I achieve that Unity resolves the correct type to create depending on some context?


Solution

  • If you want to remove the dependence of the factory on the container you will have to wire up all of the dependencies ahead of time.

    An elegant way to do this would be with Unity's support for automatic factories.

    You could define your factory like this:

    public class AuditOperationLogWriterFactory
    {
        private Dictionary<Operation, Func<IAuditOperationLogWriter>> auditCreator = 
            new Dictionary<Operation, Func<IAuditOperationLogWriter>>();
    
        public AuditOperationLogWriterFactory(Func<AuditInsertLogWriter> insert,
            Func<AuditUpdateLogWriter> update)
        {
            auditCreator[Operation.Insert] = insert;
            auditCreator[Operation.Update] = update;
        }
    
        public IAuditOperationLogWriter Create(Operation operation)
        {
            if (auditCreator.ContainsKey(operation))
            {
                return auditCreator[operation]();
            }
    
            return null;
        }
    }
    

    And then you can just resolve the factory and Unity should wire up the dependencies and the object creation is deferred until runtime (based on context). If they are complicated you may have to help via configuration (config file, code, or attributes).

    IUnityContainer container = new UnityContainer();
    
    var factory = container.Resolve<AuditOperationLogWriterFactory>();
    
    var logWriter = factory.Create(Operation.Insert);