当前位置: 首页 > news >正文

山东机关建设网站东莞商城网站建设价格

山东机关建设网站,东莞商城网站建设价格,苏州网站建设苏州,东莞seo网站推广ZKWeb网站框架是一个自主开发的网页框架#xff0c;实现了动态插件和自动编译功能。ZKWeb把一个文件夹当成是一个插件#xff0c;无需使用csproj或xproj等形式的项目文件管理#xff0c;并且支持修改插件代码后自动重新编译加载。 下面将说明ZKWeb如何实现这个功能#xff… ZKWeb网站框架是一个自主开发的网页框架实现了动态插件和自动编译功能。ZKWeb把一个文件夹当成是一个插件无需使用csproj或xproj等形式的项目文件管理并且支持修改插件代码后自动重新编译加载。 下面将说明ZKWeb如何实现这个功能您也可以参考下面的代码和流程在自己的项目中实现。ZKWeb的开源协议是MIT有需要的代码可以直接搬不需要担心协议问题。 实现动态编译依赖的主要技术 编译: Roslyn CompilerRoslyn是微软提供的开源的c# 6.0编译工具可以通过Roslyn来支持自宿主编译功能。要使用Roslyn可以安装nuget包Microsoft.CodeAnalysis.CSharp。微软还提供了更简单的Microsoft.CodeAnalysis.CSharp.Scripting包这个包只需简单几行就能实现c#的动态脚本。 加载dll: System.Runtime.Loader在.Net Framework中动态加载一个dll程序集可以使用Assembly.LoadFile但是在.Net Core中这个函数被移除了。微软为.Net Core提供了一套全新的程序集管理机制要求使用AssemblyLoadContext来加载程序集。遗憾的是我还没有找到微软官方关于这方面的说明。 生成pdb: Microsoft.DiaSymReader.Native, Microsoft.DiaSymReader.PortablePdb为了支持调试编译出来的程序集还需要生成pdb调试文件。在.Net Core中Roslyn并不包含生成pdb的功能还需要安装Microsoft.DiaSymReader.Native和Microsoft.DiaSymReader.PortablePdb才能支持生成pdb文件。安装了这个包以后Roslyn会自动识别并使用。 实现动态编译插件系统的流程 在ZKWeb框架中插件是一个文件夹网站的配置文件中的插件列表就是文件夹的列表。在网站启动时会查找每个文件夹下的*.cs文件对比文件列表和修改时间是否与上次编译的不同如果不同则重新编译该文件夹下的代码。网站启动后会监视*.cs和*.dll文件是否有变化如果有变化则重新启动网站以重新编译。ZKWeb的插件文件夹结构如下 插件文件夹net: .Net Framework编译的程序集netstandard: .Net Core编译的程序集插件名称.dll: 编译出来的程序集插件名称.pdb: 调试文件CompileInfo.txt: 储存了文件列表和修改时间同net文件夹下的内容bin程序集文件夹src 源代码文件夹static 静态文件的文件夹其他文件夹…… 通过Roslyn编译代码文件到程序集dll 在网站启动时插件管理器在得到插件文件夹列表后会使用Directory.EnumerateFiles递归查找该文件夹下的所有*.cs文件。在得到这些代码文件路径后我们就可以传给Roslyn让它编译出dll程序集。ZKWeb调用Roslyn编译的完整代码可以查看这里下面说明编译的流程 首先调用CSharpSyntaxTree.ParseText来解析代码列表到语法树列表我们可以从源代码列表得出ListSyntaxTree。parseOptions是解析选项ZKWeb会在.Net Core编译时定义NETCORE标记这样插件代码中可以使用#if NETCORE来定义.Net Core专用的处理。path是文件路径必须传入文件路径才能调试生成出来的程序集否则即使生成了pdb也不能捕捉断点。 // Parse source files into syntax trees// Also define NETCORE for .Net Corevar parseOptions CSharpParseOptions.Default;#if NETCOREparseOptions parseOptions.WithPreprocessorSymbols(NETCORE);#endifvar syntaxTrees sourceFiles.Select(path CSharpSyntaxTree.ParseText(File.ReadAllText(path), parseOptions, path, Encoding.UTF8)) .ToList(); 接下来需要分析代码中的using来找出代码依赖了哪些程序集并逐一载入这些程序集。例如遇到using System.Threading;会尝试载入System和System.Threading程序集。 // Find all using directive and load the namespace as assembly// Its for resolve assembly dependencies of pluginLoadAssembliesFromUsings(syntaxTrees); LoadAssembliesFromUsings的代码如下虽然比较长但是逻辑并不复杂。关于IAssemblyLoader将在后面阐述这里只需要知道它可以按名称载入程序集。 /// summary/// Find all using directive/// And try to load the namespace as assembly/// /summary/// param namesyntaxTreesSyntax trees/paramprotected void LoadAssembliesFromUsings(IListSyntaxTree syntaxTrees) {    // Find all using directivevar assemblyLoader Application.Ioc.ResolveIAssemblyLoader();    foreach (var tree in syntaxTrees) {        foreach (var usingSyntax in ((CompilationUnitSyntax)tree.GetRoot()).Usings) {            var name usingSyntax.Name;            var names new Liststring();            while (name ! null) {                // The type is IdentifierNameSyntax if its single identifier// eg: System// The type is QualifiedNameSyntax if its contains more than one identifier// eg: System.Threadingif (name is QualifiedNameSyntax) {                    var qualifiedName (QualifiedNameSyntax)name;                    var identifierName (IdentifierNameSyntax)qualifiedName.Right;names.Add(identifierName.Identifier.Text);name qualifiedName.Left;} else if (name is IdentifierNameSyntax) {                    var identifierName (IdentifierNameSyntax)name;names.Add(identifierName.Identifier.Text);name null;}}            if (names.Contains(src)) {                // Ignore if it looks like a namespace from plugin continue;}names.Reverse();            for (int c 1; c names.Count; c) {                // Try to load the namespace as assembly// eg: will try System and System.Threading from System.Threadingvar usingName string.Join(., names.Take(c));                if (LoadedNamespaces.Contains(usingName)) {                    continue;}                try {assemblyLoader.Load(usingName);} catch {}LoadedNamespaces.Add(usingName);}}} } 经过上面这一步后代码依赖的所有程序集应该都载入到当前进程中了我们需要找出这些程序集并且传给Roslyn在编译代码时引用这些程序集文件。下面的代码生成了一个ListPortableExecutableReference对象。 // Add loaded assemblies to compile referencesvar assemblyLoader Application.Ioc.ResolveIAssemblyLoader();var references assemblyLoader.GetLoadedAssemblies().Select(assembly assembly.Location).Select(path MetadataReference.CreateFromFile(path)).ToList(); 最后调用Roslyn编译传入语法树列表和引用程序集列表可以得到目标程序集。使用Emit函数编译后会返回一个EmitResult对象里面保存了编译中出现的错误和警告信息。注意编译出错时Emit不会抛出例外需要手动检查EmitResult中的Success属性。 // Compile to assembly, throw exception if error occurredvar compilation CSharpCompilation.Create(assemblyName).WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,optimizationLevel: optimizationLevel)).AddReferences(references).AddSyntaxTrees(syntaxTrees);var emitResult compilation.Emit(assemblyPath, pdbPath);if (!emitResult.Success) {    throw new CompilationException(string.Join(\r\n, emitResult.Diagnostics)); } 到此已经完成了代码文件(cs)到程序集(dll)的编译下面来看如何载入这个程序集。 载入程序集 在.Net Framework中载入程序集文件非常简单只需要调用Assembly.LoadFile。在.Net Core中载入程序集文件需要定义AssemblyLoadContext并且所有相关的程序集都需要通过同一个Context来载入。需要注意的是AssemblyLoadContext不能用在.Net Framework中ZKWeb为了消除这个差异定义了IAssemblyLoader接口。完整的代码可以查看IAssemblyLoaderCoreAssemblyLoaderNetAssemblyLoader .Net Framework的载入只是调用了Assembly中原来的函数这里就不再说明了。.Net Core使用的载入器定义了AssemblyLoadContext代码如下代码中的plugin.ReferenceAssemblyPath指的是插件自带的第三方dll文件用于载入插件依赖但是主项目中没有引用的dll文件。 /// summary/// The context for loading assembly/// /summaryprivate class LoadContext : AssemblyLoadContext {    protected override Assembly Load(AssemblyName assemblyName) {        try {            // Try load directlyreturn Assembly.Load(assemblyName);} catch {            // If failed, try to load it from plugins reference directoryvar pluginManager Application.Ioc.ResolvePluginManager();            foreach (var plugin in pluginManager.Plugins) {                var path plugin.ReferenceAssemblyPath(assemblyName.Name);                if (path ! null) {                    return LoadFromAssemblyPath(path);}}            throw;}} } 定义了LoadContext以后需要把这个类设为单例载入时都通过这个Context来载入。因为.Net Core目前无法获取到所有已载入的程序集只能获取程序本身依赖的程序集列表这里还添加了一个ISetAssembly LoadedAssemblies用于记录历史载入的所有程序集。 /// summary/// Load assembly by name/// /summarypublic Assembly Load(string name) {    // Replace name if replacement existsname ReplacementAssemblies.GetOrDefault(name, name);    var assembly Context.LoadFromAssemblyName(new AssemblyName(name));LoadedAssemblies.Add(assembly);    return assembly; }/// summary/// Load assembly by name object/// /summarypublic Assembly Load(AssemblyName assemblyName) {    var assembly Context.LoadFromAssemblyName(assemblyName);LoadedAssemblies.Add(assembly);    return assembly; }/// summary/// Load assembly from its binary contents/// /summarypublic Assembly Load(byte[] rawAssembly) {    using (var stream new MemoryStream(rawAssembly)) {        var assembly Context.LoadFromStream(stream);LoadedAssemblies.Add(assembly);        return assembly;} }/// summary/// Load assembly from file path/// /summarypublic Assembly LoadFile(string path) {    var assembly Context.LoadFromAssemblyPath(path);LoadedAssemblies.Add(assembly);    return assembly; } 到这里已经可以载入编译的程序集(dll)文件了下面来看如何实现修改代码后自动重新编译。 检测代码文件变化并自动重新编译 ZKWeb使用了FileSystemWatcher来检测代码文件的变化完整代码可以查看这里。主要的代码如下 // Function use to stop websiteAction stopWebsite () {    var stoppers Application.Ioc.ResolveManyIWebsiteStopper();stoppers.ForEach(s s.StopWebsite()); };// Function use to handle file changedActionstring onFileChanged (path) {    var ext Path.GetExtension(path).ToLower();    if (ext .cs || ext .json || ext .dll) {stopWebsite();} };// Function use to start file system watcherActionFileSystemWatcher startWatcher (watcher) {watcher.NotifyFilter NotifyFilters.LastWrite | NotifyFilters.FileName;watcher.Changed (sender, e) onFileChanged(e.FullPath);watcher.Created (sender, e) onFileChanged(e.FullPath);watcher.Deleted (sender, e) onFileChanged(e.FullPath);watcher.Renamed (sender, e) { onFileChanged(e.FullPath); onFileChanged(e.OldFullPath); };watcher.EnableRaisingEvents true; };// Monitor plugin directoryvar pathManager Application.Ioc.ResolvePathManager(); pathManager.GetPluginDirectories().Where(p Directory.Exists(p)).ForEach(p {    var pluginFilesWatcher new FileSystemWatcher();pluginFilesWatcher.Path p;pluginFilesWatcher.IncludeSubdirectories true;startWatcher(pluginFilesWatcher); }); 这段代码监视了插件文件夹下的cs, json, dll文件一旦发生变化就调用IWebsiteStopper来停止网站网站下次打开时将会重新编译和载入插件。IWebsiteStopper是一个抽象的接口在Asp.Net中停止网站调用了HttpRuntime.UnloadAppDomain而在Asp.Net Core中停止网站调用了IApplicationLifetime.StopApplication。 Asp.Net停止网站会卸载当前的AppDomain下次刷新网页时会自动重新启动。而Asp.Net Core停止网站会终止当前的进程使用IIS托管时IIS会在自动重启进程但使用自宿主时则需要依赖外部工具来重启。 写在最后 ZKWeb实现的动态编译技术大幅度的减少了开发时的等待时间主要节省在不需要每次都按快捷键编译和不需要像其他模块化开发一样需要从子项目复制dll文件到主项目如果dll文件较多而且用了机械硬盘复制时间可能会比编译时间还要漫长。 我将会在这个博客继续分享ZKWeb框架中使用的技术。如果有不明白的部分欢迎加入ZKWeb交流群522083886询问 相关文章  Roslyn项目系统简介Roslyn开源第一年试炼与凯旋 原文地址http://www.cnblogs.com/zkweb/p/5857355.html .NET社区新闻深度好文微信中搜索dotNET跨平台或扫描二维码关注
http://www.yutouwan.com/news/225551/

