How to add service dependancies in ASP.NET CORE API - c#

I am new to ASP.NET CORE API so please bare with my limited understand. I am learning about dependancy injection and I am trying to register my services in the start up class. However I noticed that one of my services has an dependancy to another service via constructor. How do I pass this within the configure services method.
Do I just 'new up' a the dependancy class and add it within the constructor.
Here is my implementation in the start up class:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "TestAPI", Version = "v2" });
});
services.AddSingleton<IAdminRepository>(new AdminRepository());
services.AddSingleton<IAdminService>(new AdminService(need to add dependancy here));
}
Here is implementation in my service class:
private IAdminRepository _adminRepository;
public AdminService(IAdminRepository adminRepository)
{
_adminRepository = adminRepository;
}

You don't need to add the dependency explicitly, you just have to register your service with
services.AddSingleton<IAdminService, AdminService>();
The dependency injection framework will automatically detect the required services for your AdminService.

You don't need to add the dependency explicitly, add a theme like this pattern.
services.AddSingleton<IAdminRepository, AdminRepository>();
services.AddSingleton<IAdminService, AdminService>();
This way, the DI framework will do it wherever needed to create a new instance.
moreover, you can use Addscope or Addtransient if you need it, for example:
services.Addscope<IAdminRepository, AdminRepository>();
services.Addscope<IAdminService, AdminService>();

Related

Is it possible to pass the ServiceProvider into a constructor parameter?

Problem:
We have a .NET 5 WPF application that has an EntityFramework Core entities class file DbEntities, which implements the DbContext. We use constructor injection when instantiating it. One of the options that we use is AddInterceptors in order to append an Access Token to the SqlConnection. The interceptor is called AzureAuthenticationInterceptor. When registering the service, we would like to pass in the ServiceProvider so that it is available in the interceptors constructor, which can be used to get a service that implements Access Token in-memory caching.
The reason for it is that we have a project with 50+ classes that all use the same DbEntities file, which takes 0 arguments in the constructor. This was upgraded to .NET 5 where Dependency Injection was avoided due to the work it would take to apply it to all of the forms. So, the DbEntities is instantiated in the forms with new DbEntities();.
But, in this case, we are implementing an access token cache, which needs to be registered as a service. Otherwise, if we just instantiate the cache every time we create a new DbContext, then the cache will be wiped out.
The access token in-memory cache is implemented using this method https://mderriey.com/2020/09/12/resolve-ef-core-interceptors-with-dependency-injection/
We only want to use dependency injection for the in-memory token cache. The only way we think of as a shortcut is to pass the ServiceProvider in the interceptor's constructor, but it does not appear available in the ConfigureServices method.
Question:
Is it possible to pass in the ServiceProvider? If not, is there any other way we can implement dependency injection on the interceptor without having to change 50 class files?
Program.cs
Public static void Main()
{
...
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, builder) =>
{
builder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
})
.ConfigureServices((context, services) =>
{
Configuration = context.Configuration;
ConfigureServices(Configuration, services);
})
.Build();
...
}
private static void ConfigureServices(IConfiguration objConfiguration, IServiceCollection objServices)
{
objServices.AddMemoryCache()
.AddSingleton<IAzureSqlTokenProvider, AzureIdentityAzureSqlTokenProvider>()
.Decorate<IAzureSqlTokenProvider, CacheAzureSqlTokenProvider>()
.AddSingleton(new AzureAuthenticationInterceptor(IServiceProvider_NeededHere))
;
}
DbEntities.cs
public DbEntities() :
base(new DbContextOptionsBuilder<DbEntities>()
.UseSqlServer(ConfigurationManager.ConnectionStrings["DbEntities"].ConnectionString)
.AddInterceptors(new AzureAuthenticationInterceptor())
.Options)
{ }
AzureAuthenticationInterceptor.cs
public AzureAuthenticationInterceptor(IServiceProvider objServiceProvider)
{
this.IAzureSqlTokenProvider = (IAzureSqlTokenProvider)objServiceProvider.GetService(typeof(IAzureSqlTokenProvider));
}
First, avoid injecting IServiceProvider, it is a code smell and leads to poor design.
Refactor AzureAuthenticationInterceptor.cs
public AzureAuthenticationInterceptor(IAzureSqlTokenProvider tokenProvider) {
this.IAzureSqlTokenProvider = tokenProvider;
}
So that way explicit dependencies can be injected as needed
//...
.AddSingleton<AzureAuthenticationInterceptor>()
//...
When resolving the interceptor while configuring the DbEntities
//...
services.AddDbContext<DbEntities>((provider, options) => {
options.UseSqlServer(Configuration.GetConnectionString("<connection-string-name>"));
options.AddInterceptors(provider.GetRequiredService<AzureAuthenticationInterceptor>());
});
//...
Note that if you are manually initializing the context using the default constructor, ie:new DbEntities(); Then this bypasses the opportunity to apply dependency injection via constructor injection.

Middleware add new dependency to DI Container or Services

I am writing a piece of middleware (maybe I want a scoped service??), I guess my plan is to have some kind of multi-tenant scenario.
If for example, I have 2 domains that respond on this service:
www.domain1.com
www.domain2.com
I want to capture the request when it starts, look at the host name that is being used and then set some other object to be available through Dependency Injection for everything further up the pipeline.
It seems that middleware should be the right way to achieve this, but not sure how to do the final step.
My options seem to be:
Middleware
Register Singleton service to access database
Register early to be the first item of middleware to capture the request.
Analyse Request Object and build custom configuration object
Add custom configuration as a scoped object to the DI container for use by other services
Service
Register Singleton service to access database
Register Singleton service for IHttpContextAccessor
Register Scoped? Service - to do equivalent of middleware
Analyse the request object and build custom configuration object
Register custom object as new scoped object in the DI container
My assumption is that the Service is able to register the custom scoped object as it is still within the ConfigureServices method of the startup.cs
However, with middleware it is initialised through the Configure method by which point the DI container has already been built?
You can use the factory-overload of AddScoped for the service you want to be different per tenant/request. Here's an example:
services.AddScoped<IServiceForTenant>(sp =>
{
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
var serviceForTenant = new ServiceForTenant();
// TODO: Use httpContextAcccessor.HttpContext to configure serviceForTenant.
return serviceForTenant;
});
For each request that comes in to your ASP.NET Core application, the code above will run when you first request IServiceForTenant in e.g. a controller. At this point, your code can read from IHttpContextAccessor.HttpContext and make whatever decisions it needs in order to create the implementation instance for IServiceForTenant. This same instance will then be used for the rest of the request (i.e. further up the pipeline).
The argument passed into AddScoped is Func<IServiceProvider, T>. All you need to provide here is a delegate of some kind, which could be done in one of many ways. Here's some examples:
You could just wrap the call into its own extension method, like this:
public static void AddServiceForTenant(this IServiceCollection services)
{
services.AddScoped<IServiceForTenant>(sp =>
{
// ...
});
}
In ConfigureServices:
services.AddServiceForTenant();
Use a class with a static method:
public static class ServiceForTenantFactory
{
public static ITenantForService Create(IServiceProvider sp)
{
// ...
}
}
In ConfigureServices:
services.AddScoped(ServiceForTenantFactory.Create);
Use a class with an instance method:
public class ServiceForTenantFactory
{
public ITenantForService Create(HttpContext httpContext)
{
// ...
}
}
In ConfigureServices:
services.AddScoped(sp =>
{
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
var serviceForTenantFactory = new ServiceForTenantFactory(); // Or use DI.
return serviceForTenantFactory.Create(httpContextAccessor.HttpContext);
});
This last option is the most flexible, as you could even resolve ServiceForTenantFactory itself from DI and it can have its own dependencies, etc. Note also that Create here takes the HttpContext directly (as an example).
As I've already said, there are yet more options than the three of shown, but this should be a good base to work with.

