ZKWeb网站框架介绍,vs中web网址和web应用程序的分化

ZKWeb网址框架是二个自主开拓的网页框架,完成了动态插件和机关编写翻译功能。
ZKWeb把一个文书夹当成是二个插件,无需使用csproj或xproj等花样的品类文件管理,并且辅助修改插件代码后活动重新编写翻译加载。

ZKWeb网址框架ZKWeb网站框架介绍,vs中web网址和web应用程序的分化。是八个独立开垦的网页框架,达成了动态插件和机动编写翻译效率。
ZKWeb把贰个文书夹当成是三个插件,无需使用csproj或xproj等方式的品种文件管理,并且补助修改插件代码后活动重新编写翻译加载。

Vs200五和VS二〇一〇中都有树立web应用程序和Web网址,总搞的望族不知所戳。
web应用程序大概是微软为了让程序员很好的从winform过渡到web开荒而保留了。Web网址就完全要使用到web开辟的。其实两者之间未有啥大的界别,自个儿从表象计算了一晃他们的异同点。
相同:
一、都以规划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显示有2个本来文件Default.aspx.cs。
贰、web应用程序有重新生成和揭穿两项;Web网址唯有3个公布网站。
三、web应用程序和1般的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应用程序有命名空间,多了1项System.Collections空间引用。

框架地址

下边将表达ZKWeb如何兑现那一个职能,您也足以参考下边的代码和流程在友好的连串中贯彻。
ZKWeb的开源协议是MIT,有须要的代码能够一贯搬,不须求操心协议难题。

上边将表明ZKWeb怎样促成这一个职能,您也可以参见上面包车型客车代码和流程在自身的项目中得以实现。
ZKWeb的开源协议是MIT,有亟待的代码能够一贯搬,不供给怀念协议难题。

 

金镶玉裹福禄双全动态编写翻译重视的要紧才具

编译: 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会自动识别并采纳。

兑现动态编写翻译正视的最主要才干

编译: Roslyn Compiler
Roslyn是微软提供的开源的c#
六.0编译工具,能够透过Roslyn来支撑自宿小编写翻译效能。
要利用Roslyn能够设置nuget包Microsoft.CodeAnalysis.CSharp
微软还提供了更简明的Microsoft.CodeAnalysis.CSharp.Scripting包,这些包只需轻易几行就能促成c#的动态脚本。

加载dll:
System.Runtime.Loader
在.Net
Framework中动态加载1个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.net2006中绝非web应用程序项目。唯有新建网址的功力。SP第11中学扩张了web应用程序的服从。此效能推出,满足了众多VS.NET2003开荒网址的爱侣们。
vs200五的“网址”项目中。其实也有壹些独到之处。原来的vs200三和VS200五SP第11中学的WEB应用程序.是将全体网址应用程序编写翻译成四个DLL。而网站项目中是对各样aspx生成的代码文件,单独编写翻译。特殊目录App_Code中代码文件才编写翻译成单独三个程序集。那种设计。能够独自生成2个页和该页程序集。上传的时候,能够只更新此页。
但这一个“网址”项目,编写翻译速度慢,类型检查不彻底。八个分裂的ASPX能够转移同样的三个称呼的类。宣布的时候,也异常慢,会删除全体原始发表目录中的全数文件,且复制全数新的公文。并且中间还有停顿,需求用户主动按覆盖文件的按键技巧发表。
而在SP1中的WEB应用程序中,编写翻译和公布速度中,明显变快,公布的时候一同头就足以设置是或不是覆盖。原来的网址要晋升过来,须要生成2个设计类代码页。有了此文件,编写翻译的时候,编写翻译器就不用再分析ASPX页面了。明显加速了编写翻译速度。且只生成多少个顺序集。实施的速度页快了。
WebApplication编制程序模型的长处:
●编写翻译速度快,使用非增量编写翻译方式,编写翻译成单独的dll方便管理,。
●生成的主次集
WebSite:生成随机的先后集名,须要经过插件WebDeployment才得以调换单一程序集
WebApplication:能够钦点网址项目转移单一程序集,因为是独立的程序集,所以和其他种类同样能够钦点应用程序集的名字、版本、输出地方等音信
●能够将网址拆分成几个体系以方便管理
●能够从品类中和源代码管理中革除二个文书
●辅助VSTS的Team Build方便每日构建
●更加强劲的代码检查职能,并且检查计策受源代码调控
●能够对编写翻译前后开始展览温馨鲜明的拍卖
●对App_GlobalResources 的Resource强类协理(网上说的,还并未有询问过)
●间接进级使用VS2003创设的特大型系统
WebSite编程模型的长处:
●动态编写翻译该页面,立即能够看看成效,不用编写翻译整个站点(主要优势)
●同上,能够使错误的1对和动用的一对不相干扰(能够必要唯有编写翻译通过才干签入)
●能够每一种页不熟悉成三个程序集(不会选取那种方法)
●能够把三个目录当做三个Web应用来处理,间接复制文件就能够发表,不需重要项目目文件(无所谓,只适合小站点)
●能够把页面也编写翻译到程序集中(应该用不到,而且WebApplication也能够通过WebDeployment插件来兑现)
三种编程模型的交互调换:
VS200伍 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来搜索代码依赖了怎么样程序集,并逐项载入那个程序集。
比如境遇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)的编写翻译,下面来看哪样载入那一个程序集。

