vs中web网址和web应用程序的分别,ZKWeb网址框架的动态编写翻译的贯彻原理

By admin in 4858.com on 2019年4月6日

ZKWeb网址框架是2个独立自主开发的网页框架,实现了动态插件和电动编写翻译成效。
ZKWeb把三个文件夹当成是一个插件,无需使用csproj或xproj等花样的连串文件管理,并且援救修改插件代码后自动重新编写翻译加载。

ZKWeb网址框架是三个自主开发的网页框架,完结了动态插件和机关编译功能。
ZKWeb把三个文书夹当成是一个插件,无需使用csproj或xproj等格局的项目文件管理,并且帮助修改插件代码后自动重新编写翻译加载。

框架地址

Vs200伍和VS二零零六中都有树立web应用程序和Web网址,总搞的豪门不知所戳。
web应用程序大概是微软为了让程序员很好的从winform过渡到web开发而保留了。Web网站就全盘要运用到web开发的。其实两者之间未有啥大的分别,本人从表象计算了一下他们的异同点。
相同:
1、都以陈设性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呈现有2个原始文件Default.aspx.cs。
2、web应用程序有再度生成和揭穿两项;Web网址只有3个揭露网址。
三、web应用程序和一般的winform未有何分别都有引用的是命名空间等;Web网址在引用后出现三个bin文件夹这里存放dll和pdb文件。
4、web应用程序能够当作类库被引述;Web网址则不得以当作类库被引用。
5、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#
陆.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提供了1套全新的次第集管理机制,须要运用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.net贰零零陆中从未web应用程序项目。唯有新建网址的成效。SP第11中学增添了web应用程序的功能。此意义推出,满意了好多VS.NET200叁付出网址的情人们。
vs200伍的“网址”项目中。其实也有局地亮点。原来的vs200三和VS200伍SP第11中学的WEB应用程序.是将总体网址应用程序编译成1个DLL。而网址项目中是对各种aspx生成的代码文件,单独编写翻译。特殊目录App_Code中代码文件才编写翻译成单独2个先后集。那种设计。能够独立生成二个页和该页程序集。上传的时候,能够只更新此页。
但以此“网址”项目,编写翻译速度慢,类型检查不根本。八个例外的ASPX能够扭转相同的八个称呼的类。发表的时候,也不快,会删除全部原始公布目录中的全数文件,且复制全数新的文件。并且中间还有停顿,需求用户积极按覆盖文件的按钮才能发表。
而在SP第11中学的WEB应用程序中,编写翻译和发表速度中,明显变快,公布的时候壹开首就足以设置是或不是覆盖。原来的网址要升高过来,必要生成三个安排类代码页。有了此文件,编写翻译的时候,编写翻译器就毫无再分析ASPX页面了。鲜明加快了编写翻译速度。且只生成二个主次集。执行的快慢页快了。
WebApplication编制程序模型的亮点:
●编写翻译速度快,使用非增量编写翻译方式,编写翻译成单独的dll方便管理,。
●生成的次第集
WebSite:生成随机的顺序集名,须求通过插件WebDeployment才可以转移单①程序集
WebApplication:可以钦赐网站项目转移单1程序集,因为是单独的程序集,所以和其他门类同样能够钦赐应用程序集的名字、版本、输出位置等音信
●可以将网站拆分成多少个档次以方便管理
vs中web网址和web应用程序的分别,ZKWeb网址框架的动态编写翻译的贯彻原理。●能够从品种花潮源代码管理中革除3个文件
●帮忙VSTS的Team Build方便每一日构建
●更加强大的代码检查职能,并且检查策略受源代码控制
●能够对编译前后开展温馨分明的拍卖
●对App_GlobalResources 的Resource强类帮助(网上说的,还平昔不领悟过)
●直接升高使用VS200三构建的重型系统
WebSite编制程序模型的亮点:
●动态编写翻译该页面,立刻能够看看效果,不用编写翻译整个站点(主要优势)
●同上,能够使错误的片段和运用的片段不相苦恼(能够要求惟有编写翻译通过才能签入)
●能够每种页目生成一个程序集(不会利用那种办法)
●能够把三个目录当做贰个Web应用来处理,直接复制文件就可以发表,不需重要项目目文件(无所谓,只适合小站点)
●能够把页面也编写翻译到程序集中(应该用不到,而且WebApplication也能够通过WebDeployment插件来贯彻)
二种编制程序模型的互相转换:
VS二〇〇六 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框架中,插件是2个文书夹,网址的布署文件中的插件列表就是文件夹的列表。
在网址运转时,会招来每种文件夹下的*.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编写翻译的1体化代码能够查阅这里,下边表达编写翻译的流水生产线:

