PilgrimViis PilgrimViis - 1 month ago 5
C# Question

Proper way to use dependency injection when some arguments will be known only at runtime

I read a bunch of questions and all docs at Ninject wiki, but I still doubt and ask questions to myself like "Am I doing it right?"


  1. What if I need to use controller for initialization of object? And field argument will be known only at runtime?



A) So I need something like this:

public class MyClass : IMyInterface
{
private string _field;
readonly IRepository _repo;
public MyClass(string field, IRepository repo)
{
_field = field;
_repo = repo;
}
}


But how to Bind it properly? Or should I forever forget about using constructor for any reason but Constructor Dependency Injection?

B) And do it like:

public class MyClass : IMyInterface
{
private string _field;
readonly IRepository _repo;
public MyClass(IRepository repo)
{
_repo = repo;
}
public void Initialize(string field)
{
_field = field;
}
}


I think it isn't ok to call anytime when I need object this Initialize function or I'm wrong?

According to this answers Can't combine Factory / DI and Dependency Inject (DI) "friendly" library and Is there a pattern for initializing objects created via a DI container


Use Abstract Factory if you need a short-lived object

Dependencies injected with Constructor Injection tend to be long-lived, but sometimes you need a short-lived object, or to construct the dependency based on a value known only at run-time.


C) I should do it like:

public interface IMyInterface { }
public interface IMyFactory
{
MyClass Create(string field);
}
public class MyFactory : IMyFactory
{
private readonly IRepository _repo;
public MyFactory (IRepository repo)
{
_repo = repo;
}
public MyClass Create(string field)
{
return new MyClass(_repo, field);
}
}
public class MyClass : IMyInterface
{
private string _field;
readonly IRepository _repo;
public MyClass(IRepository repo, string field)
{
_repo = repo;
_field = field;
}
}


And if I need MyClass from another class I gonna use it like

public class OtherClass
{
private IMyInterface _myClass;
public OtherClass(IMyFactory factory)
{
_myClass = factory.Create("Something");
}
}


Isn't it too bulky?

And my question is: What I have to use A, B or C case? And why? Or maybe something else?

Answer

What if I need to use controller for initialization of object? And field argument will be known only at runtime?

As explained here, your application components should not require runtime data during initialization. Instead you should either:

  1. pass runtime data through method calls of the API or
  2. retrieve runtime data from specific abstractions that allow resolving runtime data.

Contrary to popular belief, abstract factories are hardly ever the right solution to this problem, because:

a factory is hardly ever the right abstraction. Instead of lowering complexity for the consumer, a factory actually increases complexity, because instead of having just a dependency on the service abstraction IService, the consumer now requires a dependency on both IService and the Abstract Factory IServiceFactory. [...] the increase in complexity can be felt instantly when unit testing such classes. Not only does this force us to test the interaction the consumer has with the service, we have to test the interaction with the factory as well.

For a detailed discussion, read this article.

Comments