ZKWeb网址框架是2个独立自己作主开拓的网页框架,完成了动态插件和自动编写翻译效用。
ZKWeb把3个文书夹当成是1个插件,无需使用csproj或xproj等花样的花色文件管理,并且协助修改插件代码后自动重新编译加载。

ZKWeb网址框架是一个独立开采的网页框架,完结了动态插件和自行编写翻译成效。
ZKWeb把四个文件夹当成是二个插件,无需使用csproj或xproj等格局的品类文件管理,并且帮忙修改插件代码后自行重新编写翻译加载。

Vs2006和VS二零一零中都有创造web应用程序和Web网站,总搞的大家不知所戳。
web应用程序也许是微软为了让程序猿很好的从winform过渡到web开拓而保留了。Web网址就完全要利用到web开采的。其实两者之间未有何样大的界别,自个儿从表象总结了1晃他们的异同点。
相同:
一、都以陈设Asp网页的。
贰、都足以增加ASP.Net文件夹(都不外乎App_Browsers、App_Data、App_GlobalResources、App_LocalResources、App_Themes)。
不同:
1、web应用程序Default.aspx展现有多个原有文件及Default.aspx.cs和Default.aspx.designer.cs;Web网址Default.aspx呈现有三个本来文件Default.aspx.cs。
2、web应用程序有再次生成和公布两项;Web网址唯有二个布告网址。
3、web应用程序和一般的winform未有怎么差距都有引用的是命名空间等;Web网址在引用后边世三个bin文件夹那里存放dll和pdb文件。
四、web应用程序可以看做类库被引用;Web网址则不可以用作类库被引述。
伍、web应用程序可以增多ASP.Net文件夹中不包括bin、App_Code;Web网址能够加多ASP.Net文件夹包涵bin、App_Code。
陆、web应用程序还可增加组件和类;Web网址则从未。
柒、源文件即使都是Default.aspx.cs可是web应用程序有命名空间,多了壹项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三支付网址的对象们。
vs200五的“网址”项目中。其实也有一对优点。原来的vs2003和VS200伍SP第11中学的WEB应用程序.是将全部网址应用程序编写翻译成三个DLL。而网址项目中是对种种aspx生成的代码文件,单独编写翻译。特殊目录App_Code中代码文件才编写翻译成单独二个主次集。那种布署。能够单独生成3个页和该页程序集。上传的时候,能够只更新此页。
但以此“网址”项目,编写翻译速度慢,类型检查不到底。多少个区别的ASPX能够变动一样的几个称呼的类。公布的时候,也非常的慢,会删除全部原始揭橥目录中的全体文件,且复制全体新的文件。并且中间还有停顿,必要用户积极按覆盖文件的按键才能宣布。
而在SP第11中学的WEB应用程序中,编写翻译和揭破速度中,显然变快,发布的时候一开首就可以安装是不是覆盖。原来的网址要晋升过来,要求生成3个企划类代码页。有了此文件,编写翻译的时候,编写翻译器就不用再分析ASPX页面了。显明加快了编写翻译速度。且只生成3个顺序集。试行的速度页快了。
WebApplication编程模型的独到之处:
●编写翻译速度快,使用非增量编写翻译形式,编写翻译成单独的dll方便管理,。
●生成的顺序集
WebSite:生成随机的次序集名,须要通过插件WebDeployment才得以变动单一程序集
WebApplication:可以钦赐网址项目转移单1程序集,因为是独立的程序集,所以和任何品种同样能够钦赐应用程序集的名字、版本、输出地点等新闻
●能够将网址拆分成四个体系以方便管理
●能够从类型仲阳源代码管理中排除一个文书
●支持VSTS的Team Build方便每日创设
●更有力的代码检查职能,并且检查计谋受源代码调整
●能够对编写翻译前后开始展览友好明确的管理
●对App_GlobalResources 的Resource强类援助(网络说的,还未曾明白过)
●直接晋级使用VS200三塑造的大型系统
WebSite编制程序模型的长处:
●动态编写翻译该页面,立即能够见到效果,不用编写翻译整个站点(首要优势)
●同上,能够使错误的一些和采用的一些不相苦恼(可以供给唯有编写翻译通过技艺签入)
【金沙注册送58】vs中web网址和web应用程序的分别,ZKWeb网址框架的动态编写翻译的得以达成原理。●能够每种页面生成多个程序集(不会接纳那种方法)
●能够把叁个目录当做一个Web应用来管理,直接复制文件就足以颁发,不需重要项目目文件(无所谓,只适合小站点)
●能够把页面也编写翻译到程序集中(应该用不到,而且WebApplication也得以通过WebDeployment插件来贯彻)
三种编制程序模型的并行转变:
VS200五 SP1内置了更动程序,能够丰盛方便的从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编写翻译的全部代码能够查阅那里,上边表达编写翻译的流程:

