基于 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平台提供给我们的很强大的工具用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码来扩展我们已有 的应用程序.这在很大程度上给我们提供了另外 ...
随机推荐
- 自定义滚动条样式 -webkit-scrollbar
demo .page-one-content-area-inner-select-wrap height 200px margin-bottom 30px overflow auto &::- ...
- 主干(trunk)、分支(branch )、标记(tag)
主干(trunk).分支(branch ).标记(tag) 用法示例 + 图解 以svn为例,git的master相当于trunk,dev分支相当于branches --------------- ...
- WPF中取得系统字体列表
原文:WPF中取得系统字体列表 在GDI+中,我们可以通过如下方式取得系统所有字体: foreach(FontFamily f in FontFamily.Families){ // 处理代码} ...
- MySQL第五个学习笔记 该数据表的操作
MySQL在创建表,创建.frm文件保存表和列定义.索引存储在一个.MYI(MYindex)且数据存储在有.MYD(MYData)扩展名的文件里. 一.用SHOW/ DESCRIBE语句显示数据表 ...
- 短暂聊天VS温馨提示
介绍 作为一个合作格该程序猿,使用的开发工具的熟练使用是我们必须掌握的. 而作为一名优秀的程序猿.我们不仅要能熟练的使用.并且还要去高效的使用. 而作为.Net平台专门定制的开发工具VS那当然是我们必 ...
- Entity States
Added. The entity does not yet exist in the database. The SaveChanges method must issue an INSERT st ...
- WPF UpdateSourceTrigger的使用
<Window x:Class="XamlTest.Window8" xmlns="http://schemas.microsoft.com/winf ...
- WPF中INotifyPropertyChanged用法与数据绑定
在WPF中进行数据绑定的时候常常会用到INotifyPropertyChanged接口来进行实现,下面来看一个INotifyPropertyChanged的案例. 下面定义一个Person类: usi ...
- DotNetBar for Windows Forms 14.0.0.3_冰河之刃重打包版原创发布
关于 DotNetBar for Windows Forms 14.0.0.3_冰河之刃重打包版 --------------------11.8.0.8_冰河之刃重打包版-------------- ...
- ASP.NET Core 2 学习笔记(一)开始
原文:ASP.NET Core 2 学习笔记(一)开始 来势汹汹的.NET Core似乎要取代.NET Framework,ASP.NET也随之发布.NET Core版本.虽然名称沿用ASP.NET, ...