经过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指的是插件自带的第一方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近日无法取获得具有已载入的程序集,只好获取程序本人依赖的程序集列表,
此处还增添了3个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中,载入程序集文件1贰分简单,只供给调用Assembly.LoadFile
在.Net
Core中,载入程序集文件需求定义AssemblyLoadContext,并且存有相关的次第集都亟待经过同三个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;
}

到此处1度得以载入编写翻译的次第集(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是1个空洞的接口,在Asp.Net中截至网址调用了HttpRuntime.UnloadAppDomain,而在Asp.Net
Core中截止网址调用了IApplicationLifetime.StopApplication

Asp.Net甘休网址会卸载当前的AppDomain,下次刷新网页时会自动重新起动。
而Asp.Net
Core甘休网站会终止当前的进程,使用IIS托管时IIS会在自动重启进程,但接纳自宿主时则须要注重外部工具来重启。

ZKWeb是贰个重大急忙支付和模块开辟的网址框架。

写在最后

ZKWeb完成的动态编写翻译技巧大幅度的回落了开销时的等候时间,
第贰节省在不需求每一回都按火速键编写翻译和不供给像别的模块化开拓一样须求从子项目复制dll文件到主项目,假使dll文件较多而且用了教条主义硬盘,复制时间也许会比编写翻译时间还要漫长。

自个儿将会在这几个博客继续分享ZKWeb框架中应用的才干。
即使有不领会的一部分,欢迎参预ZKWeb交换群52208388陆打听,

写在结尾

ZKWeb落成的动态编写翻译本事大幅度的回落了开支时的等候时间,
要害节省在不要求每一次都按快速键编写翻译和不须要像任何模块化开拓同样须要从子项目复制dll文件到主项目,假如dll文件较多而且用了形而上学硬盘,复制时间恐怕会比编写翻译时间还要漫长。

作者将会在那几个博客继续分享ZKWeb框架中利用的技能。
1旦有不晓得的1对,欢迎出席ZKWeb交换群522083886打探,

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

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

主要作用

  • .Net Core支持
    • 支撑运营在.Net Framework和.Net Core上
  • 插件系统
    • 使用Roslyn
    • 支撑动态加载插件
    • 支撑修改插件源代码后自行重新编写翻译和加载
  • 模板系统
    • 使用DotLiquid
    • 支撑Django风格的模板重载
    • 襄帮手提式有线电话机版专用模板(优先从templates.mobile读取模板内容)
    • 支撑区域和针对性区域的动态内容,能够在那基础上落到实处可视化编辑
    • 支撑对页面中的部分内容举办独立缓存,能够小幅进级页面包车型大巴响应速度
  • IoC容器
    • 轻量且急迅
    • 暗中认可协助使用性质注册程序集中的门类到容器
    • 支撑构造函数注入
  • 支撑三个框架的托管
    • 支撑托管在Asp.Net
    • 支撑托管在Asp.Net Core
    • 支撑托管在Owin
    • 插件不须要理会托管在哪些框架,使用抽象层就能够
  • 支持四个OBMWX伍M
    • 支持Dapper
    • 支持EntityFramework Core
    • 支持InMemory
    • 支持MongoDB
    • 支持NHibernate
      • NHibernate还无法运转在.Net Core上
    • NHibernate和EFCore扶助运营时自动更新数据表结构,不要求手动员搬迁移
    • OCRUISERM有联合的抽象层,壹份代码能够而且在富有OHummerH二M上运维,但不可能兑现完全合营
  • 本地化
    • 扶助多语言
    • 帮忙多时区
    • 提供了gettext风格的翻译函数
  • 测试
    • 协理在调整台和网页运营测试
    • 帮助在测试中重载IoC容器
    • 支撑在测试中重载Http上下文
    • 支撑在测试中动用一时数据库
  • 花色工具
    • 提供创制项目选拔的工具
    • 提供宣布项目应用的工具

暗中认可插件集中的重大成效

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

花色地址

DEMO

地址:

用户名: demo

密码: 123456

品类进程

主导框架已宣布标准的本子。
政工插件仍在编写制定,目标是行使那套框架做3个开源的杂货铺系统。

讨论QQ群:522083886

网站地图xml地图