2015-07-28 57 views
4

我想为我的ASP.Net 5.0/MVC 6应用程序创建一个插件环境。我使用Autofac作为IOC容器,并且我喜欢从DNX LibraryManager中的构建中加载插件(类库)。使用库管理器的目标是,我不必关心NuGet包和框架。自动注册库Autofac 4和vNext

我遇到的问题是LifeCycle,我必须在库管理器的实例可用之前构建IOC容器。因为Autofac容器提供了他自己的IServiceProvider实例,我必须在ConfigureService()方法调用(AddAutofac)中注入它。

有谁知道如何得到这个工作?

更新:我已经解决了我与戴维斯帮助的问题,并更新了代码以使其与候选版本一起工作。另外我还添加了对配置的支持。

在我的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

我一直在寻找解决这个问题的方法,我认为你在这里有一个好主意,但根据AutoFac文档(http://docs.autofac.org/en/latest/resolve/),您的Resolve方法可能会导致内存泄漏。我已经构建了一个ConfigurationContainer,我认为它解决了这个问题,并且将与上面的一些解决方案一起实现。如果你想看到它或者其他地方,如果它会更好,我可以发布代码作为答案。 – DrewB

+0

我认为代码不再工作,因为微软一遍又一遍地改变着一切。但是,是的,如果你可以改进代码,那将是非常好的。我还花了几个小时的时间通过使用内部依赖解析器和多个启动文件来创建没有autofac的版本。但是现在我已经停止了与vnext的合作,因为没有机会获得稳定的版本。 – endeffects

回答

1

我想出了一个使用其中的一部分,也是一个解决方案使用,在解决潜在的内存泄漏ComponentContainer依赖关系解析器。这也适用于RC1。还不确定关于RC2,因为它对我来说还不够完整。

的ComponentContainer看起来是这样的:

public static class ComponentContainer { 
    static IContainer _container; 
    static ContainerBuilder _containerBuilder; 

    public static ContainerBuilder Builder { 
     get { 
      if (_containerBuilder == null) 
       _containerBuilder = new ContainerBuilder(); 
      return _containerBuilder; 
     } 
    } 

    public static IServiceProvider ServiceProvider { 
     get { 
      if (_container == null) 
       _container = _containerBuilder.Build(); 
      return _container.Resolve<IServiceProvider>(); 
     } 
    } 

    public static ComponentFactory<TObject> Component<TObject>() => new ComponentFactory<TObject>(_container); 

    public static void RegisterAssembly(Assembly assembly) { 
     if (assembly == null) return; 

     foreach (var obj in assembly.GetTypes().Where(t => t.GetCustomAttribute<ExportAttribute>() != null)) { 
      ExportAttribute att = obj.GetCustomAttribute<ExportAttribute>(); 
      if (att.ContractType != null) { 
       _containerBuilder.RegisterType(obj).As(att.ContractType); 
      } else { 
       foreach (var intf in obj.GetInterfaces()) 
        _containerBuilder.RegisterType(obj).As(intf); 
      } 
     } 
    } 
} 

public class ComponentFactory<TObject> : IDisposable { 
    protected TObject CurrentObject; 
    protected ILifetimeScope CurrentScope; 
    public TObject Current => (TObject)CurrentObject; 
    public ComponentFactory(IContainer container) { 
     CurrentScope = container.BeginLifetimeScope(); 
     CurrentObject = CurrentScope.Resolve<TObject>(); 
    } 

    public TObject Component => CurrentObject; 

    public void Dispose() { 
     (CurrentObject as IDisposable)?.Dispose(); 
     CurrentScope.Dispose(); 
    } 
} 

然后在Startup.cs我做了以下内容:

public virtual IServiceProvider ConfigureServices(IServiceCollection services) { 
     services.AddMvc(); 
     services.AddOptions(); 
     services.AddSession(); 
     services.AddCaching(); 

     var assemblyLoadContextAccessor = services.FirstOrDefault(s => s.ServiceType == typeof(IAssemblyLoadContextAccessor)).ImplementationInstance as IAssemblyLoadContextAccessor; 
     var libraryManager = services.FirstOrDefault(s => s.ServiceType == typeof(ILibraryManager)).ImplementationInstance as ILibraryManager; 

     var loadContext = assemblyLoadContextAccessor.Default; 

     foreach(var library in libraryManager.GetLibraries()) { 
      var assembly = loadContext.Load(library.Name); 

      if(assembly != null) { 
       var module = assembly.GetTypes().FirstOrDefault(t => t == typeof(IModule)); 

       if(module != null) 
        ComponentContainer.Builder.RegisterAssemblyModules(assembly); 
       else 
        ComponentContainer.RegisterAssembly(assembly);       
      } 
     } 
     ComponentContainer.Builder.Populate(services); 

     return ComponentContainer.ServiceProvider; 
    } 

到装配中导出的模块,我将其标记为与ExportAttribute或添加一个实现Autofac的IModule的程序集的类。 ConfigureServices中的代码将通过应用程序的模块进行枚举并将它们提供给ComponentContainer中的静态生成器。一旦容器已经建成,既可以解决通过注射模块构造也可以通过请求特定类型:

(using var myComponentFactory = ComponentContainer.Component<IMyModule>()) { 
    //You can now access your component through myComponentFactory.Component 
    //Once it passes out of scope of using, it will be properly disposed of 
    //along with the scope from which it was created. 
} 

编辑:随着RC2的发布,这个代码是不再有效程序集和类的枚举将失败。我还没有提出一个好的解决方案。如果其他人有任何关于在RC2中列举程序集的建议,请让我知道。

3

这是一个耻辱,ConfigureServices不注射,这将使这个方便很多。

看着代码,你应该可以安全地更换Configure(...)里面的IServiceProvider而不是ConfigureServices(...),并获得预期的行为。 ApplicationServices is setable

在你UseAutofac方法,你应该能够做这样的事情:

public static IApplicationBuilder UseAutofac([NotNull] this IApplicationBuilder applicationBuilder) 
{ 
    IAutofacResolver autofacResolver = applicationBuilder.GetService<IAutofacResolver>(); 
    ILibraryManager libraryManager = applicationBuilder.GetService<ILibraryManager>(); 

    autofacResolver.RegisterLibraryModules(libraryManager); 
    applicationBuilder.ApplicationServices = autofacResolver.Resolve(); 

    return applicationBuilder; 
} 
+0

哇非常感谢,这是一个简单的解决方案,它的工作原理! – endeffects

+0

有趣的是'ILibraryManager'是实例化的,但是应用程序托管方式的方式,它在ConfigureServices内部不可用。希望它运作良好。 –

+0

嗨@endeffects,不错的工作!您能否请您发布最终解决方案?我没有得到大卫的回答。非常感谢! – stevo