For example, I have a class CreateAutoDeletingRequestReachSuspensionDaysLimitAndInactiveLongTimeService
with the following dependencies:
protected readonly IDeviceService _deviceService;
protected readonly IAzureFunctionLogService _azureFunctionLogService;
protected readonly IDeviceValidationService _deviceValidationService;
so, I can create ctor for the class:
public CreateAutoDeletingRequestReachSuspensionDaysLimitAndInactiveLongTimeService(
IDeviceService deviceService,
IDeviceValidationService deviceValidationService,
IAzureFunctionLogService azureFunctionLogService)
{
_deviceService = deviceService;
_deviceValidationService = deviceValidationService;
_azureFunctionLogService = azureFunctionLogService;
}
then inject all dependencies like:
services.AddTransient<CreateAutoDeletingRequestReachSuspensionDaysLimitAndInactiveLongTimeService>();
services.AddSingleton<Func<CreateAutoDeletingRequestReachSuspensionDaysLimitAndInactiveLongTimeService>>(sp =>
() => sp.GetRequiredService<CreateAutoDeletingRequestReachSuspensionDaysLimitAndInactiveLongTimeService>()
);
and then use it like this:
private readonly Func<CreateAutoDeletingRequestReachSuspensionDaysLimitAndInactiveLongTimeService> _service;
public FunctionDebugPendingStatusWorkflow(
Func<CreateAutoDeletingRequestReachSuspensionDaysLimitAndInactiveLongTimeService> service,
//....
ILoggerFactory loggerFactory)
{
_service = service;
//....
_logger = loggerFactory.CreateLogger<FunctionDebugPendingStatusWorkflow>();
}
so, it works fine.
But how can I add one more param to ctor, which set in caller? For example, I want to pass deviceId
to ctor and can't describe it as dependency using dependency injector in Program.cs
(in my case)
I have to create "Init" method like this:
public void Init(int deviceId)
{
_device = _deviceService.GetDeviceById(deviceId);
// ...
}
and add logic there.
Then I have to call _service.Init(...);
before using _service methods. It works, but all disadvantages and potential problems are obviously (if forgot to call etc)
How to pass this parameter using DI ?
✅ You can do it with .NET 6 Dependency Injection like this.
First, create a .NET 6 Console App (with top-level statements) and add necessary NuGet packages.
📦 Install NuGet packages:
Add usings:
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
Create a dependency that can be injected to our service and in other places:
public class Dependency : IDependency
{
// Initialize Id with a random value to validate that it's the same instance for the same scope
public Guid Id { get; init; } = Guid.NewGuid();
}
// Interface for DI
public interface IDependency
{
public Guid Id { get; }
}
Define our service with custom ID per scope:
public class RequestService : IRequestService
{
public IDependency Dependency { get; init; }
private int _id;
// Constructor is used for standard dependency injection
public RequestService(IDependency dependency)
{
Dependency = dependency;
}
// Init method is used to set custom ID via DI.
public void Init(int id)
{
_id = id;
}
}
public interface IRequestService
{
IDependency Dependency { get; }
void Init(int id);
}
Create a Host Builder:
var builder = Host.CreateDefaultBuilder();
// You can do exactly the same with an `WebApplicationBuilder`
// WebApplication.CreateBuilder(args);
Start configuring DI:
builder.ConfigureServices(services =>
{
// Must be scoped
services.AddScoped<IDependency, Dependency>();
// Must be scoped
services.AddScoped<IRequestService, RequestService>();
// <Add a factory method DI configuration here>
});
5.1. Configure a factory to instantiate your service via DI and to set an arbitrary ID during that.
💥 This is the main part of the DI magic
Factory function will do:
int
id as an input.IRequestService
via DI (essentially, will create a RequestService
object)..Init(id)
with ID value passed as an argument.// Add into `builder.ConfigureServices(services => {})`
// Must be scoped
services.AddScoped<Func<int, IRequestService>>(implementationFactory: provider =>
{
// Create a new factory
return new Func<int, IRequestService>(id =>
{
var result = provider.GetRequiredService<IRequestService>();
result.Init(id);
return result;
});
});
After that just build the Host and create a DI scope:
var host = builder.Build();
var scope = host.Services.CreateScope();
Finally, use the DI:
// Resolve an `IDependency` just for the sake of the example
var dependency = scope.ServiceProvider.GetRequiredService<IDependency>();
// Resolve a factory for your service
var requestServiceFactory = scope.ServiceProvider.GetRequiredService<Func<int, IRequestService>>();
// Use a factory with custom ID
var requestService = requestServiceFactory(32);
Note: dependency
and requestService.Dependency
will be the same instance.
So, if you inject Func<int, IRequestService>
anywhere, you'll be able to instantiate your IRequestService
using that factory with custom ID.
👍 You will never forget to call .Init(id)
as it's required by the function signature and compiler will not allow you to skip it.
❗ If your service is disposable, you would have to take care of it yourself. DI will not help you with it.
⚠ I use a Generic Host and Console App but it will work exactly in the same way in the Web API or ASP.NET MVC application.
Useful links: