I have a translation service and I need to expose a property that I want to use across my application.
services.AddScoped<IMyTranslator, MyTranslator>();
I use services.AddScoped to register this interface and its implementation. When I set a breakpoint to the constructor of MyTranslator, I can see that this class is initialized all the time.
I could not use AddSingleton, because it's shared across all sessions. I can not change the language for one user base on another user has changed language once.
I need a method that will initialize my middleware once per session.
The services.AddScoped is already scoped to the user request, each time a user request reaches the server, an instance is created to serve this specific request only and it is not shard with other users.
Related
Why I should use AddScoped() for my repositories or services? Why not AddSingleton()?
I know about differences between them, but dont understand why I shouldn't use singleton instances to avoid creating new objects for each requests.
Can you explain it (preferably with examples :) )?
As you said, you know the difference so I won't get into that.
The reason you don't want addSingleton for your repositories or services is because typically your repositories and services are considered "business logic" and "persistence logic". And in your business logic you might have some class level variables that are getting set. Those properties would not be different for every request, they would be shared across the requests. (think of them like static properties).
Example:
Imagine you have a user service that sets the username of the user making the request as a class level variable.
Singleton logic:
Now imagine Bob makes a request to the api. The username would be set to "Bob" . Now imagine at the same time, John makes a request to the api. The username would get set to "John". But because the user service is a singleton, both John and Bob are sharing the same instance, meaning Bob's username would also be set to "John".
Scoped logic:
Imagine the exact same scenario as above, but this time when John makes a request, it does not override bobs username, because they are different instances.
The below three methods define the lifetime of the services,
AddTransient
Transient lifetime services are created each time they are requested. This lifetime works best for lightweight, stateless services.
AddScoped
Scoped lifetime services are created once per request.
AddSingleton
Singleton lifetime services are created the first time they are requested (or when ConfigureServices is run if you specify an instance there) and then every subsequent request will use the same instance.
Reference here
Imagine you have a aspnet-core project.
If you want to create an object only once during the program's runtime and use the same object each time, you should use addingingleton.
If you want an object to be new() again every time it receives a request while the program is running, you should use addscoped().
If you want an object to new() every request and response, you must use AddTransient.
Example value of 3 methods
Understanding with an infographic
When registering with HttpclientFactory, can only do certificate injection in startUp. Is there any way to do dynamic injection?
services.AddHttpClient().ConfigurePrimaryHttpMessageHandler(() => {
var certificate = new X509Certificate2("", "", X509KeyStorageFlags.MachineKeySet);
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(certificate);
return handler;
});
For any future users who stumble across this question, I had a very similar need and was frustrated with this limitation of the default HttpClientFactory implementation; I had a very robust HttpClient pipeline with Polly and delegating handlers all chained up, but the underlying certificate that needed to be used might be different based on the specific endpoint URL, and I didn't necessarily want to register four or five versions of the same pipeline for different typed clients nor did I want to have to have the certificates available at the composition root. I wrote a library to extend DefaultHttpClientFactory to add support for contextually applied handlers while retaining the pooling, expiry, and typed client pipelines that DefaultHttpClientFactory provides.
https://github.com/agertenbach/Ringleader
https://www.nuget.org/packages/Ringleader
The DefaultHttpClientFactory uses the named client's name or typed client's type name to uniquely distinguish and track both the pipeline and options you attach at startup, as well as the name that the resultant primary handler will have in the managed pool; for example, a typed client "MyTypedClient" will have primary handlers in the pool named "MyTypedClient" that will get reused and renewed as needed.
By decorating the HttpClientFactoryOptions IOptionsMonitor that DefaultHttpClientFactory uses and hooking in an IHttpMessageHandlerBuilderFilter during the handler management processes, it's possible to keep all the pipeline stuff working, but create and resolve more granular handlers in the pool, i.e. "MyTypedClient-contextA", "MyTypedClient-contextB", that have different contextual settings, such as a different certificate. The library just requires you implement a couple interfaces to distinguish those contexts and then return a well-formed primary handler for that context when a new one must be created.
This still is not ideal if your certificates are going to be different on every request or very infrequently reused (as you're losing all the benefits of the pooling anyways), but if you have a well-developed typed client pipeline that has a different cert for URL A vs URL B (or user A vs user B, etc.) and you're connecting to them all fairly frequently, this might save you some headaches. Happy for any feedback or comments if this helps you out.
The delegate you are passing in for ConfigurePrimaryHttpMessageHandler is called depending on its lifetime (two minutes by default). So in that delegate you can actually make a dynamic list of certificates (and it will called every two minutes -when you create the HttpClient instance-).
You can choose the lifetime of the handler using SetHandlerLifeTime (on the builder on AddHttpClient()) and choose a shorter time span if it needs be... however if you need a different handler for the same client many times, that defies the whole purpose of using the HttpClientFactory.
So I see three options:
If you have sets of different certificates, use named or typed clients and assign a different handler for each name/type.
If, instead, your http client "generally" uses the same certificates and they just change at some point in time... then set a reasonable lifespan (with SetHandlerLifetime) depending on your application needs.
If you actually need the certificates to be defined per-request (or per-instance)... then don't use the HttpClientFactory or any other client pooling method because you do not want to have the clients pooled at all and you want a different one (with its own handler) per request.
Scenario
I am working to follow the IoC pattern and use the Microsoft DI Framework, but I'm hitting a wall and can't decide if it's my mindset, my approach, or I'm just doing it wrong.
I have a multi-tenant application that utilizes a Utility class to handle isolation and accessing the data of the tenant based on their unique configuration. The tenant is identified during authentication, but the tenant data has to be accessible and handled based on the request. This Utility class is registered under ConfigureServices in the Startup.cs and the constructor requires two parameters - a TenantDbContext and a Tenant.
public class TenantUtility{
public TenantUtility(TenantDbContext context, Tenant tenant){/*...*/}
}
Problem
Realizing that I'm probably pushing the limits of the DI Framework, I'm trying to build an implementationFactory in the Startup.cs. I've tried two approaches for accessing the tenant Id to build the Tenant object: one is using a User Claim, the second is a Route parameter.
services.AddTransient<TenantUtility>((svc)=> {
var tenantContext = svc.GetService<TenantDbContext>();
var accessor = svc.GetService<IHttpContextAccessor>();
var httpContext = accessor.HttpContext;
//httpContext is NULL...
//How do I get access to the tenant?
Common.Tenant t = new Common.Tenant();
//Set Tenant Identifier in t (once I get it)
return new StudentDataManager(tenantContext, t);
});
In both situations, when I setup the ImplementationFactory inside the Startup.cs, I don't have access to the user and I don't have access to the RouteData (or can't figure out how to get it) -- I even tried using IHttpContextAccessor, but the HttpContext property is null.
Am I approaching the pattern incorrectly? Should I be able to use DI for this level of detail be injected into the Utility before the Utility class is passed into the Controller?
You shouldn't pass in non-DI-managed types into services you want the container to provide for you (in this case, your Tenant parameter). Only request services that the container can provide. If you need a tenant, perhaps another service like an ITenantAccessor could be injected and would be able to get one.
I suspect your implementation could be done in middleware (if not using MVC) or as a filter (if using MVC). In either case, you can use DI from the filter or middleware class to inject your tenant service. In the class's invoke method, you will have access to the current context and request and should be able to do the things you need to. Remember that ConfigureServices runs before the app has started, so there is no context and no request is yet being made.
If you want to see some examples of filters, and especially how to do DI into filters, check out:
https://github.com/ardalis/GettingStartedWithFilters
and
http://ardalis.com/real-world-aspnet-core-mvc-filters
If you want to do it in middleware, then these might help:
https://github.com/ardalis/NotFoundMiddlewareSample
and
http://ardalis.com/using-custom-middleware-to-record-and-fix-404s-in-aspnet-core-apps
It seems to me that what you are injecting in the TenantUtility as a frist parameter is a valid abstraction (though it's probably better to use some interface), but the other one is a value-object. You don't generally inject value objects as they are not abstraction of some operations that you need to perform, but data. So I would pass the Tenant as a parameter to an operation on the TenantUtility class.
Another thing that I can suggest is to use a full-blown DI container like SimpleInjector (there are many others like Ninject, CastlWindsor, etc.). They are much more advanced as far as I know and can easily be integrated. Many of them have .Net Core integration already.
Is there a possibility to register a service scoped to the Session?
In the documentation I found the possibility to add
Transient: New instance each time the service is requested
Scoped: New instance for each HTTP request
Singleton: One instance as long as the server is up and running
Instance: Basically a singleton but with the instance I create
But I did not find anything to register a service scoped to the Session.
Is there a workaround for this? Or is it just not possible?
It would be possible, of course. But as far as I know it would need some coding to write custom solution. I would start with try to write and integrate custom 'life time manager' (in unity nomenclature), or create custom factory for subject service and register it as singleton.
I have an application in ASP.NET MVC that also have a WCF Service included in the same proyect.
Im using Autofac to manage dependency injection. The problem is that when the application is accessed throught web, I need the dependencies to be instanced per Http request. And when the application is accessed throught WCF, I need the dependencies to be instanced per dependency.
In Castle.Windsor, there is a proyect to manage hybrid lifestyles (in this link).
I need something similar, something like:
builder.Register<UnitOfMeasureService>(x => new UnitOfMeasureService())
.As<IUnitOfMeasureService>().HybridLifetimeInstance();
Are there a workaround to manage the instance lifetime depending on when the application has a HttpContext or not?
Autofac does not have support for custom lifestyle managers.
Autofac lifetimes revolve around scopes, which are nestable and can be optionally tagged with a known ID. That's how instance-per-HTTP-request works: A nested scope "tagged" with a known value ("AutofacWebRequest") is created when a web request comes in. The hierarchy looks like this:
Container (root lifetime scope)
Web Reqeust Scope (tagged "AutofacWebRequest")
Any child scopes you might create in your code
When using InstancePerHttpRequest it's basically the same as InstancePerMatchingLifetimeScope("AutofacWebRequest"). If you resolve the type, it falls back until it finds a scope with that name and then uses the same instance in that tagged scope.
In standard WCF hosting, Autofac resolves everything out of a child scope you can get from the instance context (AutofacInstanceContext.Current.OperationLifetime). You could create a child lifetime scope from that and manually tag it, then resolve your dependencies like this:
var opScope = AutofacInstanceContext.Current.OperationLifetime;
using(var requestScope = opScope.BeginLifetimeScope("AutofacWebRequest"))
{
// Resolve InstancePerHttpRequest items from requestScope
}
However, there's no way to do that automatically and the WCF hosting mechanism isn't currently architected in a way you can "plug in" and do this - if you needed the web request scope automatically created, you'd have to roll your own WCF hosting mechanism based on the Autofac source code.
The other option is to have two different containers - one for your web stuff and one for your WCF stuff - and register the component with a different lifetime in each container.
Beyond that... there's really no way to "switch" lifetimes based on context. A component gets one lifetime declared and has to live with it for that component registry.