基于 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平台提供给我们的很强大的工具用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码来扩展我们已有 的应用程序.这在很大程度上给我们提供了另外 ...
随机推荐
- jsonkit 分解nsarray 时刻 一个错误
jsonkit 分解nsarray 时刻 一个错误 Assertion failure in -[TXJKArray count], /Users/mqq/hudson/1740/src/Tencen ...
- UWP 扩展/自定义标题栏的方法,一些概念和一些注意事项
原文 UWP 扩展/自定义标题栏的方法,一些概念和一些注意事项 在 Windows 10 的前几个版本中将页面内容扩展到标题栏上还算简单,主要是没什么坑.直到一些新控件的引入和一些外观设计趋势变化之后 ...
- PFIF网上寻人协议
原文:http://www.csdn.net/article/2013-04-22/2814980 本文的主要内容来自Wikipedia(http://en.wikipedia.org/wiki/Pe ...
- win10家庭版连接不上远程桌面
解决方案来自于:https://blog.csdn.net/weixin_39129504/article/details/80303615 这个解决方案的关键: done!不知是否侵权 如有侵权 ...
- SpringBoot、Groovy
Java——搭建自己的RESTful API服务器(SpringBoot.Groovy) 这又是一篇JavaWeb相关的博客,内容涉及: SpringBoot:微框架,提供快速构建服务的功能 Sp ...
- HDU2665 Kth number 【合并树】
Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- php_Ubuntu Linux下为PHP5安装cURL,mysql
如果你在用PHP, 你可能需要用到cURL, 这是其中最流行的插件. PHP CURL插件需要通过libcurl来实现, Daniel Stenberg创建的一个库, 能够和许多不同类型协议的web服 ...
- c#中的GetUpperBound,GetLowerBound方法
今天使用数组的时候,用到了几个数组的属性,总结如下: Array的Rank 属性:语法:public int Rank { get; } 得到Array的秩(维数).Array的GetUpperBou ...
- 全面剖析Redis Cluster原理和应用 (good)
redis redis cluster注意的问题 : 1.‘cluster-require-full-coverage’参数的设置.该参数是redis配置文件中cluster模式的一个参数,从字面上基 ...
- VS2012 调试Web项目 遭遇 HTTP 错误 500.23 - Internal Server Error
原文:VS2012 调试Web项目 遭遇 HTTP 错误 500.23 - Internal Server Error 在使用vs2012 调试Web站点时 报错 500.23,详细如图 此错误是因为 ...