威尼斯人线上娱乐

ZKWeb网址框架介绍,ZKWeb网址框架的动态编写翻译的得以完成原理

21 4月 , 2019  

ZKWeb网址框架是二个独立开垦的网页框架,完结了动态插件和机动编译功效。
ZKWeb把三个文本夹当成是三个插件,无需使用csproj或xproj等形式的类型文件管理,并且援救修改插件代码后活动重新编写翻译加载。

ZKWeb网址框架是三个独立开荒的网页框架,达成了动态插件和电动编译作用。
ZKWeb把三个文本夹当成是二个插件,无需使用csproj或xproj等情势的品种文件管理,并且援救修改插件代码后自行重新编写翻译加载。

Vs二零零七和VS二零零六中都有树立web应用程序和Web网址,总搞的望族不知所戳。
web应用程序或许是微软为了让程序猿很好的从winform过渡到web开垦而保留了。Web网站就完全要使用到web开采的。其实两者之间未有啥大的界别,本人从表象计算了壹晃他们的异同点。
相同:
一、都以设计Asp网页的。
贰、都得以增添ASP.Net文件夹(都席卷App_Browsers、App_Data、App_GlobalResources、App_LocalResources、App_Themes)。
不同:
一、web应用程序Default.aspx展现有多个原有文件及Default.aspx.cs和Default.aspx.designer.cs;Web网址Default.aspx显示有三个原来文件Default.aspx.cs。
二、web应用程序有双重生成和揭露两项;Web网址唯有二个发表网址。
ZKWeb网址框架介绍,ZKWeb网址框架的动态编写翻译的得以完成原理。叁、web应用程序和一般的winform未有怎么分裂都有引用的是命名空间等;Web网址在引用后出现八个bin文件夹那里存放dll和pdb文件。
四、web应用程序能够当做类库被引述;Web网址则不得以当作类库被引述。
5、web应用程序能够增加ASP.Net文件夹中不包含bin、App_Code;Web网址能够增加ASP.Net文件夹包含bin、App_Code。
6、web应用程序还可加多组件和类;Web网址则并未有。
七、源文件即便都以Default.aspx.cs然则web应用程序有命名空间,多了1项System.Collections空间引用。

框架地址

上面将表明ZKWeb怎么着促成这一个效率,您也足以参考上边包车型的士代码和流程在本身的类型中达成。
ZKWeb的开源协议是MIT,有亟待的代码能够直接搬,不必要忧郁协议难点。

上边将表达ZKWeb怎样贯彻那一个效果,您也得以参照上面的代码和流程在协调的项目中落到实处。
ZKWeb的开源协议是MIT,有亟待的代码能够直接搬,不须求操心协议难题。

 

得以落成动态编写翻译依赖的入眼手艺

编译: Roslyn Compiler
Roslyn是微软提供的开源的c#
陆.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.NativeMicrosoft.DiaSymReader.PortablePdb手艺扶助生成pdb文件。
安装了这么些包将来Roslyn会自动识别并选用。

金玉锦绣动态编写翻译注重的重视技艺

编译: Roslyn Compiler
Roslyn是微软提供的开源的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.NativeMicrosoft.DiaSymReader.PortablePdb本领协助生成pdb文件。
安装了这一个包将来Roslyn会自动识别并使用。

原vs.net贰零零伍中从不web应用程序项目。唯有新建网址的意义。SP第11中学加进了web应用程序的法力。此功能推出,满意了过多VS.NET200三支出网址的爱侣们。
vs2007的“网址”项目中。其实也有部分独到之处。原来的vs200三和VS200五SP1中的WEB应用程序.是将总体网址应用程序编写翻译成三个DLL。而网址项目中是对各样aspx生成的代码文件,单独编写翻译。特殊目录App_Code中代码文件才编写翻译成单独3个顺序集。那种规划。能够单独生成二个页和该页程序集。上传的时候,能够只更新此页。
但以此“网址”项目,编写翻译速度慢,类型检查不干净。两个不等的ASPX能够改造一样的七个名称的类。发表的时候,也相当慢,会去除全数原始宣布目录中的全部文件,且复制全数新的公文。并且中间还有停顿,供给用户积极按覆盖文件的开关本事揭发。
而在SP第11中学的WEB应用程序中,编写翻译和通知速度中,明显变快,公布的时候1开始就能够安装是还是不是覆盖。原来的网址要升迁过来,必要生成2个布置类代码页。有了此文件,编写翻译的时候,编写翻译器就毫无再分析ASPX页面了。明显加快了编译速度。且只生成3个程序集。实行的快慢页快了。
WebApplication编制程序模型的帮助和益处:
●编译速度快,使用非增量编写翻译模式,编写翻译成单独的dll方便管理,。
●生成的程序集
WebSite:生成随机的主次集名,要求经过插件WebDeployment才足以调换单1程序集
WebApplication:能够钦点网址项目更动单1程序集,因为是单身的程序集,所以和任何品种雷同能够钦赐应用程序集的名字、版本、输出地点等音信
●能够将网址拆分成多个项目以方便处理
●能够从品类花潮源代码管理中化解1个文本
●援救VSTS的Team Build方便天天营造
●更加强劲的代码检查成效,并且检查计谋受源代码调整
●能够对编写翻译前后开始展览和煦显著的管理
●对App_GlobalResources 的Resource强类协理(网络说的,还并未有理解过)
●直接晋级使用VS200三构建的大型系统
WebSite编制程序模型的优点:
●动态编写翻译该页面,登时能够观望效果,不用编写翻译整个站点(首要优势)
●同上,能够使错误的片段和平运动用的片段不相困扰(能够要求只有编写翻译通过才干签入)
●能够各样页面生成一个程序集(不会接纳这种形式)
●能够把贰个索引当做2个Web应用来拍卖,直接复制文件就足以揭露,不需重要项目目文件(无所谓,只适合小站点)
●能够把页面也编写翻译到程序集中(应该用不到,而且WebApplication也能够经过WebDeployment插件来促成)
三种编制程序模型的相互调换:
VS2005 SP一内置了更改程序,能够十一分有利的从WebSite转变来WebApplication
只必要复制文件,右键实践“转变为Web应用程序”就可以。
未查到有特意的反向转变工具,但正如后开掘只要转变也非凡轻便。
删除全体*.designer.cs
将*.aspx、*.ascx、*.master页面文件中的 Codebehind=”FileList.aspx.cs”
批量替换到 CodeFile=”FileList.aspx.cs”

新的文书档案地址

兑现动态编译插件系统的流程

在ZKWeb框架中,插件是多少个文书夹,网址的布置文件中的插件列表正是文件夹的列表。
在网址运转时,会寻觅每一种文件夹下的*.cs文本比较文件列表和修改时间是还是不是与上次编写翻译的不相同,假若分裂则再一次编写翻译该公文夹下的代码。
网站运营后,会监视*.cs*.dll文件是还是不是有转变,假若有转换则另行启航网址以重新编写翻译。
ZKWeb的插件文件夹结构如下

  • 插件文件夹
    • bin:程序集文件夹
      • net: .Net Framework编写翻译的先后集
        • 插件名称.dll: 编写翻译出来的次序集
        • 插件名称.pdb: 调节和测试文件
        • CompileInfo.txt: 积攒了文件列表和更动时间
      • netstandard: .Net Core编写翻译的次第集
        • 同net文件夹下的内容
    • src 源代码文件夹
    • static 静态文件的公文夹
    • 其它文件夹……

落成动态编写翻译插件系统的流程

