0

I'm looking to use Unity to resolve types at runtime based on specific data received. My code (similar to that shown below) currently registers the types in a bootstrapper class at start-up and then within the main flow a decision is made on what type is required.

What I'm looking to do is replace the code lines that use the 'new' keyword with a resolver, however as this code is outwith my bootstrapper I'm not sure how this can be done...I'm new to Unity so please go easy.

// In Bootstrapper class
resolver.RegisterType<IDataType1, DataType1>();
resolver.RegisterType<IDataType2, DataType2>();
resolver.RegisterType<IDataType3, DataType3>();


// Main flow...outwith bootstrapper
switch (dataRecordType)
{
    case DataRecordType.dataType1:
        DataType1 dt1 = new DataType1();
        dt1.ProcessData();
        break;

    case DataRecordType.dataType2:
        DataType2 dt2 = new DataType2();
        dt2.ProcessData();
        break;

    case DataRecordType.dataType3:
        DataType3 dt3 = new DataType3();
        dt3.ProcessData();
        break;

    default:
        break;
}

1 Answer 1

2

You are missing a few abstractions here. You're missing an general abstraction over your data types and an abstraction for creating implementations of those data types:

// In your core layer
public interface IDataType {
    void ProcessData();
}

public interface IDataTypeFactory {
    IDataType Create(DataRecordType dataRecordType);
}

// In Bootstrapper class
resolver.RegisterInstance<IDataTypeFactory>(new DataTypeFactory(resolver));
resolver.RegisterType<IDataType1, DataType1>();
resolver.RegisterType<IDataType2, DataType2>();
resolver.RegisterType<IDataType3, DataType3>();

private sealed class DataTypeFactory : IDataTypeFactory {
    private readonly IUnityContainer container;
    public DataTypeFactory(IUnityContainer container) {
        this.container = container;
    }

    public IDataType Create(DataRecordType dataRecordType) {
        switch (dataRecordType) {
            case DataRecordType.dataType1:
                return this.container.Resolve<IDataType1>();
            case DataRecordType.dataType2:
                return this.container.Resolve<IDataType2>();
            case DataRecordType.dataType3:
                return this.container.Resolve<IDataType3>();
            default:
                throw new InvalidEnumArgumentException();
        }
    }
}

What you can see is that the code for creating implementations is moved to the factory. Now the remaining application code can be comes something like this:

// Main flow...outwith bootstrapper
IDataType dt = this.dataTypeFactory.Create(dataRecordType);
dt.ProcessData();

The IDataType1, IDataType2 and IDataType3 are now only used in the bootstrapper and have become redundant (or at least, redundant with the code you presented), so you could even remove them all together and change the bootstrap logic to the following:

// In Bootstrapper class
resolver.RegisterInstance<IDataTypeFactory>(new DataTypeFactory(resolver));
resolver.RegisterType<DataType1>();
resolver.RegisterType<DataType2>();
resolver.RegisterType<DataType3>();

private sealed class DataTypeFactory : IDataTypeFactory {
    private readonly IUnityContainer container;
    public DataTypeFactory(IUnityContainer container) {
        this.container = container;
    }

    public IDataType Create(DataRecordType dataRecordType) {
        switch (dataRecordType) {
            case DataRecordType.dataType1:
                return this.container.Resolve<DataType1>();
            case DataRecordType.dataType2:
                return this.container.Resolve<DataType2>();
            case DataRecordType.dataType3:
                return this.container.Resolve<DataType3>();
            default:
                throw new InvalidEnumArgumentException();
        }
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

+1 It is worth mentioning that IOC container shouldn't slip the composition root (as OP seems to be a beginner). Also is there a way that we can create a factory without coupling the code with container.Resolve?
@SriramSakthivel: Thanks for noticing. I agree, the container shouldn't be referenced anywhere but the Composition Root. It's possible to decouple the factory from the container, but since this factory implementation can be part of the Compostion Root, there's no problem that the factory references the container itself. But another option is to inject the DataTypeX classes into the factory instead of the container.
I guess you mean to inject Func<DataTypeX> to Factory? otherwise we need multiple factory instances for getting new instance of DataTypeX isn't it?
@SriramSakthivel: No. What I mean is to have a factory with the following constructor: DataTypeFactory(DataType1 dt1, DataType2 dt2, DataType3 dt3).
Yes, but then your factory will always return same instance. That changes the behavior am I right? To achieve the current behavior(transient) we need DataTypeFactory(Func<DataType1> dt1, Func<DataType2> dt2, ...) Correct me if am wrong. I'm autofac user, default lifetime is instance per dependency there, that's why I ask this question :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.