基于 Roslyn 实现动态编译
基于 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
- 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 实现动态编译的更多相关文章
- 基于roslyn的动态编译库Natasha
人老了,玩不转博客园的编辑器,详细信息转到:https://mp.weixin.qq.com/s/1r6YKBkyovQSMUgfm_VxBg 关键字:Github, NCC, Natasha,Ros ...
- 使用 Roslyn引擎动态编译代码
Roslyn引擎自2014年开源至今这么久,一直没怎么了解过,虽然VS2015早就集成了它. 以前老一套的动态编译方法在 .NET Core中似乎不再支持了,很多方法都是未实现的.下面就介绍如何在.N ...
- 基于 Roslyn 实现一个简单的条件解析引擎
基于 Roslyn 实现一个简单的条件解析引擎 Intro 最近在做一个勋章的服务,我们想定义一些勋章的获取条件,满足条件之后就给用户颁发一个勋章,定义条件的时候会定义需要哪些参数,参数的类型,获取勋 ...
- 基于.net standard 的动态编译实现
在前文[基于.net core 微服务的另类实现]结尾处,提到了如何方便自动的生成微服务的客户端代理,使对于调用方透明,同时将枯燥的东西使用框架集成,以提高使用便捷性.在尝试了基于 Emit 中间语言 ...
- 分享基于.NET动态编译&Newtonsoft.Json封装实现JSON转换器(JsonConverter)原理及JSON操作技巧
看文章标题就知道,本文的主题就是关于JSON,JSON转换器(JsonConverter)具有将C#定义的类源代码直接转换成对应的JSON字符串,以及将JSON字符串转换成对应的C#定义的类源代码,而 ...
- 基于.NetCore开发博客项目 StarBlog - (12) Razor页面动态编译
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
- ZKWeb网站框架的动态编译的实现原理
ZKWeb网站框架是一个自主开发的网页框架,实现了动态插件和自动编译功能. ZKWeb把一个文件夹当成是一个插件,无需使用csproj或xproj等形式的项目文件管理,并且支持修改插件代码后自动重新编 ...
- 重写代码生成器支持模板(多层架构,MVC),多语言c#,java;支持mysql和sqlserver,动态编译
多年前用过李天平前辈的,自己改过,后来李老师做动软了,不给源码,修改不是很方便.加上我目前需要转java方向,于是决定自己搞.到目前为止花了整整一个星期了,看看目前的成果. 最后是代码工程文件,用c# ...
- .NET中的动态编译
代码的动态编译并执行是一个.NET平台提供给我们的很强大的工具用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码来扩展我们已有 的应用程序.这在很大程度上给我们提供了另外 ...
随机推荐
- MyEclipse各种版本号注冊码
一:MyEclipse_6.0.1GA_E3.3.1_FullStackInstaller注冊码 Subscriber:javp Subscription Code:wLR7ZL-655551-685 ...
- yii2.0预先处理方法
public function beforeAction($action){ return $action; }
- 转载来自朱小厮的博客的NIO相关基础篇
用户空间以及内核空间概念 我们知道现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操心系统的核心是内核,独立于普通的应用程序,可以访问受保 ...
- c语言学习笔记(11)——枚举
# include <stdio.h> enum WeekDay //定义了一个数据类型(值只能写一下值) { MonDay, TuesDay, WednesDay, ThursDay, ...
- QComboBox实现复选功能(三种方法:嵌套QListWidget, 设置QStandardItemModel, 设置Delegate)
今天介绍一下一个小东西 — 如何让QComboBox实现复选功能? 需求: 下拉列表有复选功能 不可编辑 显示所有选中项 关于QComboBox的复选功能有几种方案: QStandardIte ...
- EPPlus简单使用
在使用之前需要在项目中添加对EEPULS.dll的引用 1,创建excel 2,创建sheet 3,添加内容 4,修改 5,保存 FileInfo newFile = new FileInfo(fil ...
- 【C#】【WPF】如何读写app.config文件
WPF生成的项目中会有.exe.config.一般是系统默认配置的 格式是xml格式,C#的项目可以直接读写这些文件.方法代码如下. public static string GetConnectio ...
- MVC 组件之间的关系
View和Controller都可以直接请求Model 但是Model不依赖View和controller lController可以直接请求View来显示具体页面 View不依赖Controller ...
- golang1.8 通过plugin方式build so
golang1.83只有linux下支持plugin方式生成so package main // // No C code needed. import "C" import ( ...
- select ,update 加锁
最近我在弄一个项目,其中涉及到了数据批量导入数据库的过程,在导入数据的时候,每一条数据会生成一个唯一标识,但是我发现有些数据的标识重复了.我在网上查了一下说这是“数据库 并发性”的问题解决方案,上锁. ...