基于 Roslyn 实现动态编译

Intro

之前做的一个数据库小工具可以支持根据 Model 代码文件生成创建表的 sql 语句,原来是基于 CodeDom 实现的,最近改成使用基于 Roslyn 去做了。实现的原理在于编译选择的Model 文件生成一个程序集,再从这个程序集中拿到 Model (数据库表)信息以及属性信息(数据库表字段信息),拿到数据库表以及表字段信息之后就根据数据库类型生成大致的创建表的 sql 语句。

CodeFirst 效果如下图所示:

如果你还不知道这个数据库小工具,欢迎访问这个项目了解更多https://github.com/WeihanLi/DbTool

迁移原因

最初的 CodeDom 也是可以用的,但是有一些比较新的 C# 语法不支持,比如 C#6 中的指定属性初始值 public int Number {get;set;} = 1;,最初我是迁移到了 Microsoft.CodeDom.Providers.DotNetCompilerPlatform

这个是一个 CodeDom 过渡到 Roslyn 的实现,他提供了和 CodeDom 差不多的语法,支持 C#6 的语法。但是还是有个问题,我的项目使用了新的项目文件格式,在 VS 中可以编译通过,但是 dotnet cli 编译不通过,详见 issue https://github.com/aspnet/RoslynCodeDomProvider/issues/51

这个问题已经过去一年了仍未解决,最终决定迁移到 Roslyn,直接使用 Roslyn 实现动态编译。

对 CodeDom 感兴趣的童鞋可以看 DbTool 之前的 commit 记录,在此不多叙述。

使用 Roslyn 实现动态编译

Roslyn 好像没有直接根据几个文件去编译(可能有只是我没发现),我就使用了一个比较笨的办法,把几个文件的内容都读出来,合并在一起(命名空间需要去重),然后去编译,完整源代码地址

,实现代码如下:

/// <summary>
/// 从 源代码 中获取表信息
/// </summary>
/// <param name="sourceFilePaths">sourceCodeFiles</param>
/// <returns></returns>
public static List<TableEntity> GeTableEntityFromSourceCode(params string[] sourceFilePaths)
{
if (sourceFilePaths == null || sourceFilePaths.Length <= 0)
{
return null;
}
var usingList = new List<string>(); var sourceCodeTextBuilder = new StringBuilder(); foreach (var path in sourceFilePaths)
{
foreach (var line in File.ReadAllLines(path))
{
if (line.StartsWith("using ") && line.EndsWith(";"))
{
//
usingList.AddIfNotContains(line);
}
else
{
sourceCodeTextBuilder.AppendLine(line);
}
}
} var sourceCodeText =
$"{usingList.StringJoin(Environment.NewLine)}{Environment.NewLine}{sourceCodeTextBuilder}"; // 获取完整的代码 var systemReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var annotationReference = MetadataReference.CreateFromFile(typeof(TableAttribute).Assembly.Location);
var weihanliCommonReference = MetadataReference.CreateFromFile(typeof(IDependencyResolver).Assembly.Location); var syntaxTree = CSharpSyntaxTree.ParseText(sourceCodeText, new CSharpParseOptions(LanguageVersion.Latest)); // 获取代码分析得到的语法树 var assemblyName = $"DbTool.DynamicGenerated.{ObjectIdGenerator.Instance.NewId()}"; // 创建编译任务
var compilation = CSharpCompilation.Create(assemblyName) //指定程序集名称
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))//输出为 dll 程序集
.AddReferences(systemReference, annotationReference, weihanliCommonReference) //添加程序集引用
.AddSyntaxTrees(syntaxTree) // 添加上面代码分析得到的语法树
;
var assemblyPath = ApplicationHelper.MapPath($"{assemblyName}.dll");
var compilationResult = compilation.Emit(assemblyPath); // 执行编译任务,并输出编译后的程序集
if (compilationResult.Success)
{
// 编译成功,获取编译后的程序集并从中获取数据库表信息以及字段信息
try
{
byte[] assemblyBytes;
using (var fs = File.OpenRead(assemblyPath))
{
assemblyBytes = fs.ToByteArray();
}
return GeTableEntityFromAssembly(Assembly.Load(assemblyBytes));
}
finally
{
File.Delete(assemblyPath); // 清理资源
}
} var error = new StringBuilder(compilationResult.Diagnostics.Length * 1024);
foreach (var t in compilationResult.Diagnostics)
{
error.AppendLine($"{t.GetMessage()}");
}
// 获取编译错误
throw new ArgumentException($"所选文件编译有错误{Environment.NewLine}{error}");
}

Reference

