-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update context pooling multitenancy sample to more closely approximate a real application #3787
Comments
@amiru3f you probably want to give this doc section a read. To summarize, that sample first registers a singleton pooling DbContextFactory (as is standard), but because of multi-tenancy, a scoped IDbContextFactory is registered as well, which gets injects tenant-specific (and therefore scoped) data into the pooled contexts before handing them over to the user. |
I read this doc Dear roji, But by the corresponding issue I mean that it can be implemented like the following code and injected as Singleton to reduce memory allocation: public class WeatherForecastScopedFactory : IDbContextFactory
{
private readonly IDbContextFactory _pooledFactory;
private readonly int _tenantId;
public WeatherForecastScopedFactory(
IDbContextFactory<WeatherForecastContext> pooledFactory,
IHttpContextAccessor httpContextAccessor)
{
_pooledFactory = pooledFactory;
}
public WeatherForecastContext CreateDbContext()
{
var context = _pooledFactory.CreateDbContext();
context.TenantId = GetTenantId();
return context;
}
private int GetTenantId()
{
// In this sample, we simply accept the tenant ID as a request query, which means that a client can impersonate any tenant.
// In a real application, the tenant ID would be set based on secure authentication data.
var tenantIdString = httpContextAccessor.HttpContext.Request.Query["TenantId"];
if (tenantIdString != StringValues.Empty && int.TryParse(tenantIdString, out var tenantId))
{
return tenantId;
}
return -1; //or throw exception to be caught in another middleware.
}
} |
How would that work, given that the tenant ID is a scoped piece of data? In the sample above, HttpContextAccessor is a scoped service - since each HTTP request has a different scope, with its own tenant ID - and so cannot be injected into a singleton service... |
Dear roji, Thanks for your reply HttpContextAccessor is a singleton service, not scoped. See this We can access scoped httpContext using Singleton HttpContextAccessor. And it would work. |
@davidfowl Is using HttpContextAccessor to get the current request preferred over using a scoped service? |
Assuming this was a singleton service yes. We don't have anything specific to access the http context with a scoped lifetime |
@davidfowl the goal here is simply to get an HTTP header from the current request (containing a tenant ID) from a controller - what's the recommended way to do that? I would have assumed some sort of scoped service would provide access to that (because tied to the current HTTP request) |
HttpContextAccessor ? |
IHttpContextAccessor is the way to do that yes. Ideally though you'd have another service that resolved the current tenant ID so that it's not coupled to the HttpContext, and there would be an Http based tenant resolver that would use the IHttpContextAccessor to grab the header. |
IHttpContextAccessor is what I used in the sample (see discussion above), but I was surprised to learn that it's a singleton service (which would mean that scoped HTTP request information is retrieved via AsyncLocal or some similar mechanism?). Would have expected some scoped DI mechanism for retrieving HTTP request-bound data... |
There is no scoped service for accessing the HTTP request data. |
Hello my friends and Appreciate the time you spent to review my issue. |
To have a proper, scoped ITenant service. Closes dotnet#3787
To have a proper, scoped ITenant service. Closes dotnet#3787
@amiru3f you're right that since HttpContextAccessor is a singleton service which internally uses AsyncLocal to access HTTP header data, it's possible to simply extend EF Core's PooledDbContextFactory, have it accept HttpContextAccessor and inject the tenant ID into contexts it hands out - while being registered as a Singleton itself. However, the point of this code sample is to show how to manage context pooling when Scoped data is involved. Also, in most multi-tenant applications it would be inappropriate for EF's DbContext factory to directly access the HttpContext and perform tenant resolution, since tenant information is also needed elsewhere. So I've submitted #3822 to more closely approximate such a multitenant app, with a Scoped ITenant service. |
To have a proper, scoped ITenant service. Closes dotnet#3787
To have a proper, scoped ITenant service. Closes #3787
Hi,
As you know the DbContext should be injected as Scoped mode. But I guess the WeatherForecastScopedFactory can be injected as singleton, because there is no need to have a scoped factory object per request (per scope). The singleton WeatherForecastScopedFactory can create a scoped DbContext per scope and the corresponding DbContext will be pooled.
Could you please describe that why the "WeatherForecastScopedFactory" service is injected as scoped?
There is the line which scoped injection is occured:
EntityFramework.Docs/samples/core/Performance/AspNetContextPoolingWithState/Program.cs
Line 21 in a230306
The text was updated successfully, but these errors were encountered: