We have a dnx46 web project that is using autofac assembly scanning during startup to register types. Our project.json dependencies include:
"Autofac.Configuration": "4.0.0-rc1-268",
"Autofac.Extensions.DependencyInjection": "4.0.0-rc1-177",
"Autofac.Extras.CommonServiceLocator": "3.2.0",
"Microsoft.AspNet.Hosting": "1.0.0-rc1-final",
"Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
"Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
"Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-final",
"Microsoft.AspNet.Mvc.ViewFeatures": "6.0.0-rc1-final",
"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
"Microsoft.AspNet.Session": "1.0.0-rc1-final",
"Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final",
"Microsoft.AspNet.Tooling.Razor": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging.Debug": "1.0.0-rc1-final",
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-rc1-final",
"Newtonsoft.Json": "8.0.3"
Our startup.cs ConfigureServices method looks like this:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddSingleton(serviceType => Configuration);
services.AddInstance<Microsoft.Extensions.Configuration.IConfiguration>(Configuration);
services.AddCaching();
services.AddSession();
services.AddMvc();
var builder = new ContainerBuilder();
var assemblies = Directory.GetFiles(<ourBinDirectoryPath>, "*.dll", SearchOption.TopDirectoryOnly).Select(Assembly.LoadFrom);
foreach (var assembly in assemblies)
{
builder.RegisterAssemblyTypes(assembly).Where(t => t.Name.EndsWith("Service") || t.Name.EndsWith("Repository") || t.Name.EndsWith("DataContext")).AsSelf().AsImplementedInterfaces();
builder.RegisterAssemblyTypes(assembly).Where(t => !t.Name.EndsWith("Service") && !t.Name.EndsWith("Repository") && !t.Name.EndsWith("DataContext")).AsSelf().AsImplementedInterfaces().InstancePerDependency();
}
builder.Populate(services);
var container = builder.Build();
return container.Resolve<IServiceProvider>();
}
Our process is that we have several projects that we build that drop dlls that this project will need into a custom bin folder. On startup, we scan those assemblies to register the types with Autofac. We did this in a proof of concept project, and it worked without issues. In our new project though, we have the following issues:
After trying many things, I took our proof of concept project and copied it over to our new solution (in case we missed some small setting or something when setting up our new project), and the bad behavior continued. I am starting to think that our assemblies are introducing some bug (since those are the only things that have changed after the copy), but Autofac does not throw an error during scanning, and instead just doesn't use the types it scanned in correctly.
Edit 1
I have created and posted a very simplified example that reproduces the issue I am running into. You should be able to pull that down and run it to reproduce the error. You should get an error in the home controller constructor indicating service resolution failed.
Note: when running this example, it executes a .bat file that puts output dll from the domain project in your user profile .nuget folder.
Edit 2
Split interface into abstraction folder based on recommendation from @Travis Illig. Once I did, solution started working. We have it split in our "real" project, but it is not working. Still experimenting to figure out why.
Edit 3
So finally got our project working again. Issue ended up being several things:
This took me way longer to figure out, and my biggest issue at this point is that Autofac would fail silently. I really wish it would blow up and give me some debug output or something indicating there was an issue. Instead, everything appeared to scan and load fine, but then when you went to use the container, nothing would resolve.
Chances are the assemblies are being loaded in such an order where some types can't be loaded due to prerequisite assemblies not being loaded.
For example, say you have assembly A that contains interface IComponent
. Also say you have assembly B that contains a class Component
that implements IComponent
. If you load assembly B first, the type Component
won't be loadable because the interface it implements isn't defined.
When doing type scanning, Autofac tries to be as safe as possible and won't explode if out runs into types that can't be loaded. You can see in the scanning registration source where it uses a special safe method to do loading.
In your spike code, I'm betting there were far fewer assemblies to load so you ended up loading things in the right order. Happy accident. When you get into larger projects, it's much easier to fall to control the load order and cause issues like this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With