Саморегистрация библиотек с помощью 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"
                }
            ]
        }
    ]
}

Ответы на вопрос(0)

Ваш ответ на вопрос