I would like to create an Enviroment plugin for my ASP.Net 5.0 / MVC 6 application. I use Autofac as an IOC container and I like to load plugins (class libraries) from the assembly in the DNX LibraryManager. The purpose of using the library manager is that I donβt need to worry about NuGet packages and infrastructures.
The problem I have is LifeCycle, I need to create an IOC container before the LibraryManager instance is available. Since Autofac Container provides its own instance of IServiceProvider, which I have to enter inside a call to the ConfigureService () (AddAutofac) method.
Does anyone know how to do this?
Update: I fixed my problem with Davids and updated the code to make it work with release candidates. I also added configuration support.
In my DNX class library, I implemented a self-registration class:
public class AutofacModule : Module { protected override void Load(ContainerBuilder builder) { builder.Register(c => new SimpleService()) .As<IService>() .InstancePerLifetimeScope(); } }
In my MVC WebApplication, I added the class library as dependent.
Startup.cs
public IConfiguration Configuration { get; set; } public class Startup { public Startup( IApplicationEnvironment applicationEnvironment ) { IConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); configurationBuilder.SetBasePath( applicationEnvironment.ApplicationBasePath ); configurationBuilder.AddJsonFile( "appsettings.json" ); configurationBuilder.AddJsonFile( "autofac.json" ); configurationBuilder.AddEnvironmentVariables(); this.Configuration = configurationBuilder.Build(); } public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddDependencies(); } public void Configure(IApplicationBuilder applicationBuilder, IHostingEnvironment hostingEnvironment) { applicationBuilder.UseDependencies( this.Configuration ); applicationBuilder.UseStaticFiles(); applicationBuilder.UseMvc(); } }
I created a DependencyResolver to store an instance of ContainerBuilder.
DependencyResolver.cs
public class DependencyResolver : IDependencyResolver { private IContainer container; private readonly ContainerBuilder builder; public DependencyResolver() { this.builder = new ContainerBuilder(); } public void RegisterModule( IModule module ) { this.builder.RegisterModule( module ); } public void RegisterModules( IEnumerable<Assembly> assemblies ) { this.builder.RegisterAssemblyModules(assemblies.ToArray()); } public void Populate( IServiceCollection services) { this.builder.Populate( services ); } public void Build() { this.container = this.builder.Build(); } public T Resolve<T>() where T : class { return this.container?.Resolve<T>(); } }
IDependencyResolver.cs
public interface IDependencyResolver { void RegisterModule( IModule module ); void RegisterModules( IEnumerable<Assembly> assemblies ); void Populate(IServiceCollection services); void Build(); T Resolve<T>() where T : class; }
Last but not least, I created an extension class
DependencyResolverExtensions.cs
public static class DependencyResolverExtensions { public static IServiceCollection AddDependencies( this IServiceCollection services ) { DependencyResolver dependencyResolver = new DependencyResolver(); dependencyResolver.Populate(services); ServiceDescriptor serviceDescriptor = new ServiceDescriptor(typeof ( IDependencyResolver ), dependencyResolver ); services.TryAdd(serviceDescriptor); return services; } public static IApplicationBuilder UseDependencies(this IApplicationBuilder applicationBuilder, IConfiguration configuration) { IDependencyResolver dependencyResolver = applicationBuilder.GetService<IDependencyResolver>(); if (dependencyResolver == null) return applicationBuilder; ILibraryManager libraryManager = applicationBuilder.GetService<ILibraryManager>(); if (libraryManager == null) return applicationBuilder; IEnumerable<Assembly> assemblies = libraryManager.GetLoadableAssemblies(); dependencyResolver.RegisterModules(assemblies); ConfigurationModule configurationModule = new ConfigurationModule( configuration ); dependencyResolver.RegisterModule( configurationModule ); dependencyResolver.Build(); IServiceProvider serviceProvider = dependencyResolver.Resolve<IServiceProvider>(); applicationBuilder.ApplicationServices = serviceProvider; return applicationBuilder; } public static IEnumerable<Assembly> GetLoadableAssemblies(this ILibraryManager libraryManager) { List<Assembly> result = new List<Assembly>(); IEnumerable<Library> libraries = libraryManager.GetLibraries(); IEnumerable<AssemblyName> assemblyNames = libraries.SelectMany(e => e.Assemblies).Distinct(); assemblyNames = Enumerable.Where(assemblyNames, e => e.Name.StartsWith("MyLib.")); foreach (AssemblyName assemblyName in assemblyNames) { Assembly assembly = Assembly.Load(assemblyName); result.Add(assembly); } return result; } public static T GetService<T>(this IApplicationBuilder applicationBuilder) where T : class { return applicationBuilder.ApplicationServices.GetService(typeof (T)) as T; } }
If you need to switch between different implementations, such as layout and real data, you can use the Autofac configuration.
autofac.json
{ "components": [ { "type": "MyLib.Data.EF.EntitiesData, MyLib.Data.EF", "services": [ { "type": "MyLib.Abstractions.IDataRepository, MyLib.Abstractions" } ] } ] }