Lifetime domain with autofac, webforms and ServiceLocator

In a legacy ASP.NET ASP.NET application, I am trying to introduce multiple IOCs.

I will not go into details, but for this I think ServiceLocator is a good tool to work with. I know that a service locator is an anti-pattern; o)

According to the documentation https://github.com/autofac/Autofac/wiki/Common-Service-Locator , the ServiceLocator configuration looks like this:

var container = builder.Build(); var csl = new AutofacServiceLocator(container); ServiceLocator.SetLocatorProvider(() => csl); 

The problem is that LoacatorProvider is installed with the root container. Therefore, life cycle management is inactive.

I am thinking about how to solve this problem.

Since ServiceLocator.SetLocatorProvider accepts a delegate as a parameter, and because this delegate is called every time ServiceLocator.Current called, why not provide an AutofacServiceLocator instance for the request.

 public class Global : HttpApplication, IContainerProviderAccessor { // Provider that holds the application container. static IContainerProvider _containerProvider; void Application_Start(object sender, EventArgs e) { // Code that runs on application startup RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); _containerProvider = new ContainerProvider(AutoFacBootstrapper.CreateContainer()); ServiceLocator.SetLocatorProvider(() => { AutofacServiceLocator asl = HttpContext.Current.Items["AutofacServiceLocator"] as AutofacServiceLocator; if (asl == null) { var cpa = (IContainerProviderAccessor)HttpContext.Current.ApplicationInstance; var cp = cpa.ContainerProvider.RequestLifetime; asl = new AutofacServiceLocator(cp); HttpContext.Current.Items["AutofacServiceLocator"] = asl; } return asl; }); } public IContainerProvider ContainerProvider { get { return _containerProvider; } } } 

This code works. IDisposable objects are located at the end of the request. InstancePerRequest configuration is fine.

Are there any problems (executions, memory, ...) with this implementation?

+6
source share
1 answer

If this helps someone else, here's how I implemented it as a request handler.

Here is my Startup.cs:

 public class Startup { public void Configuration(IAppBuilder app) { var config = new HttpConfiguration(); // ... IContainer container = ConfigureAutofac(config); // This should be the first middleware added to the IAppBuilder. app.UseAutofacMiddleware(container); // Make sure the Autofac lifetime scope is passed to Web API. app.UseAutofacWebApi(config); app.UseWebApi(config); config.MessageHandlers.Add(new ServiceLocatorHandler()); } } 

Below I will show ServiceLocatorHandler.cs .

Here is my ConfigureAutofac function for completeness:

  private IContainer ConfigureAutofac(HttpConfiguration config) { var builder = new ContainerBuilder(); var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>(); builder.RegisterAssemblyModules(assemblies.ToArray()); // Register Web API classes builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); builder.RegisterWebApiFilterProvider(config); builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration); // Register the modules // ... // Create the container var container = builder.Build(); // Hook up the Web API dependency resolver var resolver = new AutofacWebApiDependencyResolver(container); config.DependencyResolver = resolver; // ... ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container)); return container; } 

Finally, here is the handler:

 public class ServiceLocatorHandler : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { var properties = request.Properties; var scope = properties["MS_DependencyScope"]; AutofacWebApiDependencyScope dependency_scope = scope as AutofacWebApiDependencyScope; if (dependency_scope != null && dependency_scope.LifetimeScope != null) { AutofacWebApiDependencyResolver resolver = new AutofacWebApiDependencyResolver(dependency_scope.LifetimeScope); AutofacServiceLocator asl = new AutofacServiceLocator(resolver.Container); ServiceLocator.SetLocatorProvider(() => asl); } return base.SendAsync(request, cancellationToken); } } 

In my use case, I have a third-party library that uses IoC to resolve DBContext implementations through the repository template.

This library is used in other environments using different DI containers, and when using it with the default AutofacServiceLocator root AutofacServiceLocator I would either see EF caching problems with old data, or record failure, because new contexts would be created between adding an object and committing.

Many thanks to OP for helping me solve this.

0
source

Source: https://habr.com/ru/post/972970/


All Articles