I want to use IServiceScopeFactory
in a way, which is not the Service Locator anti-pattern.
Therefore I thought about making a generic one, which supports explicitly only the given service.
I have made these interfaces:
public interface IServiceScopeFactory<T> where T : class
{
IServiceScope<T> CreateScope();
}
public interface IServiceScope<T> : IDisposable where T : class
{
T Service { get; }
}
And these implementations:
public class ServiceScopeFactory<T> : IServiceScopeFactory<T> where T:class
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public ServiceScopeFactory(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public IServiceScope<T> CreateScope()
{
return new ServiceScope<T>(_serviceScopeFactory.CreateScope());
}
}
public class ServiceScope<T> : IServiceScope<T> where T : class
{
private readonly IServiceScope _scope;
private T _service;
public ServiceScope(IServiceScope scope)
{
_scope = scope;
}
public void Dispose()
{
_scope?.Dispose();
}
public T Service => _service ?? (_service = _scope.ServiceProvider.GetRequiredService<T>());
}
Usage is for example:
public class Foo { private readonly IServiceScopeFactory<ConmaniaDbContext> _dbContextFactory; public Foo(IServiceScopeFactory<ConmaniaDbContext> dbContextFactory) { _dbContextFactory = dbContextFactory; } public void Bar() { using (var scope = _dbContextFactory.CreateScope()) { var dbContext = scope.Service //use service } } }
For registering it, I have made these extension methods:
public static void AddServiceScopeFactory<T>(this IServiceCollection serviceCollection) where T : class
{
serviceCollection.AddTransient<IServiceScopeFactory<T>, ServiceScopeFactory<T>>();
}
The registration is like that:
services.AddScoped<ConmaniaDbContext, ConmaniaDbContext>(); //Scoped Service
services.AddServiceScopeFactory<ConmaniaDbContext>();
services.AddSingleton<Foo, Foo>(); //Singleton Service
My base problem was that I need the Scoped
Service DbContext
in a Singleton
service.
Is this a good way for dependency injection with a custom scope or is this just a wrapper around the Service Locator pattern? Is there anything you would improve? Or any obviously problems I might get with this implementation?
To clarify, this is where I am coming from:
public class Foo { private readonly IServiceScopeFactory _scopeFactory; public Foo(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } public void Bar() { using (var scope = _scopeFactory.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService<ConmaniaDbContext>() //use service } } }
But this feels like the Service Locator Pattern, or am I wrong? The problem is here, that the class constructor doesn't know which dependencies the class needs.
The problem I am facing is that IServiceScopeFactory
feels like the Service Locator Pattern. Therefore I created a IServiceScopeFactory<T>
.
Since the whole Dependency Injection Pattern is relative new to me, I want to know if this is a suitable way of doing it, or some misconception.
Foo
class. TheBar
method would for example query onetime the database to get some informations. @t3chb0t \$\endgroup\$scope
will automatically dispose any services acquired from it, so you don't need to put thedbContext
in ausing
block. \$\endgroup\$ConmaniaDbContext
which is what should have been injected. \$\endgroup\$ConmaniaDbContext
is registeredScoped
while the Service, that needs to use it, is registered asSingleton
. This is the a part that I can not change. ASingleton
can not consumeScoped
orTransient
services. @Nkosi \$\endgroup\$