在ZKWeb框架中,插件是三个文本夹,网址的布署文件中的插件列表正是文本夹的列表。
在网址运维时,会搜索每种文件夹下的*.cs文件比较文件列表和改动时间是还是不是与上次编写翻译的比不上,倘若分裂则重复编写翻译该公文夹下的代码。
网址运维后,会监视*.cs*.dll文件是还是不是有生成,假若有生成则再度开动网址以重新编写翻译。
ZKWeb的插件文件夹结构如下

  • 插件文件夹
    • bin:程序集文件夹
      • net: .Net Framework编写翻译的程序集
        • 插件名称.dll: 编写翻译出来的先后集
        • 插件名称.pdb: 调节和测试文件
        • CompileInfo.txt: 积攒了文本列表和改变时间
      • netstandard: .Net Core编写翻译的次序集
        • 同net文件夹下的始末
    • src 源代码文件夹
    • static 静态文件的文书夹
    • 别的文件夹……

通过Roslyn编写翻译代码文件到程序集dll

在网址运行时,插件管理器在取得插件文件夹列表后会选择Directory.EnumerateFiles递归查找该公文夹下的具备*.cs文件。
在获得那么些代码文件路线后,我们就足以传给Roslyn让它编写翻译出dll程序集。
ZKWeb调用Roslyn编写翻译的一体化代码能够查看那里,上边表明编写翻译的流程:

率先调用CSharpSyntaxTree.ParseText来分析代码列表到语法树列表,我们能够从源代码列表得出List<SyntaxTree>
parseOptions是分析选项,ZKWeb会在.Net
Core编写翻译时定义NETCORE标识,那样插件代码中得以接纳#if NETCORE来定义.Net
Core专用的管理。
path是文件路线,必须传入文件路径才能调治生成出来的程序集,不然固然生成了pdb也不可能捕捉断点。

// Parse source files into syntax trees
// Also define NETCORE for .Net Core
var parseOptions = CSharpParseOptions.Default;
#if NETCORE
parseOptions = parseOptions.WithPreprocessorSymbols("NETCORE");
#endif
var syntaxTrees = sourceFiles
    .Select(path => CSharpSyntaxTree.ParseText(
        File.ReadAllText(path), parseOptions, path, Encoding.UTF8))
.ToList();

接下去供给分析代码中的using来找寻代码信赖了什么程序集,并一1载入这一个程序集。
比方说遭遇using System.Threading;会尝试载入SystemSystem.Threading程序集。

// Find all using directive and load the namespace as assembly
// It's for resolve assembly dependencies of plugin
LoadAssembliesFromUsings(syntaxTrees);

LoadAssembliesFromUsings的代码如下,就算比较长不过逻辑并不复杂。
关于IAssemblyLoader就要后头解说,那里只须要驾驭它可以按名称载入程序集。

