Asp.net Vnext 模块化实现

概述


 本文已经同步到《Asp.net Vnext 系列教程 》中]

在程序中实现模块化可以加快开发效率,通过替换模块实现升级.

架构


vnext 没有 Virtualpathprovider,本文通过IFileProvider实现模块

ModularVNext.Startup 启动类

   public class Startup
    {
        private IFileProvider _modulesFileProvider;
        private readonly string ApplicationBasePath;
        private readonly IAssemblyLoadContextAccessor _assemblyLoadContextAccessor;
        private readonly IAssemblyLoaderContainer _assemblyLoaderContainer;
        public Startup(IHostingEnvironment hostingEnvironment,
            IApplicationEnvironment applicationEnvironment,
            IAssemblyLoaderContainer assemblyLoaderContainer,
            IAssemblyLoadContextAccessor assemblyLoadContextAccessor)
            {
            //程序集加载上下文访问
            _assemblyLoadContextAccessor = assemblyLoadContextAccessor;
            //程序集加载容器
            _assemblyLoaderContainer = assemblyLoaderContainer;
            //程序基本路径
            ApplicationBasePath = applicationEnvironment.ApplicationBasePath;
            Configuration = new Configuration()
                .AddJsonFile("config.json")
                .AddEnvironmentVariables();
            }

        public IConfiguration Configuration { get; set; }

        public void ConfigureServices(IServiceCollection services)
        {
            var basePaths = Configuration.Get("additionalFileProviderBasePaths")?.Split(';') ?? new string[] { };
            var modulesPath = Path.Combine(ApplicationBasePath.Substring(0,ApplicationBasePath.IndexOf("src")), Configuration.Get("moduleLoadPath"));

            var moduleAssemblies = LoadAssembliesFrom(modulesPath, _assemblyLoaderContainer, _assemblyLoadContextAccessor);

            _modulesFileProvider = GetModulesFileProvider(basePaths, moduleAssemblies);


            services.AddInstance(Configuration);

            services.AddMvc();

            services.Configure<RazorViewEngineOptions>(o =>
            {
                o.FileProvider = _modulesFileProvider;
            });

            services.AddInstance(new ModuleAssemblyLocator(moduleAssemblies));
            services.AddTransient<DefaultAssemblyProvider>();
            services.AddTransient<IAssemblyProvider, ModuleAwareAssemblyProvider>();
            }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory)
        {
            loggerfactory.AddConsole();

            if (string.Equals(env.EnvironmentName, "Development", StringComparison.OrdinalIgnoreCase))
                {
                app.UseBrowserLink();
                app.UseErrorPage(ErrorPageOptions.ShowAll);
                }
            else
                {
                app.UseErrorHandler("/Home/Error");
                }

            app.UseStaticFiles(new StaticFileOptions
                {
                FileProvider = _modulesFileProvider
                });

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "areaRoute",
                    template: "{area:exists}/{controller}/{action}",
                    defaults: new { controller = "Home", action = "Index" }
                    );
                routes.MapRoute(
                    name: "default",
                    template: "{controller}/{action}/{id?}",
                    defaults: new { controller = "Home", action = "Index" });
            });
            }

        private List<Assembly> LoadAssembliesFrom(string modulesDirectory,
          IAssemblyLoaderContainer assemblyLoaderContainer,
          IAssemblyLoadContextAccessor loadContextAccessor)
            {
            var assemblies = new List<Assembly>();
            var loadContext = _assemblyLoadContextAccessor.GetLoadContext(typeof(Startup).GetTypeInfo().Assembly);
            using (assemblyLoaderContainer.AddLoader(new DirectoryLoader(modulesDirectory, loadContext)))
                {

        
                foreach (var modulePath in Directory.EnumerateFiles(@"C:\Users\Administrator\Documents\Visual Studio 2015\Projects1\ModularVNext\artifacts\bin\Module1\Debug\dnx451", "*.dll"))
                    {
                    var name = Path.GetFileNameWithoutExtension(modulePath);
                    assemblies.Add(loadContext.Load(name));
                    }
                }
            return assemblies;
            }



        private IFileProvider GetModulesFileProvider(string[] basePaths, List<Assembly> moduleAssemblies)
            {
       
            var redirectedFileProviders = basePaths
                .Select(path => Path.IsPathRooted(path) ? path : Path.Combine(ApplicationBasePath, path))
                .Select(root => new PhysicalFileProvider(root)).ToList();
          
     
         
            var resourceFileProviders = moduleAssemblies.Select(a => new SafeEmbeddedFileProvider(a)).ToList();
             
         var redirectedFileProviders1=  redirectedFileProviders.Select(x => x as IFileProvider);
            var resourceFileProviders1=   resourceFileProviders.Select(x => x as IFileProvider);
          var co=  redirectedFileProviders1.Concat<IFileProvider>(resourceFileProviders1).ToList();
           

            IFileProvider rootProvider = new PhysicalFileProvider(ApplicationBasePath);

            co.Add(rootProvider);
            return new CompositeFileProvider(co);

           
            }

        }

