Sitecore 8.2u5
Primary Question
Why does Optimizing Code cause Dependency Injection to fail?
We've used DI on many projects at this point and just had an issue with a recent one. The error presented itself like this:
We quickly realized this is how the site would respond if DI wasn't working properly, as the controllers truly do not have a default constructor. After comparing past projects to this one, we found the following:
- The app pools' where the problem manifested were originally running with
32-bit Mode = true
- We toggled this to
false
in order for the iFilter to be read correctly - (see this question) - This immediately caused the YSOD above
We tracked the issue down to the “Optimize Code” checkbox in our primary web project (where DI is wired up):
Workaround
By unchecking this box and rerunning a build, everything works fine in 64-bit mode. I verified in past projects that we have this unchecked as well.
I reviewed the two different DLLs using JustAssembly, but other than seeing the wildly different code (optimized vs non-optimized) nothing stood out.
I'm comfortable leaving this option unchecked, but obviously, it's there for a reason- to make the code more efficient. I was surprised to learn no one else has documented this issue.
DI is wired up as follows:
Configuration
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/" >
<sitecore>
<services>
<configurator type="MyProject.Web.ServicesConfigurator, MyProject.Web" />
</services>
</sitecore>
</configuration>
Wired up using Kamsars method for adding to all controllers:
namespace MyProject.Web
{
public class ServicesConfigurator : IServicesConfigurator
{
public void Configure(IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<ISitecoreContext>(provider => new SitecoreContext());
serviceCollection.AddTransient<ISitecoreService>(provider => new SitecoreService(Sitecore.Context.ContentDatabase));
serviceCollection.AddTransient<IGlassHtml, GlassHtml>();
serviceCollection.AddTransient<ISitecoreData, SitecoreData>();
serviceCollection.AddTransient<IItemService, ItemService>();
serviceCollection.AddScoped<IGlobalItems, GlobalItems>();
serviceCollection.AddScoped<ILinkService, LinkService>();
serviceCollection.AddScoped<ISearchService>(provider => new SearchService(Settings.GetSetting("Search.Index")));
serviceCollection.AddSingleton<IPardotService, PardotService>();
serviceCollection.AddMvcControllersInCurrentAssembly();
}
}
}
Again, this DI technique is working on multiple projects, but with the Opimize Code
checkbox unchecked.
Why am I unable to run our application with optimized code?
AddMvcControllers
method exists in a separate project/DLL and works properly when optimized as it has theNoInlining
decoration. I debugged locally and strangely when my Web project is optimized, the DLL that is passed to the AddMvcControllers method isSitecore.Kernel
which correctly finds zero Controllers to register. When the Web project is not optimized, the DLL passed to the method is the correct Web project, and therefore it finds Controllers to register.NoInlining
decoration to the ServicesConfigurator, but that feels a bit odd as well. The ServicesConfigurator class is unchanged between optimized/non-optimized. Will continue to look a bit further for the real answer.