1

We are currently doing sitecore upgrade and but there are something broken with autofac DI integration with sitecore. we created our own serviceProviderBuilder which extends Sitecore.DependencyInjection.BaseServiceProviderBuilder, we then patch it through sitecore config, but it throws runtime errors

[LockRecursionException: Recursive read lock acquisitions not allowed in this mode.]

Our custom service provider builder as shown in below

public class AutofacServiceProviderBuilder : Sitecore.DependencyInjection.BaseServiceProviderBuilder
    {
        protected override IServiceProvider BuildServiceProvider(IServiceCollection serviceCollection)
        {
            var builder = new ContainerBuilder();

            builder.Populate(serviceCollection);

            // Register All Controllers In The Current Scope
            builder.RegisterControllers(AppDomain.CurrentDomain.GetAssemblies());
            builder.RegisterApiControllers(AppDomain.CurrentDomain.GetAssemblies());

            // Register Modules
            builder.RegisterType<MemberOnlineMediatorService>().As<IMemberOnlineMediator>();
            builder.RegisterType<InteractionMaintenanceService>().As<IInteractionMaintenanceService>();
            builder.RegisterType<MemberStatementsService>().As<IMemberStatementsService>();
            builder.RegisterType<DbHealthCheckService>().As<IDbHealthCheckService>();

            // Register all Autofac Modules from assemblies
            builder.RegisterAssemblyModules(AppDomainScanner.AllAssemblies);
            
            builder.RegisterFilterProvider();

            // Register AutoMapper
            AutoMapperConfiguration(builder);

            // Register Additional Dependencies
            RegisterAbstractValidators(builder);

            // Build container
            var container = builder.Build();
            return new AutofacServiceProvider(container);
        }
    }
6
  • please, provide your version of BaseServiceProviderBuilder Commented Feb 27, 2023 at 7:37
  • it is in sitecore.kernel.dll (10.3)
    – Ting Wang
    Commented Feb 27, 2023 at 22:11
  • also tried this autofacserviceproviderbuilder with clean sitecore vanilla instance, it throws the same error.
    – Ting Wang
    Commented Feb 28, 2023 at 2:51
  • did you check showconfig on your environment? Just to be sure that you don't have 2 processors that registor DI Commented Feb 28, 2023 at 8:50
  • unable to load sitecore login screen at all due to that exception. I tried to patch on sitecore vanilla instance, same error.
    – Ting Wang
    Commented Mar 1, 2023 at 0:59

2 Answers 2

0

I am facing the same issue while upgrading from Sitecore 8 to Sitecore 10.3.1 or 10.4. I still have an open Sitecore support case to eventually get a better fix, but here is the workaround I am using so far:

  1. I continue to patch the serviceProviderBuilder:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <serviceProviderBuilder>
      <patch:attribute name="type" value="Custom.Web.AutofacServiceProviderBuilder, Custom.Web" />
    </serviceProviderBuilder>
  </sitecore>
</configuration>
  1. I limit the implementation to the same code Sitecore has, but first I additionally put the serviceCollection from Sitecore in Application state:
public class AutofacServiceProviderBuilder : BaseServiceProviderBuilder
{
    protected override IServiceProvider BuildServiceProvider(IServiceCollection serviceCollection)
    {
        //Uncomment this line if you want to debug from here
        //System.Diagnostics.Debugger.Launch();
        HttpContext.Current.Application.Add("SitecoreServiceCollectionWorkaround", serviceCollection);
        return serviceCollection.BuildServiceProvider();
  1. I extend the Global.asax and I override Application_Start:
public class Global : Sitecore.Web.Application
public override void Application_Start(object sender, EventArgs e)
  1. Normally you would call the base class, but I comment that (it looks safe to do cause the only thing Sitecore does is set the ServiceLocatorProvider read only):
//WE DON'T DO THIS AS A WORKAROUND BECAUSE WE DON'T WANT THE SERVICELOCATOR TO BE READONLY!
//base.Application_Start(sender, e);
  1. I build up my autofac container here and I register the Sitecore collection as well:
if(HttpContext.Current.Application["SitecoreServiceCollectionWorkaround"] != null)
    builder.Populate((IServiceCollection)HttpContext.Current.Application["SitecoreServiceCollectionWorkaround"]);
  1. I set the ServiceLocator here:
//WORKAROUND: As we can't set Autofac in AutofacServiceProviderBuilder anymore, we do it here
Sitecore.DependencyInjection.ServiceLocator.Reset();
Sitecore.DependencyInjection.ServiceLocator.SetServiceProvider(new AutofacServiceLocator(container));

So, during startup Sitecore starts with its own DI, which during Application_Start I quickly override with my an Autofac ServiceProviderBuilder.

0

Update: an answer was received from Sitecore Support and I have chosen to take this approach (so far no issue):

Autofac is third-party software that is not included in Sitecore out of the box. Therefore, we do not have an official guide on its compatibility, as it has not been tested by Sitecore. In addition, we have discussed this behavior with our Product team and received feedback that we don't support custom DI containers with Microsoft.Extensions.DependencyInjection since "https://github.com/aspnet/DependencyInjection" repository was archived in 2018.

Nevertheless, I spent some time investigating this behavior and found that there is a difference between how Autofac and MSDI handle registrations that use factories. When Autofac tries to resolve a class and the method resolving it returns null, Autofac throws an error. The service that caused the problem you faced was the HttpContextBase class, which is registered in the DefaultSitecoreServicesConfigurator. It returns null when the HttpContext.Current is null. In order to resolve the issue, we need to remove the default implementation and add the custom one, which doesn't return null when the HttpContext.Current is null.

In your BuildServiceProvider method add these two lines as the first things to be executed:

var serviceDescriptor = serviceCollection.FirstOrDefault(descriptor => descriptor.ServiceType == typeof(HttpContextBase));
serviceCollection.Remove(serviceDescriptor);

After line builder.Populate(serviceCollection); add the following code:

builder.Register<HttpContextBase>(_ =>
            {
                if (HttpContext.Current == null)
                {
                    return new HttpContextWrapper(new HttpContext(new HttpRequest("", "https://www.sitecore.com/", ""), new HttpResponse(new MockTextWriter())));
                }

                return new HttpContextWrapper(HttpContext.Current);
            });

In your project add this class:

public class MockTextWriter : TextWriter
    {
        public MockTextWriter()
        {
        }

        public override Encoding Encoding => Encoding.UTF8;
    }

However, please note that this approach has not been fully tested, and we are not aware of any potential issues it may cause. We also cannot provide guidance on how to configure Autofac correctly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.