/// <summary>
/// Find all using directive
/// And try to load the namespace as assembly
/// </summary>
/// <param name="syntaxTrees">Syntax trees</param>
protected void LoadAssembliesFromUsings(IList<SyntaxTree> syntaxTrees) {
    // Find all using directive
    var assemblyLoader = Application.Ioc.Resolve<IAssemblyLoader>();
    foreach (var tree in syntaxTrees) {
        foreach (var usingSyntax in ((CompilationUnitSyntax)tree.GetRoot()).Usings) {
            var name = usingSyntax.Name;
            var names = new List<string>();
            while (name != null) {
                // The type is "IdentifierNameSyntax" if it's single identifier
                // eg: System
                // The type is "QualifiedNameSyntax" if it's contains more than one identifier
                // eg: System.Threading
                if (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.Threading"
                var usingName = string.Join(".", names.Take(c));
                if (LoadedNamespaces.Contains(usingName)) {
                    continue;
                }
                try {
                    assemblyLoader.Load(usingName);
                } catch {
                }
                LoadedNamespaces.Add(usingName);
            }
        }
    }
}

由此地点这一步后,代码信赖的具有程序集应该都载入到近年来进度中了,
大家须要搜索那么些程序集并且传给Roslyn,在编写翻译代码时引用这一个程序集文件。
下边的代码生成了多少个List<PortableExecutableReference>对象。

// Add loaded assemblies to compile references
var assemblyLoader = Application.Ioc.Resolve<IAssemblyLoader>();
var references = assemblyLoader.GetLoadedAssemblies()
    .Select(assembly => assembly.Location)
    .Select(path => MetadataReference.CreateFromFile(path))
    .ToList();

构建编写翻译选项
那边需求调用微软非公开的函数WithTopLevelBinderFlags来安装IgnoreCorLibraryDuplicatedTypes。
其1标记让Roslyn能够忽略System.Runtime.Extensions和System.Private.CoreLib中重新的门类。
借使急需让Roslyn符合规律干活在windows和linux上,必须安装那个标识,具体能够看
Roslyn Scripting暗许会利用那么些标记,操蛋的微软

// Create compilation options and set IgnoreCorLibraryDuplicatedTypes flag
// To avoid error like The type 'Path' exists in both
// 'System.Runtime.Extensions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
// and
// 'System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
var compilationOptions = new CSharpCompilationOptions(
    OutputKind.DynamicallyLinkedLibrary,
    optimizationLevel: optimizationLevel);
var withTopLevelBinderFlagsMethod = compilationOptions.GetType()
    .FastGetMethod("WithTopLevelBinderFlags", BindingFlags.Instance | BindingFlags.NonPublic);
var binderFlagsType = withTopLevelBinderFlagsMethod.GetParameters()[0].ParameterType;
compilationOptions = (CSharpCompilationOptions)withTopLevelBinderFlagsMethod.FastInvoke(
    compilationOptions,
    binderFlagsType.GetField("IgnoreCorLibraryDuplicatedTypes").GetValue(binderFlagsType));

最后调用Roslyn编译,传入语法树列表和引用程序集列表能够赢得目的程序集。
使用Emit函数编写翻译后会再次回到2个EmitResult目标,里面保存了编写翻译中出现的荒唐和警告新闻。
瞩目编写翻译出错开上下班时间Emit不会抛出不相同,供给手动物检疫查EmitResult中的Success属性。

// Compile to assembly, throw exception if error occurred
var compilation = CSharpCompilation.Create(assemblyName)
    .WithOptions(compilationOptions)
    .AddReferences(references)
    .AddSyntaxTrees(syntaxTrees);
var emitResult = compilation.Emit(assemblyPath, pdbPath);
if (!emitResult.Success) {
    throw new CompilationException(string.Join("\r\n",
        emitResult.Diagnostics.Where(d => d.WarningLevel == 0)));
}

到此已经做到了代码文件(cs)到程序集(dll)的编译,上面来看怎么样载入那么些顺序集。

由此Roslyn编写翻译代码文件到程序集dll

在网址运行时,插件管理器在得到插件文件夹列表后会动用Directory.EnumerateFiles递归查找该公文夹下的装有*.cs文件。
在赚取那几个代码文件路线后,咱们就足以传给Roslyn让它编写翻译出dll程序集。
ZKWeb调用Roslyn编写翻译的完好代码能够翻看那里,上面表达编写翻译的流程:

先是调用CSharpSyntaxTree.ParseText来分析代码列表到语法树列表,我们能够从源代码列表得出List<SyntaxTree>
parseOptions是分析选项,ZKWeb会在.Net
Core编译时定义NETCORE标识,那样插件代码中能够运用#if NETCORE来定义.Net
Core专用的拍卖。
path是文本路线,必须传入文件路径才具调解生成出来的程序集,不然就是生成了pdb也不可能捕捉断点。

// Parse source files into syntax trees
// Also define NETCORE for .Net Core
var parseOptions = CSharpParseOptions.Default;
#if NETCORE
parseOptions = parseOptions.WithPreprocessorSymbols("NETCORE");
#endif
var syntaxTrees = sourceFiles
    .Select(path => CSharpSyntaxTree.ParseText(
        File.ReadAllText(path), parseOptions, path, Encoding.UTF8))
.ToList();

接下去要求分析代码中的using来寻找代码信赖了怎样程序集,并逐条载入那几个程序集。
譬如蒙受using System.Threading;会尝试载入SystemSystem.Threading程序集。

// Find all using directive and load the namespace as assembly
// It's for resolve assembly dependencies of plugin
LoadAssembliesFromUsings(syntaxTrees);

LoadAssembliesFromUsings的代码如下,尽管相比较长可是逻辑并不复杂。
关于IAssemblyLoader将要背后解说,这里只必要知道它能够按名称载入程序集。

/// <summary>
/// Find all using directive
/// And try to load the namespace as assembly
/// </summary>
/// <param name="syntaxTrees">Syntax trees</param>
protected void LoadAssembliesFromUsings(IList<SyntaxTree> syntaxTrees) {
    // Find all using directive
    var assemblyLoader = Application.Ioc.Resolve<IAssemblyLoader>();
    foreach (var tree in syntaxTrees) {
        foreach (var usingSyntax in ((CompilationUnitSyntax)tree.GetRoot()).Usings) {
            var name = usingSyntax.Name;
            var names = new List<string>();
            while (name != null) {
                // The type is "IdentifierNameSyntax" if it's single identifier
                // eg: System
                // The type is "QualifiedNameSyntax" if it's contains more than one identifier
                // eg: System.Threading
                if (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.Threading"
                var usingName = string.Join(".", names.Take(c));
                if (LoadedNamespaces.Contains(usingName)) {
                    continue;
                }
                try {
                    assemblyLoader.Load(usingName);
                } catch {
                }
                LoadedNamespaces.Add(usingName);
            }
        }
    }
}

经过地方这一步后,代码重视的全数程序集应该都载入到当前历程中了,
我们供给寻找那几个程序集并且传给Roslyn,在编写翻译代码时引用这几个程序集文件。
上面包车型地铁代码生成了三个List<PortableExecutableReference>对象。

// Add loaded assemblies to compile references
var assemblyLoader = Application.Ioc.Resolve<IAssemblyLoader>();
var references = assemblyLoader.GetLoadedAssemblies()
    .Select(assembly => assembly.Location)
    .Select(path => MetadataReference.CreateFromFile(path))
    .ToList();

营造编译选项
此地须求调用微软非公开的函数WithTopLevelBinderFlags来安装IgnoreCorLibraryDuplicatedTypes。
这么些标识让Roslyn能够忽略System.Runtime.Extensions和System.Private.CoreLib中重复的连串。
假如急需让Roslyn平常干活在windows和linux上,必须设置这几个标记,具体能够看
Roslyn Scripting暗许会利用那么些标识,操蛋的微软

// Create compilation options and set IgnoreCorLibraryDuplicatedTypes flag
// To avoid error like The type 'Path' exists in both
// 'System.Runtime.Extensions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
// and
// 'System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
var compilationOptions = new CSharpCompilationOptions(
    OutputKind.DynamicallyLinkedLibrary,
    optimizationLevel: optimizationLevel);
var withTopLevelBinderFlagsMethod = compilationOptions.GetType()
    .FastGetMethod("WithTopLevelBinderFlags", BindingFlags.Instance | BindingFlags.NonPublic);
var binderFlagsType = withTopLevelBinderFlagsMethod.GetParameters()[0].ParameterType;
compilationOptions = (CSharpCompilationOptions)withTopLevelBinderFlagsMethod.FastInvoke(
    compilationOptions,
    binderFlagsType.GetField("IgnoreCorLibraryDuplicatedTypes").GetValue(binderFlagsType));

最后调用Roslyn编写翻译,传入语法树列表和引用程序集列表能够获得目的程序集。
使用Emit函数编写翻译后会重回壹个EmitResult目的,里面保存了编写翻译中冒出的失实和警戒音讯。
在意编写翻译出错开上下班时间Emit不会抛出区别,供给手动检查EmitResult中的Success属性。

// Compile to assembly, throw exception if error occurred
var compilation = CSharpCompilation.Create(assemblyName)
    .WithOptions(compilationOptions)
    .AddReferences(references)
    .AddSyntaxTrees(syntaxTrees);
var emitResult = compilation.Emit(assemblyPath, pdbPath);
if (!emitResult.Success) {
    throw new CompilationException(string.Join("\r\n",
        emitResult.Diagnostics.Where(d => d.WarningLevel == 0)));
}

到此已经达成了代码文件(cs)到程序集(dll)的编写翻译,上边来看怎么载入那个顺序集。

请参见以上的文书档案以取得最新的音信。

载入程序集

在.Net
Framework中,载入程序集文件卓殊轻巧,只须要调用Assembly.LoadFile
在.Net
Core中,载入程序集文件必要定义AssemblyLoadContext,并且具备有关的主次集都亟需经过同三个Context来载入。
内需注意的是AssemblyLoadContext无法用在.Net
Framework中,ZKWeb为了祛除那个距离定义了IAssemblyLoader接口。
完整的代码能够查阅
IAssemblyLoader
CoreAssemblyLoader
NetAssemblyLoader

.Net
Framework的载入只是调用了Assembly中原来的函数,那里就不再表达了。
.Net Core使用的载入器定义了AssemblyLoadContext威尼斯人线上娱乐 ,,代码如下:
代码中的plugin.ReferenceAssemblyPath指的是插件自带的第2方dll文件,用于载入插件注重可是主项目中从不引用的dll文件。

/// <summary>
/// The context for loading assembly
/// </summary>
private class LoadContext : AssemblyLoadContext {
    protected override Assembly Load(AssemblyName assemblyName) {
        try {
            // Try load directly
            return Assembly.Load(assemblyName);
        } catch {
            // If failed, try to load it from plugin's reference directory
            var pluginManager = Application.Ioc.Resolve<PluginManager>();
            foreach (var plugin in pluginManager.Plugins) {
                var path = plugin.ReferenceAssemblyPath(assemblyName.Name);
                if (path != null) {
                    return LoadFromAssemblyPath(path);
                }
            }
            throw;
        }
    }
}

定义了LoadContext随后须求把那个类设为单例,载入时都通过那个Context来载入。
因为.Net
Core最近不能获得到具备已载入的程序集,只好得到程序自己依赖的次第集列表,
那边还增多了一个ISet<Assembly> LoadedAssemblies用来记录历史载入的有所程序集。

/// <summary>
/// Load assembly by name
/// </summary>
public Assembly Load(string name) {
    // Replace name if replacement exists
    name = ReplacementAssemblies.GetOrDefault(name, name);
    var assembly = Context.LoadFromAssemblyName(new AssemblyName(name));
    LoadedAssemblies.Add(assembly);
    return assembly;
}

/// <summary>
/// Load assembly by name object
/// </summary>
public Assembly Load(AssemblyName assemblyName) {
    var assembly = Context.LoadFromAssemblyName(assemblyName);
    LoadedAssemblies.Add(assembly);
    return assembly;
}

/// <summary>
/// Load assembly from it's binary contents
/// </summary>
public 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
/// </summary>
public Assembly LoadFile(string path) {
    var assembly = Context.LoadFromAssemblyPath(path);
    LoadedAssemblies.Add(assembly);
    return assembly;
}

到此处1度足以载入编译的主次集(dll)文件了,下边来看怎样促成修改代码后活动重新编写翻译。

载入程序集

在.Net
Framework中,载入程序集文件万分简单,只供给调用Assembly.LoadFile
在.Net
Core中,载入程序集文件须求定义AssemblyLoadContext,并且有全部关的次第集都亟需经过同2个Context来载入。
内需注意的是AssemblyLoadContext不能够用在.Net
Framework中,ZKWeb为了排除那么些差别定义了IAssemblyLoader接口。
完整的代码能够查阅
IAssemblyLoader
CoreAssemblyLoader
NetAssemblyLoader

.Net
Framework的载入只是调用了Assembly中原来的函数,那里就不再表达了。
.Net Core使用的载入器定义了AssemblyLoadContext,代码如下:
代码中的plugin.ReferenceAssemblyPath指的是插件自带的第2方dll文件,用于载入插件依赖不过主项目中从不引用的dll文件。

/// <summary>
/// The context for loading assembly
/// </summary>
private class LoadContext : AssemblyLoadContext {
    protected override Assembly Load(AssemblyName assemblyName) {
        try {
            // Try load directly
            return Assembly.Load(assemblyName);
        } catch {
            // If failed, try to load it from plugin's reference directory
            var pluginManager = Application.Ioc.Resolve<PluginManager>();
            foreach (var plugin in pluginManager.Plugins) {
                var path = plugin.ReferenceAssemblyPath(assemblyName.Name);
                if (path != null) {
                    return LoadFromAssemblyPath(path);
                }
            }
            throw;
        }
    }
}

定义了LoadContext日后供给把那一个类设为单例,载入时都因此那个Context来载入。
因为.Net
Core近来无法获取到具备已载入的程序集,只好获得程序本身注重的先后集列表,
此间还加多了2个ISet<Assembly> LoadedAssemblies用于记录历史载入的具备程序集。

/// <summary>
/// Load assembly by name
/// </summary>
public Assembly Load(string name) {
    // Replace name if replacement exists
    name = ReplacementAssemblies.GetOrDefault(name, name);
    var assembly = Context.LoadFromAssemblyName(new AssemblyName(name));
    LoadedAssemblies.Add(assembly);
    return assembly;
}

/// <summary>
/// Load assembly by name object
/// </summary>
public Assembly Load(AssemblyName assemblyName) {
    var assembly = Context.LoadFromAssemblyName(assemblyName);
    LoadedAssemblies.Add(assembly);
    return assembly;
}

/// <summary>
/// Load assembly from it's binary contents
/// </summary>
public 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
/// </summary>
public Assembly LoadFile(string path) {
    var assembly = Context.LoadFromAssemblyPath(path);
    LoadedAssemblies.Add(assembly);
    return assembly;
}

到此地一度得以载入编写翻译的次第集(dll)文件了,上边来看怎么完毕修改代码后自动重新编写翻译。


检查实验代码文件变化并自动重新编写翻译

ZKWeb使用了FileSystemWatcher来检验代码文件的生成,完整代码能够查看那里。
第2的代码如下

// Function use to stop website
Action stopWebsite = () => {
    var stoppers = Application.Ioc.ResolveMany<IWebsiteStopper>();
    stoppers.ForEach(s => s.StopWebsite());
};
// Function use to handle file changed
Action<string> onFileChanged = (path) => {
    var ext = Path.GetExtension(path).ToLower();
    if (ext == ".cs" || ext == ".json" || ext == ".dll") {
        stopWebsite();
    }
};
// Function use to start file system watcher
Action<FileSystemWatcher> 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 directory
var pathManager = Application.Ioc.Resolve<PathManager>();
pathManager.GetPluginDirectories().Where(p => Directory.Exists(p)).ForEach(p => {
    var pluginFilesWatcher = new FileSystemWatcher();
    pluginFilesWatcher.Path = p;
    pluginFilesWatcher.IncludeSubdirectories = true;
    startWatcher(pluginFilesWatcher);
});

那段代码监视了插件文件夹下的cs, json, dll文件,
1旦爆发变化就调用IWebsiteStopper来终止网址,网址下次开垦时将会另行编写翻译和载入插件。
IWebsiteStopper是一个虚无的接口,在Asp.Net中停止网址调用了HttpRuntime.UnloadAppDomain,而在Asp.Net
Core中甘休网址调用了IApplicationLifetime.StopApplication

Asp.Net截至网址会卸载当前的AppDomain,下次刷新网页时会自动重新启航。
而Asp.Net
Core截止网址会终止当前的进度,使用IIS托管时IIS会在活动重启进度,但采纳自宿主时则要求借助外部工具来重启。

检查实验代码文件变化并自行重新编写翻译

ZKWeb使用了FileSystemWatcher来检验代码文件的转移,完整代码能够查看那里。
根本的代码如下

// Function use to stop website
Action stopWebsite = () => {
    var stoppers = Application.Ioc.ResolveMany<IWebsiteStopper>();
    stoppers.ForEach(s => s.StopWebsite());
};
// Function use to handle file changed
Action<string> onFileChanged = (path) => {
    var ext = Path.GetExtension(path).ToLower();
    if (ext == ".cs" || ext == ".json" || ext == ".dll") {
        stopWebsite();
    }
};
// Function use to start file system watcher
Action<FileSystemWatcher> 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 directory
var pathManager = Application.Ioc.Resolve<PathManager>();
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是3个至关心重视要神速支付和模块开荒的网址框架。

写在结尾

ZKWeb完结的动态编写翻译本事大幅的削减了开荒时的等候时间,
重在节省在不供给每趟都按快速键编写翻译和不要求像别的模块化开辟同样供给从子项目复制dll文件到主项目,若是dll文件较多而且用了教条硬盘,复制时间可能会比编写翻译时间还要漫长。

本人将会在这一个博客继续分享ZKWeb框架中接纳的技术。
假设有不明了的一些,接待到场ZKWeb交换群5220838八6打听,

写在最后

ZKWeb落成的动态编写翻译本领大幅的回落了支付时的等候时间,
关键节省在不须要每趟都按神速键编写翻译和不须要像别的模块化开采相同必要从子项目复制dll文件到主项目,即使dll文件较多而且用了教条主义硬盘,复制时间恐怕会比编写翻译时间还要漫长。

本人将会在那一个博客继续分享ZKWeb框架中运用的手艺。
万1有不清楚的部分,迎接参与ZKWeb交换群5220838八陆打探,

提供了动态插件和活动管理数据库结构的功效。

模板系统和自动生成页面参考了Django的做法,并遵从Don’t repeat
yourself原则。

要害效能

  • .Net Core支持
    • 帮忙运行在.Net Framework和.Net Core上
  • 插件系统
    • 使用Roslyn
    • 协助动态加载插件
    • 帮衬修改插件源代码后活动重新编译和加载
  • 模板系统
    • 使用DotLiquid
    • 扶助Django风格的模版重载
    • 支撑手机版专用模板(优先从templates.mobile读取模板内容)
    • 帮忙区域和指向区域的动态内容,能够在那基础上达成可视化编辑
    • 支撑对页面中的部分剧情进行单独缓存,能够大幅提高页面包车型客车响应速度
  • IoC容器
    • 轻量且快速
    • 私下认可援助选取质量注册程序集中的花色到容器
    • 支撑构造函数注入
  • 支撑多少个框架的托管
    • 援救托管在Asp.Net
    • 支撑托管在Asp.Net Core
    • 援助托管在Owin
    • 插件不须要理会托管在哪个框架,使用抽象层就能够
  • 支撑四个OHavalM
    • 支持Dapper
    • 支持EntityFramework Core
    • 支持InMemory
    • 支持MongoDB
    • 支持NHibernate
      • NHibernate还无法运作在.Net Core上
    • NHibernate和EFCore援助运转时自动更新数据表结构,不须求手动员搬迁移
    • O途达M有联合的抽象层,1份代码能够而且在具备O卡宴M上运转,但不可能兑现完全协作
  • 本地化
    • 支撑多语言
    • 帮忙多时区
    • 提供了gettext风格的翻译函数
  • 测试
    • 援救在调节台和网页运转测试
    • 支持在测试中重载IoC容器
    • 支撑在测试中重载Http上下文
    • 援助在测试中应用暂且数据库
  • 品类工具
    • 提供创立项目采纳的工具
    • 提供宣布项目利用的工具

暗许插件聚集的要紧成效

  • 自动生成和认证表单
  • 自动生成Ajax表格
  • 自动生成CRUD页面
  • 定时职分
  • 验证码
  • 管住后台(使用AdminLTE)
  • 活动伪静态,大概从不额外费用
  • 多货币和多国家帮助
  • 越多效益请查看各插件的文书档案

连串地址

DEMO

地址:

用户名: demo

密码: 123456

类型进程

着力框架已宣布正式的版本。
作业插件仍在编排,目的是利用那套框架做3个开源的百货公司系统。

讨论QQ群:522083886


相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图