Parts of my code need to use a ServiceLocator because constructor injection isn't supported.
My startup class configures the services. I have some that are transient, others which are singleton and others scoped.
For example:
services.AddScoped<IAppSession, AppSession>();
services.AddScoped<IAuthentication, Authentication>();
services.AddScoped<NotificationActionFilter>();
At the end of my service definitions, I have the following block of code, which sets up the service locator.
var serviceProvider = services.BuildServiceProvider();
DependencyResolver.Current = new DependencyResolver();
DependencyResolver.Current.ResolverFunc = (type) =>
{
return serviceProvider.GetService(type);
};
I noticed that in a given request, I am not receiving the same instance from the service locator that I am from the constructor injection. Instances returned from the service locator appear to be singletons and do not respect the scoping.
The code for DependencyResolver
is as follows:
public class DependencyResolver
{
public static DependencyResolver Current { get; set; }
public Func<Type, object> ResolverFunc { get; set; }
public T GetService<T>()
{
return (T)ResolverFunc(typeof(T));
}
}
How can I fix this?
I would suggest creating a middleware which will set ServiceProvider to the one which is used in other places:
public class DependencyResolverMiddleware
{
private readonly RequestDelegate _next;
public DependencyResolverMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
DependencyResolver.Current.ResolverFunc = (type) =>
{
return httpContext.RequestServices.GetService(type);
};
await _next(httpContext);
}
}
Also, DependencyResolver
should be updated to support such behavior:
public class DependencyResolver
{
private static readonly AsyncLocal<Func<Type, object>> _resolverFunc = new AsyncLocal<Func<Type, object>>();
public static DependencyResolver Current { get; set; }
public Func<Type, object> ResolverFunc
{
get => _resolverFunc.Value;
set => _resolverFunc.Value = value;
}
public T GetService<T>()
{
return (T)ResolverFunc(typeof(T));
}
}
Don't forget to register it in Configure
method in Startup.cs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseMiddleware<DependencyResolverMiddleware>();
}