user881185
user881185

Reputation: 193

Dependency Injection in Transient Objects

I'd like some advice on how to structure some objects via dependency injection.

Most of my application is singletons, and it's pretty straightforward to inject singletons as dependencies of each other.

However, I have a situation where I dynamically generate some transient objects that depend on several singletons.

Here's some C# pseudocode of the current situation:

// random singletons
public static class SingletonA {...}
public static class SingletonB {...}
public static class SingletonC {...}

// random objects that all need to depend on some subset of the above singletons
public class BaseTask {...}
public class TaskA : BaseTask {...}
public class TaskB : BaseTask {...}
public class TaskC : BaseTask {...}

public class Scheduler {
    public Scheduler() {
    }

    public void Start() {
        // When these tasks are created is actually really dependent on business logic,
        // and each task executes a bunch of internal logic.
        // Each Task can create more children Task at arbitrary times too.
        ...
        var taskA = new TaskA();
        ...
        var taskB = new TaskB();
        ...
        var taskC = new TaskC();
        ...
}

There is code in all of TaskA, TaskB, TaskC, ... that calls methods on the singletons. Also, each task can construct new tasks.

If I use dependency injection, I could do something like:

public class Scheduler {
    private ISingletonA _singletonA;
    private ISingletonB _singletonB;
    ...

    public Scheduler(ISingletonA singletonA, ISingletonB singletonB, ...) {
        _singletonA = singletonA;
        _singletonB = singletonB;
        ...
    }

    public void Start() {
        ...
        var taskA = new TaskA(_singletonA, _singletonB, ...);
        ...
        var taskB = new TaskB(_singletonA, _singletonB, ...);
        ...
        var taskC = new TaskC(_singletonA, _singletonB, ...);
        ...
    }
}

This seems like a mess, so I was thinking of refactoring all of the TaskA, TaskB, TaskC into a common class and making something like a factory:

public class Scheduler {
    public TaskFactory _taskFactory

    public Scheduler(TaskFactory taskFactory) {
        _taskFactory = taskFactory;
    }

    public void Start() {
        ...
        var taskA = _taskFactory.NewTaskA(_taskFactory);
        ...
        var taskB = _taskFactory.NewTaskB(_taskFactory);
        ...
        var taskC = _taskFactory.NewTaskC(_taskFactory);
        ...
    }
}

Any better ideas? And if not, I don't think this is the factory pattern. Is there a more accurate name?

Upvotes: 1

Views: 1464

Answers (2)

John Wu
John Wu

Reputation: 52240

I would define a factory class whose sole purpose is to construct your TaskX objects, including containing all the dependencies:

class MyTaskFactory : IMyTaskFactory
{
    private readonly ISingletonA _singletonA;
    private readonly ISingletonB _singletonB;

    public MyTaskFactory(ISingletonA singletonA, ISingletonB singletonB)
    {
        _singletonA = singletonA;
        _singletonB = singletonB;
    }

    public T Resolve<T>() where T : ITask
    {
        if (typeof(T) == typeof(TaskA)) return (T)(object)GetTaskA();
        if (typeof(T) == typeof(TaskB)) return (T)(object)GetTaskB();
        if (typeof(T) == typeof(TaskC)) return (T)(object)GetTaskC();

        throw new ArgumentException(string.Format("Type not supported: {0}", typeof(T).FullName));
    }

    protected TaskA GetTaskA()
    {
        return new TaskA(_singletonA);
    }

    protected TaskB GetTaskB()
    {
        return new TaskB(_singletonA, _singletonB);
    }

    protected TaskC GetTaskC()
    {
        return new TaskC(_singletonA, "Other runtime parameter");
    }
}

public class Scheduler
{
    protected readonly IMyTaskFactory _taskFactory;

    public Scheduler(IMyTaskFactory taskFactory)
    {
        _taskFactory = taskFactory;
    }

    public void Start()
    {
        var taskA = _taskFactory.Resolve<TaskA>();
        var taskB = _taskFactory.Resolve<TaskB>();
        var taskC = _taskFactory.Resolve<TaskC>();
    }
}

You then add the factory to your composition root:

container.Register<IMyTaskFactory,MyTaskFactory>();

And the dependencies will show up where they are needed.

Click here for a Fiddle containing compilable code.

Upvotes: 1

krim
krim

Reputation: 11

Correct me if I'm wrong but I think what you did is something similar to the Abstract Factory pattern.

I'm not sure as to how deep you want your application to be configurable. What I suggest with your current structure and based on what you showed is to create an application file where you can mix and match the injections.

    {
        "Schedulers": [
            {
                "id": "A",
                "type": "....",
                "tasks": "tA, tB"
            },
            {
                "id": "B",
                "type": "....",
                "tasks": "tC"
            }
        ],
        "Tasks": [
            {
                "id": "tA",
                "type": "..."
            },
            {
                "id": "tB",
                "type": "..."
            },
            {
                "id": "tC",
                "type": "..."
            }
        ]
    }

The basic idea is that you can assign "tasks" to your "schedulers".

However, in order to do this one, you need to use another IoC container such as Autofac since the default ASP.NET Core DI container makes this somewhat impossible to achieve.

Upvotes: 0

Related Questions