基于 Roslyn 实现动态编译

Intro

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

CodeFirst 效果如下图所示:

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

迁移原因

最初的 CodeDom 也是可以用的,但是有一些比较新的 C# 语法不支持,比如 C#6 中的指定属性初始值 publicintNumber{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

  • https://github.com/WeihanLi/DbTool/blob/wfdev/src/DbTool/Utils.cs#L27

  • https://github.com/WeihanLi/DbTool

  • https://msdn.microsoft.com/en-us/magazine/mt808499.aspx

基于 Roslyn 实现动态编译相关推荐

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

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

  2. NetCore基于Roslyn的动态编译实现

    目录 一. AvalonEdit文本器 1.功能实现 2. 高亮 3. 代码提示 4. 动态编译 1)依赖项初始化 2) 编译函数 二. 运行效果展示 三. 源码链接 四. 参考资料 一. Avalo ...

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

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

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

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

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

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

  6. 使用Roslyn动态编译和执行

    1. 安装nuget package 2.使用Roslyn 动态执行 var engine = new Roslyn.Scripting.CSharp.ScriptEngine();engine.Cr ...

  7. C#发现之旅第十四讲 基于动态编译的VB.NET脚本引擎

    本章说明 在前面章节中,笔者使用了反射和动态编译技术实现了快速ORM框架,在本章中笔者将继续使用这些技术来实现一个VB.NET的脚本引擎,使得人们在开发中能实现类似MS Office那样实现VBA宏的 ...

  8. 高性能动态编译库Natasha发布1.0版本!

    一. 前言 对于开源贡献者,Emit和表达式树不是陌生的字眼,IL的动态特性为封装工作带来了极大的方便,会Emit的开发者可以说驾驭了大部分的高性能.高动态的编程技巧.纵观ef.dapper.json ...

  9. python 动态编译代码_使用PyQt(Python+Qt)+动态编译36行代码实现的计算器

    PyQt是基于跨平台的图形界面C++开发工具Qt加Python包装的一个GPL软件(GPL是GNU General Public License的缩写,是GNU通用公共授权非正式的中文翻译),Qt基于 ...

最新文章

  1. 《自然》预测2019年重大科学事件
  2. Apache虚拟目录和多端口多主机名配置
  3. Redis的基本操作二
  4. Python Pandas –操作
  5. 安卓* 系统级 Java*/C++ 代码调试
  6. Hasura GraphQL 内部表结构
  7. 用Hough投票做物体检测(续)
  8. 用 Python 高效办公|一次写好100个word通知
  9. Mesh平滑处理的几种算法比较
  10. 图像的几何变换maketform imtransform imresize imcrop
  11. HTML学习(三):排版
  12. 计算机的无线安全类型在哪里设置,在电脑上设置无线热点的方法
  13. Pinterest模式的魅力何在?国内山寨有哪些?
  14. 全力配合金融改革,尝试期货投资基金
  15. 【网络--实验】华三防火墙命令行调试实例
  16. 写在《Python高手修炼之道》发行之前:选择一本好书,即是少走弯路
  17. RSA的非对称加密,公钥加密私钥解密,本地测试
  18. 顺丰职级分成4级_【顺丰速运内部职级和薪资水平是怎么样的?】-看准网
  19. Linux-Apache服务器常规设置——用户个人主页
  20. 轻松熊喵喵 -- 名词解释(自用)

热门文章

  1. PIE SDK与OpenCV结合说明文档
  2. 《图解 HTTP》读书笔记(未完待续)
  3. 限制nginx仅能域名访问,不可用ip访问
  4. python_getopt解析命令行输入参数的使用
  5. [图] DevOps:提速从研发到交付流程
  6. js温故而知新11(AJAX)——学习廖雪峰的js教程
  7. nginx对websocket的支持及uliweb chatroom的测试
  8. C# 文件操作详解(一)---------File类
  9. 怎么实现动态设置静态文件存储目录?
  10. 使用C#体验函数式编程之——Partial application(局部应用)