先是调用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中,载入程序集文件11分不难,只供给调用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方今不可能获得到具有已载入的程序集,只可以获取程序本人注重的顺序集列表,
那里还添加了多个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,并且有着有关的次第集都亟需经过同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近来不能获取到具有已载入的程序集,只好取得程序本身依赖的顺序集列表,
此间还添加了一个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完毕的动态编写翻译技术大幅度的滑坡了费用时的守候时间,
重大节省在不供给每便都按急忙键编写翻译和不须求像任何模块化开发1样需求从子项目复制dll文件到主项目,即使dll文件较多而且用了机械硬盘,复制时间或然会比编写翻译时间还要漫长。

自身将会在这么些博客继续分享ZKWeb框架中采纳的技艺。
只要有不精通的1些,欢迎参预ZKWeb沟通群52208388陆驾驭,

写在最终

ZKWeb完毕的动态编写翻译技术小幅度的滑坡了费用时的守候时间,
首要节省在不须要每一遍都按急速键编写翻译和不必要像任何模块化开发1样供给从子项目复制dll文件到主项目,如若dll文件较多而且用了固态硬盘,复制时间大概会比编写翻译时间还要漫长。

自个儿将会在那些博客继续分享ZKWeb框架中利用的技艺。
假诺有不知晓的1对,欢迎参与ZKWeb沟通群522083886领悟,

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

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

重在功效

  • .Net Core支持
    • 援救运维在.Net Framework和.Net Core上
  • 插件系统
    • 使用Roslyn
    • 扶助动态加载插件
    • 支撑修改插件源代码后活动重新编译和加载
  • 模板系统
    • 使用DotLiquid
    • 帮助Django风格的模版重载
    • 援帮手提式有线电电话机版专用模板(优先从templates.mobile读取模板内容)
    • 援救区域和指向区域的动态内容,能够在那基础上完结可视化编辑
    • 帮助对页面中的部分情节开始展览独立缓存,能够大幅度升级页面包车型地铁响应速度
  • IoC容器
    • 轻量且相当慢
    • 4858.com,暗许援救使用性质注册程序集中的花色到容器
    • 支撑构造函数注入
  • 支撑多少个框架的托管
    • 支撑托管在Asp.Net
    • 扶助托管在Asp.Net Core
    • 帮映衬管在Owin
    • 插件不须求理会托管在哪些框架,使用抽象层即可
  • 支撑两个O智跑M
    • 支持Dapper
    • 支持EntityFramework Core
    • 支持InMemory
    • 支持MongoDB
    • 支持NHibernate
      • NHibernate还不能够运作在.Net Core上
    • NHibernate和EFCore扶助运营时自动更新数据表结构,不必要手动员搬迁移
    • ORubiconM有联合的抽象层,1份代码能够而且在具备OMuranoM上运行,但无法促成完全相称
  • 本地化
    • 支撑多语言
    • 支撑多时区
    • 提供了gettext风格的翻译函数
  • 测试
    • 支撑在控制台和网页运行测试
    • 支撑在测试中重载IoC容器
    • 支撑在测试中重载Http上下文
    • 支持在测试中使用权且数据库
  • 品种工具
    • 提供创设项目应用的工具
    • 提供发表项目选取的工具

默许插件集中的关键意义

  • 自动生成和表明表单
  • 自动生成Ajax表格
  • 自动生成CRUD页面
  • 定时职务
  • 验证码
  • 管理后台(使用AdminLTE)
  • 电动伪静态,差不多从未额外开销
  • 多货币和多国家支持
  • 越多职能请查看各插件的文书档案

项目地址

DEMO

地址:

用户名: demo

密码: 123456

花色进程

基本框架已公布标准的本子。
事情插件仍在编辑,目的是利用这套框架做3个开源的超市系统。

讨论QQ群:522083886

发表评论

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

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有