Almost any controller needs some external dependencies to work. Here is a way to configure a dependency object (or its factory) and pass it to a controller. Doing so will help to sustain a separation of concerns, keep code clear and testable.
Say, we have an interface and its implementation that needs some values from config in its constructor:
public interface ISomeDependency
{
async Task<IEnumerable<string>> GetItemsAsync(string key);
}
public class SomeDependency : ISomeDependency
{
public SomeDependency(string connectionString)
{
...
}
...
}
It's used in some controller class:
public class SomeController : Controller
{
private reanonly ISomeDependency dependency;
public SomeController(ISomeDependency dependency)
{
...
this.dependency = dependency;
}
...
public async Task<IEnumerable<string>> Get(string key) =>
await dependency.GetItemsAsync(key);
}
One can inject this dependency in the controller constructor calling services.AddTransient
inside Startup.ConfigureServices
method:
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder().
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
...
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient(serviceProvider =>
new MyDependency(Configuration["Data:ConnectionString"]));
}
...
}
Here Data:ConnectionString
is a path to a setting in appsettings.json
file:
{
...
},
"Data": {
"ConnectionString": "some connection string"
}
}
To manage a lifetime of the injected object, along with AddTransient
another two options exist: AddSingleton
and AddScoped
. The last one means that lifetime of the object is scoped to a HTTP request.