Dependency Injection for Azure chat bot middleware?

I am working on a new chat bot using Azure Bot Service and QnAMaker. We are using BotBuilder middleware, including custom middleware, to tailor the bot behavior.
One of the middlewares will be calling an Azure function and I would like to use the new HttpClientFactory feature with the custom middleware - but this requires dependency injection.
How can I use dependency injection in BotBuilder middleware like you do with regular .NET Core middleware?
When you look at the bot configuration in the Startup.cs, you can see how it requires you to new up all of the bot dependencies:
services.AddHttpClient<MyFunctionClient>(client =>
{
client.BaseAddress = new Uri(mySettings.GetValue<string>("myFunctionUrl"));
client.DefaultRequestHeaders.Add("x-functions-key", mySettings.GetValue<string>("myFunctionKey"));
});
services.AddBot<QnAMakerBot>(options =>
{
options.CredentialProvider = new ConfigurationCredentialProvider(Configuration);
options.ConnectorClientRetryPolicy = new RetryPolicy(
new BotFrameworkHttpStatusCodeErrorDetectionStrategy(),
3,
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(20),
TimeSpan.FromSeconds(1));
var middleware = options.Middleware;
middleware.Add(new ConversationState<ChatLog>(new MemoryStorage()));
middleware.Add(new MyCustomMiddleware()); // <- I want to inject a typed HttpClient here
//... etc. ....
Is there a different way to configure the bot that allows for dependency injection?
If MyCustomMiddleware requires a typed HttpClient in its constructor, I have to create a new instance right here, so I don't get the benefit of the DI and the configuration I just set up.
While I am not a fan of service locator pattern, the current design of the bot configuration is not very dependency injection friendly.
Using the nature of how the bot middleware are setup but having to provide a new instance during startup, I came up with the following work around.
public class BotMiddlewareAdapter<TMiddleware> : IMiddleware
where TMiddleware : IMiddleware {
private readonly Lazy<TMiddleware> middleware;
public BotMiddlewareAdapter(IServiceCollection services) {
middleware = new Lazy<TMiddleware>(() =>
services.BuildServiceProvider().GetRequiredService<TMiddleware>());
}
public Task OnTurn(ITurnContext context, MiddlewareSet.NextDelegate next) {
return middleware.Value.OnTurn(context, next);
}
}
It takes the IServiceCollection as an explicit dependency and defers the creation of the service provider and eventual resolution of the actual middleware in a factory delegate.
It can then be implemented using
middleware.Add(new BotMiddlewareAdapter<MyCustomMiddleware>(services));
When the adapter is invoked it will lazy resolve the intended middleware on initial call and then invoke it.
In fact you can take this another step further and convert it to an extension method
public static class BotBuilderMiddlewareExtension {
public static void Add<TMiddleware>(this IList<IMiddleware> middleware, IServiceCollection services)
where TMiddleware : IMiddleware {
middleware.Add(new BotMiddlewareAdapter<TMiddleware>(services));
}
}
Which simplifies the setup to
middleware.Add<MyCustomMiddleware>(services);

Get per-request dependency from an Autofac func factory

I'm using ASP.NET Core and Autofac. Almost everything is registered as per lifetime scope ("per request"). So my database context DbContext is the same instance throughout a request.
However I have a singleton which also depends on DbContext. To avoid a captive dependency, it is injected as Func<Owned<DbContext>>, which means a new DbContext instance each time.
The problem is I need the same instance, as everywhere else during the request, not a new one.
I want to avoid a captive dependency bug, but I also want the same instance. Is that possible via tagging or a custom registration?
From the comments the least "architectural" painful approach may be by creating your own Scoped<T> class which will resolve the DbContext from current HttpContext
// Use an interface, so we don't have infrastructure dependencies in our domain
public interface IScoped<T> where T : class
{
T Instance { get; }
}
// Register as singleton too.
public sealed class Scoped<T> : IScoped<T> where T : class
{
private readonly IHttpContextAccessor contextAccessor;
private HttpContext HttpContext { get; } => contextAccessor.HttpContext;
public T Instance { get; } => HttpContext.RequestServices.GetService<T>();
public Scoped(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
}
}
Register it as
// Microsoft.Extensions.DependencyInjection
services.AddSingleton(typeof(IScoped<>), typeof(Scoped<>);
// Autofac
containerBuilder.RegisterType(typeof(Scoped<>))
.As(typeof(IScoped<>));
Then inject this into your validator service.
public class CustomerValidator: AbstractValidator<Customer>
{
private readonly IScoped<AppDbContext> scopedContext;
protected AppDbContext DbContext { get } => scopedContext.Instance;
public CustomValidator(IScoped<AppDbContext> scopedContext)
{
this.scopedContext = scopedContext ?? throw new ArgumentNullException(nameof(scopedContext));
// Access DbContext via this.DbContext
}
}
This way you can inject any scoped service w/o further registrations.
Additional notes
Autofac is considered a "conformer" (see docs) DI and integrates well with ASP.NET Core and Microsoft.Extensions.DependencyInjection.
From the documentation
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add services to the collection.
services.AddMvc();
// Create the container builder.
var builder = new ContainerBuilder();
// Register dependencies, populate the services from
// the collection, and build the container. If you want
// to dispose of the container at the end of the app,
// be sure to keep a reference to it as a property or field.
builder.RegisterType<MyType>().As<IMyType>();
builder.Populate(services);
this.ApplicationContainer = builder.Build();
// Create the IServiceProvider based on the container.
return new AutofacServiceProvider(this.ApplicationContainer);
}
There a few subtle differences to the default usage of Startup class and Microsoft.Extensions.DependencyInjection container.
ConfigureServices isn't void anymore, it returns IServiceProvider. This will tell ASP.NET Core to use the returned provider instead of DefaultServiceProvider from Microsoft.Extensions.DependencyInjection.
We return the Autofac container adapter: new AutofacServiceProvider(this.ApplicationContainer) which is the root container.
This is important to make ASP.NET Core use the container everywhere in ASP.NET Core, even inside middlewares which resolve per request dependencies via HttpContext.RequestedServices.
For that reasons you can't use .InstancePerRequest() lifetime in Autofac, because Autofac isn't in control of creating scopes and only ASP.NET Core can do it. So there is no easy way to make ASP.NET Core use Autofac's own Request lifetime.
Instead ASP.NET Core will create a new scope (using IServiceScopeFactory.CreateScope()) and use a scoped container of Autofac to resolve per-request dependencies.

.NET Core with Autofac DI

I want to integrate Autofac to my API. Solution is split on several projects so that everything stays decoupled. I have set up my configure services like this:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add framework services.
...
...
// Autofac
var builder = new ContainerBuilder();
builder.RegisterType<RouteRepository>().As<IRouteRepository>();
builder.Populate(services);
ApplicationContainer = builder.Build();
return new AutofacServiceProvider(ApplicationContainer);
}
However now with this code integrated, my API won't start anymore. If I start it in debug mode, I get no errors, but I don't get response either.
API landing route is pretty straightforward:
public IActionResult GetIndex()
{
return Ok("You are seeing this because controller is working!");
}
Also, what might be connected to the problem is that RouteRepository takes one variable as an argument in the constructor and I don't know where can I define what will be passed through? There is no config file by default.
If you're saying that you have one dependency for your RouteRepository, then you have to notify Autofac container how to resolve that:
// singletone
builder.RegisterInstance(new TaskRepository())
.As<ITaskRepository>();
// or instance based creation
builder.Register(c => new LogManager(DateTime.Now))
.As<ILogger>();
Or Autofac couldn't resolve your type.

Categories

Resources