金沙注册送58 ,首先调用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,在编写翻译代码时引用这一个程序集文件。
上边包车型大巴代码生成了2个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)的编写翻译,上边来看怎么载入那个程序集。

由此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,并且具有相关的次序集都亟需经过同2个Context来载入。
亟待专注的是AssemblyLoadContext无法用在.Net
Framework中,ZKWeb为了排除那些出入定义了IAssemblyLoader接口。
总体的代码可以查看
IAssemblyLoader
CoreAssemblyLoader
NetAssemblyLoader

.Net
Framework的载入只是调用了Assembly中原来的函数,这里就不再表达了。
.Net Core使用的载入器定义了AssemblyLoadContext,代码如下:
代码中的plugin.ReferenceAssemblyPath指的是插件自带的第二方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最近不可能取获得全数已载入的程序集,只好获取程序本身依赖的程序集列表,
那里还增多了1个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)文件了,上面来看哪样贯彻修改代码后活动重新编写翻译。

载入程序集

在.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近日无法得到到具有已载入的程序集,只好得到程序自身依赖的次第集列表,
那里还增加了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来检查测试代码文件的变化,完整代码能够翻开那里。
关键的代码如下

// 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使用了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是三个重大火速支付和模块开荒的网址框架。

写在最后

ZKWeb落成的动态编写翻译本事大幅度的回落了成本时的守候时间,
主要节省在不须求每趟都按飞快键编写翻译和不要求像任何模块化开拓同样需求从子项目复制dll文件到主项目,借使dll文件较多而且用了机械硬盘,复制时间或许会比编写翻译时间还要漫长。

本身将会在这几个博客继续分享ZKWeb框架中选择的才干。
1旦有不知道的一些,应接出席ZKWeb沟通群5220838八六驾驭,

写在最后

ZKWeb落成的动态编写翻译本领大幅的滑坡了耗费时的守候时间,
珍视节省在不须要每一回都按飞快键编写翻译和不供给像别的模块化开辟一样须求从子项目复制dll文件到主项目,如若dll文件较多而且用了教条硬盘,复制时间只怕会比编写翻译时间还要漫长。

自己将会在那么些博客继续分享ZKWeb框架中利用的才具。
如果有不驾驭的一部分,接待参预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
    • 插件不须求理会托管在哪些框架,使用抽象层就可以
  • 支撑多个OMuranoM
    • 支持Dapper
    • 支持EntityFramework Core
    • 支持InMemory
    • 支持MongoDB
    • 支持NHibernate
      • NHibernate还不可能运维在.Net Core上
    • NHibernate和EFCore匡助运维时自动更新数据表结构,不供给手动员搬迁移
    • O途乐M有统一的抽象层,壹份代码能够同时在具有O卡宴M上运维,但不能够落实完全协作
  • 本地化
    • 支撑多语言
    • 支撑多时区
    • 提供了gettext风格的翻译函数
  • 测试
    • 支撑在调整台和网页运营测试
    • 支撑在测试中重载IoC容器
    • 支撑在测试中重载Http上下文
    • 支撑在测试中动用一时半刻数据库
  • 品类工具
    • 提供创造项目利用的工具
    • 提供宣布项目应用的工具

默许插件集中的基本点成效

  • 自动生成和评释表单
  • 自动生成Ajax表格
  • 自动生成CRUD页面
  • 按时职责
  • 验证码
  • 管住后台(使用AdminLTE)
  • 自动伪静态,差不多未有额外开支
  • 多货币和多国家帮忙
  • 更多职能请查看各插件的文书档案

种类地址

DEMO

地址:

用户名: demo

密码: 123456

类型进程

中心框架已宣布正式的版本。
作业插件仍在编写,目标是运用那套框架做1个开源的百货公司系统。

讨论QQ群:522083886

相关文章

网站地图xml地图