ConfigureServices 方法主要是获取模块程序集和模块程序提供者

 var moduleAssemblies = LoadAssembliesFrom(modulesPath, _assemblyLoaderContainer, _assemblyLoadContextAccessor);

通过LoadAssembliesFrom方法获取模块程序集 

参数:

modulesPath 模块的路径

也就是 Module1的程序集路径

选择该选项,然后编译就可以输出程序集了

在根目录下\artifacts\bin\Module1\Debug\会产生两个程序集分别是 dnx451 和 dnxcore50 ,这里我选择的是dnx451

集体实现

  private List<Assembly> LoadAssembliesFrom(string modulesDirectory,
          IAssemblyLoaderContainer assemblyLoaderContainer,
          IAssemblyLoadContextAccessor loadContextAccessor)
            {
            var assemblies = new List<Assembly>();

            //程序集加载上下文
            var loadContext = _assemblyLoadContextAccessor.GetLoadContext(typeof(Startup).GetTypeInfo().Assembly);

              //添加程序集加载
            using (assemblyLoaderContainer.AddLoader(new DirectoryLoader(modulesDirectory, loadContext)))
                {

        
                //找出文件夹下的程序集,我这里写的是硬编码
                foreach (var modulePath in Directory.EnumerateFiles(@"C:\Users\Administrator\Documents\Visual Studio 2015\Projects1\ModularVNext\artifacts\bin\Module1\Debug\dnx451", "*.dll"))
                    {
                    var name = Path.GetFileNameWithoutExtension(modulePath);
                    //加载程序集
                    assemblies.Add(loadContext.Load(name));
                    }
                }
            return assemblies;
            }

返回程序集在条用_modulesFileProvider 获取文件提供者然后把获取到的文件提供者设置到RazorViewEngineOptions

            services.Configure<RazorViewEngineOptions>(o =>
            {
                o.FileProvider = _modulesFileProvider;
            });

ModularVNext.Infrastructure.CompositeFileProvider这是实现IFileProvider的类

   public class CompositeFileProvider : IFileProvider
    {
        //提供者目录
        private List<IFileProvider> _fileProviders;
        //触发器
        private readonly Dictionary<string, TestFileTrigger> _fileTriggers =
            new Dictionary<string, TestFileTrigger>(StringComparer.Ordinal);
        public CompositeFileProvider(IEnumerable<IFileProvider> fileProviders)
        {
            _fileProviders = fileProviders.ToList();
        }

        public IDirectoryContents GetDirectoryContents(string subpath)
        {
            foreach (var fileProvider in _fileProviders)
            {
                //更具subpat获取目录
                var contents = fileProvider.GetDirectoryContents(subpath);
                if (contents != null && contents.Exists)
                {
                    return contents;
                }
            }
            return new NotFoundDirectoryContents();
        }

        public IFileInfo GetFileInfo(string subpath)
        {
            foreach (var fileProvider in _fileProviders)
                {//更具subpat获取文件信息
                var fileInfo = fileProvider.GetFileInfo(subpath);
                if (fileInfo != null && fileInfo.Exists)
                {
                    return fileInfo;
                }
            }
            return new NotFoundFileInfo(subpath);
        }

        public IExpirationTrigger Watch(string filter)
        {

            //触发器
           TestFileTrigger trigger;
        
            
            trigger = new TestFileTrigger();
        
            return trigger;
   
            }
    }


    internal class TestFileTrigger : IExpirationTrigger
        {
        public bool ActiveExpirationCallbacks
            {
            get { return false; }
            }
        private CancellationToken Token { get; set; }
        public bool IsExpired
            {
            get {

                return false; }
            }

        public IDisposable RegisterExpirationCallback(Action<object> callback, object state)
            {
            return null;
            }
        }

没有对Module1引用

运行程序

 http://pan.baidu.com/s/1eQdakoy 下载地址

posted @ 2015-07-03 17:14  欢呼雀跃  阅读(970)  评论(0编辑  收藏  举报