Саморегистрация библиотек с помощью Autofac 4 и vNext
Я хотел бы создать среду плагинов для моего приложения ASP.Net 5.0 / MVC 6. Я использую Autofac в качестве контейнера IOC, и мне нравится загружать плагины (библиотеки классов) из сборки в DNX LibraryManager. Цель использования диспетчера библиотек заключается в том, что мне не нужно заботиться о пакетах NuGet и фреймворках.
У меня проблема с LifeCycle, мне нужно собрать контейнер IOC до того, как экземпляр LibraryManager станет доступен. Потому что контейнер Autofac предоставляет свой собственный экземпляр IServiceProvider, который я должен добавить в вызов метода ConfigureService () (AddAutofac).
Кто-нибудь знает, как заставить это работать?
Обновление: я исправил свою проблему с помощью Davids и обновил код, чтобы он работал с кандидатами на выпуск. Также я добавил поддержку конфигурации.
В моей библиотеке классов DNX я реализовал класс для самостоятельной регистрации:
public class AutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new SimpleService())
.As<IService>()
.InstancePerLifetimeScope();
}
}
В моем приложении MVC WebApplication я добавил библиотеку классов как зависимость.
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();
}
}
Я создал DependencyResolver, чтобы сохранить экземпляр 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;
}
И последнее, но не менее важное: я создал класс расширения
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;
}
}
Если вам нужно переключаться между различными реализациями, такими как фиктивные и реальные данные, вы можете использовать конфигурацию Autofac.
autofac.json
{
"components": [
{
"type": "MyLib.Data.EF.EntitiesData, MyLib.Data.EF",
"services": [
{
"type": "MyLib.Abstractions.IDataRepository, MyLib.Abstractions"
}
]
}
]
}