相关文章:

  • 网站后台fpt网站的字体做多大
  • 专做外贸的网站有哪些开发一个公司官网一般多少钱
  • 本地的响应式网站建设php 网站发布
  • 东莞网站建设做网站怎样做网站优化 关键词
  • 顺德企业手机网站建设wordpress使用缩略图
  • 水利建设专项收入在什么网站上申报做螺杆比较出名的网站
  • 域名注册好了如何做网站网站建设规划方案制作
  • 濮阳河南网站建设深圳住房与建设局官网
  • 商业类网站的设计与制作苏州相城做网站的
  • 宁波seo整站优化软件长沙教育网站开发
  • 北京个人网站设计设计网站建设合同书6
  • 一个空间做2个网站吗怎么做网盘网站
  • 济南手机网站建设公司报价房地产app开发
  • 上海做网站需要多少钱中国机械加工网订单
  • 建设一个网站的设备wordpress+分页静态
  • 潍坊市房屋和城乡建设局网站网站版权设置
  • 达州市建设规划网站新闻做的差的网站
  • 免费网站管理系统wordpress内涵段子
  • 嘉兴本地推广网站有哪些可以访问国外网站的dns
  • 手机网站用什么域名福田做网站优化乐云seo
  • 公司做网站需要什么条件外链查询网站
  • 网站开发的环境舟山建设工程信息网站
  • 个人艺术作品网站建设策划书在线登录qq网页版
  • 电商网站建设方案沈阳app定制
  • 电子商务网站费用好视通视频会议app下载安装
  • wordpress 修改文件名做第三方seo优化网站
  • 网站开发框架文档做网站按什么收费
  • 建网站难不难建设厅电工证查询网站官方网
  • 电子商务网站页面设计图片响应式网站的制作工具
  • 家里电脑做网站服务器做网站生意不赚钱6