基于 Roslyn 实现动态编译的更多相关文章

  1. 基于roslyn的动态编译库Natasha

    人老了,玩不转博客园的编辑器,详细信息转到:https://mp.weixin.qq.com/s/1r6YKBkyovQSMUgfm_VxBg 关键字:Github, NCC, Natasha,Ros ...

  2. 使用 Roslyn引擎动态编译代码

    Roslyn引擎自2014年开源至今这么久,一直没怎么了解过,虽然VS2015早就集成了它. 以前老一套的动态编译方法在 .NET Core中似乎不再支持了,很多方法都是未实现的.下面就介绍如何在.N ...

  3. 基于 Roslyn 实现一个简单的条件解析引擎

    基于 Roslyn 实现一个简单的条件解析引擎 Intro 最近在做一个勋章的服务,我们想定义一些勋章的获取条件,满足条件之后就给用户颁发一个勋章,定义条件的时候会定义需要哪些参数,参数的类型,获取勋 ...

  4. 基于.net standard 的动态编译实现

    在前文[基于.net core 微服务的另类实现]结尾处,提到了如何方便自动的生成微服务的客户端代理,使对于调用方透明,同时将枯燥的东西使用框架集成,以提高使用便捷性.在尝试了基于 Emit 中间语言 ...

  5. 分享基于.NET动态编译&Newtonsoft.Json封装实现JSON转换器(JsonConverter)原理及JSON操作技巧

    看文章标题就知道,本文的主题就是关于JSON,JSON转换器(JsonConverter)具有将C#定义的类源代码直接转换成对应的JSON字符串,以及将JSON字符串转换成对应的C#定义的类源代码,而 ...

  6. 基于.NetCore开发博客项目 StarBlog - (12) Razor页面动态编译

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  7. ZKWeb网站框架的动态编译的实现原理

    ZKWeb网站框架是一个自主开发的网页框架,实现了动态插件和自动编译功能. ZKWeb把一个文件夹当成是一个插件,无需使用csproj或xproj等形式的项目文件管理,并且支持修改插件代码后自动重新编 ...

  8. 重写代码生成器支持模板(多层架构,MVC),多语言c#,java;支持mysql和sqlserver,动态编译

    多年前用过李天平前辈的,自己改过,后来李老师做动软了,不给源码,修改不是很方便.加上我目前需要转java方向,于是决定自己搞.到目前为止花了整整一个星期了,看看目前的成果. 最后是代码工程文件,用c# ...

  9. .NET中的动态编译

    代码的动态编译并执行是一个.NET平台提供给我们的很强大的工具用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码来扩展我们已有 的应用程序.这在很大程度上给我们提供了另外 ...

随机推荐

  1. Shell Step by Step (3) —— Stdin &amp; if

    4.输入输出 #! /bin/bash # Read users input and then get his name read -p "Please input your first n ...

  2. python去噪算法

    <programming computer vision with python >中denoise 算法有误,从网上好了可用的代码贴上,以便以后使用. 书中错误的代码: def deno ...

  3. jQuery迭代器

    http://www.imooc.com/code/3417 迭代器 迭代器是一个框架的重要设计.我们经常需要提供一种方法顺序用来处理聚合对象中各个元素,而又不暴露该对象的内部,这也是设计模式中的迭代 ...

  4. [ACM] POJ 2689 Prime Distance (筛选范围大素数)

    Prime Distance Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 12811   Accepted: 3420 D ...

  5. Linux下编译,要下载tar.xz,而不要下载zip,因为换行的编码不一样,对.h.cpp没有影响,但是对脚本有影响 good

    原因是 在win下编辑的时候,换行结尾是\n\r , 而在linux下 是\n,所以才会有 多出来的\r但是这个我是直接下载的官网文件解压的,没有动过啊. 破案了. linux下编译要下 .tar.x ...

  6. build-qt.sh(Cross compile in Linux for Windows)

    #!/bin/bash set -e MINGW=${MINGW:-${ARCH:-x86_64}-w64-mingw32} PREFIX=${PREFIX:-usr} WORKSPACE=${WOR ...

  7. WPF进阶教程 - 使用Decorator自定义带三角形的边框

    原文:WPF进阶教程 - 使用Decorator自定义带三角形的边框 写下来,备忘. Decorator,有装饰器.装饰品的意思,很容易让人联想到设计模式里面的装饰器模式.Decorator类负责包装 ...

  8. 用C语言编写Windows服务程序的五个步骤

    Windows 服务被设计用于需要在后台运行的应用程序以及实现没有用户交互的任务.为了学习这种控制台应用程序的基础知识,C(不是C++)是最佳选择.本文将建立并实现一个简单的服务程序,其功能是查询系统 ...

  9. XF 表视图添加和删除行

    using System;using Xamarin.Forms;using Xamarin.Forms.Xaml; [assembly: XamlCompilation (XamlCompilati ...

  10. JAVASCRIPT高程笔记-------第六章 面向对象的程序设计

    理解对象的概念  js中的对象与其他 编程语言中的类不一样  ECMAscript 没有类的概念      ECMA-262 把对象定义为 “无序属性的集合,其属性可以包含基本值,对象或者函数”   ...