基于 Roslyn 实现代码动态编译
基于 Roslyn 实现代码动态编译
Intro
之前做的一个数据库小工具可以支持根据 Model 代码文件生成创建表的 sql 语句,原来是基于 CodeDom 实现的,后来改成使用基于 Roslyn 去做了。
实现的原理在于编译选择的Model 文件生成一个程序集,再从这个程序集中拿到 Model (数据库表)信息以及属性信息(数据库表字段信息),拿到数据库表以及表字段信息之后就根据数据库类型生成大致的创建表的 sql 语句。
最近做的一个小工具 dotnet-exec
也是类似的,将代码编译成一个程序集并通过反射的方式执行代码逻辑,
分享一下用到的一些代码
Sample
来看一个最简单的编译一段文本为程序集示例:
// 分析语法树
var syntaxTree = CSharpSyntaxTree.ParseText(sourceText, new CSharpParseOptions(LanguageVersion.Latest));// 配置引用
var references = new[]
{typeof(object).Assembly,Assembly.Load("netstandard"),Assembly.Load("System.Runtime"),
}
.Select(assembly => assembly.Location).Distinct().Select(l => MetadataReference.CreateFromFile(l)).Cast<MetadataReference>().ToArray();var assemblyName = $"DbTool.DynamicGenerated.{GuidIdGenerator.Instance.NewId()}";
// 获取编译
var compilation = CSharpCompilation.Create(assemblyName).WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)).AddReferences(references).AddSyntaxTrees(syntaxTree);using var ms = new MemoryStream();
// 生成编译结果并导出程序集信息到 stream 中
var compilationResult = compilation.Emit(ms);
if (compilationResult.Success)
{var assemblyBytes = ms.ToArray();// 加载程序集return Assembly.Load(assemblyBytes);
}var error = new StringBuilder();
foreach (var t in compilationResult.Diagnostics)
{error.AppendLine($"{t.GetMessage()}");
}
throw new ArgumentException($"Compile error:{Environment.NewLine}{error}");
多段文本的编译示例:
var parseOptions = new CSharpParseOptions(LanguageVersion.Latest);
var syntaxTrees = sourceText.Select(text => CSharpSyntaxTree.ParseText(text)).ToArray();
var references = new[]
{typeof(object).Assembly,Assembly.Load("netstandard"),Assembly.Load("System.Runtime"),
}
.Select(assembly => assembly.Location).Distinct().Select(l => MetadataReference.CreateFromFile(l)).Cast<MetadataReference>().ToArray();
var assemblyName = $"DbTool.DynamicGenerated.{GuidIdGenerator.Instance.NewId()}";
var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
var compilation = CSharpCompilation.Create(assemblyName, syntaxTrees, references, compilationOptions);await using var ms = new MemoryStream();
var compilationResult = compilation.Emit(ms);
if (compilationResult.Success)
{var assemblyBytes = ms.ToArray();return Assembly.Load(assemblyBytes);
}var error = new StringBuilder();
foreach (var t in compilationResult.Diagnostics)
{var msg = CSharpDiagnosticFormatter.Instance.Format(t);error.AppendLine($"{msg}");
}
throw new ArgumentException($"Compile error:{error}");
之前的做法是合并成一段文本,并将多段代码的 using 引用合并,可以参考下面的将多个文件代码合并成一段文本,后来发现自己傻了,改成了上面的用法,直接生成多个语法树再生成编译,推荐使用上面的方式,会更加的友好和
var usingList = new List<string>();var sourceCodeTextBuilder = new StringBuilder();
foreach (var path in sourceFilePaths.Distinct())
{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}";
More
如果需要指定 C# 代码版本可以通过CSharpParseOptions
来指定,比如要使用 preview
特性可以使用 new CSharpParseOptions(LanguageVersion.Preview)
默认地编译会编译成一个 dll 程序集,如果包含 Main 方法要生成一个可执行程序可以通过指定 CSharpCompilationOptions
的 OutputKind
为 OutputKind.ConsoleApplication
, 还有很多可以配置的选项,有需要可以自己探索一下
References
https://github.com/WeihanLi/DbTool/blob/packages/src/DbTool.Core/DefaultModelCodeExactor.cs
https://github.com/WeihanLi/DbTool.Packages/blob/main/src/DbTool.Core/DefaultCSharpModelCodeExtractor.cs
https://mp.weixin.qq.com/s?__biz=Mzg5MDEzNjA3Nw==&mid=2247483821&idx=1&sn=c2f4a672bc9bb1f939cdaaebb26eb8ac&chksm=cfe072cff897fbd9a0de68eec9a45a21d63b943d497723853b8944c5a5c48daede13d16db048&scene=21#wechat_redirect
https://github.com/WeihanLi/dotnet-exec/blob/main/src/dotnet-exec/CodeCompiler.cs
基于 Roslyn 实现代码动态编译相关推荐
- 基于.net standard 的动态编译实现
在前文[基于.net core 微服务的另类实现]结尾处,提到了如何方便自动的生成微服务的客户端代理,使对于调用方透明,同时将枯燥的东西使用框架集成,以提高使用便捷性.在尝试了基于 Emit 中间语言 ...
- 基于 Roslyn 实现一个简单的条件解析引擎
基于 Roslyn 实现一个简单的条件解析引擎 Intro 最近在做一个勋章的服务,我们想定义一些勋章的获取条件,满足条件之后就给用户颁发一个勋章,定义条件的时候会定义需要哪些参数,参数的类型,获取勋 ...
- 使用 Roslyn 分析代码注释,给 TODO 类型的注释添加负责人、截止日期和 issue 链接跟踪
如果某天改了一点代码但是没有完成,我们可能会在注释里面加上 // TODO.如果某个版本为了控制影响范围临时使用不太合适的方法解了 Bug,我们可能也会在注释里面加上 // TODO.但是,对于团队项 ...
- kan-java, 一个能裁剪语法特性的java动态编译工具
'kan-java' 就是 '砍-java' 这是一个java代码动态编译工具,也就是能够把String形式的java代码实时地编译为字节码的工具: "动态编译"工具,其实自jdk ...
- Java动态编译执行
在某些情况下,我们需要动态生成java代码,通过动态编译,然后执行代码.JAVA API提供了相应的工具(JavaCompiler)来实现动态编译.下面我们通过一个简单的例子介绍,如何通过JavaCo ...
- 基于roslyn的动态编译库Natasha
人老了,玩不转博客园的编辑器,详细信息转到:https://mp.weixin.qq.com/s/1r6YKBkyovQSMUgfm_VxBg 关键字:Github, NCC, Natasha,Ros ...
- 基于 Roslyn 实现动态编译
基于 Roslyn 实现动态编译 Intro 之前做的一个数据库小工具可以支持根据 Model 代码文件生成创建表的 sql 语句,原来是基于 CodeDom 实现的,最近改成使用基于 Roslyn ...
- NetCore基于Roslyn的动态编译实现
目录 一. AvalonEdit文本器 1.功能实现 2. 高亮 3. 代码提示 4. 动态编译 1)依赖项初始化 2) 编译函数 二. 运行效果展示 三. 源码链接 四. 参考资料 一. Avalo ...
- 使用基于Roslyn的编译时AOP框架来解决.NET项目的代码复用问题
理想的代码优化方式 团队日常协作中,自然而然的会出现很多重复代码,根据这些代码的种类,之前可能会以以下方式处理 方式 描述 应用时可能产生的问题 硬编码 多数新手,或逐渐腐坏的项目会这么干,会直接复制 ...
- 使用Roslyn将代码编译成单独的网络模块并将它们组装成动态库
目录 介绍 代码 代码说明 主程序说明 实用方法 CreateCompilationWithMscorelib(...)方法 EmitToArray(...) 方法 总结 GitHub 上的代码 介绍 ...
最新文章
- 手把手重现Science的主图Maptree
- Wireshark抓包与常见问题解决
- hdu 4430 Yukari's Birthday (简单数学 + 二分)
- Faster RCNN代码理解(Python) ---训练过程
- Alpha版会议总结
- 简述机器指令与微指令之间的关系_计算机组成原理期末考试题-百度文库
- java并发中的延迟初始化
- nginx配置php 9000,Nginx支持php配置
- [BOOST] BOOST::Format
- VBA - 字典实例集锦
- [Asp.Net Core] Blazor Server Side 项目实践 - 切换页面时保留状态
- 编写高质量代码改善C#程序的157个建议——建议50:在Dispose模式中应区别对待托管资源和非托管资源...
- GARFIELD@12-06-2004
- 【从零写javaweb框架】(零)前言
- 计算机网络中速率(date rate)和带宽的区别
- steam如何载入已经下载好的游戏
- C# 静态和非静态的区别
- 使用STC8A8K64S4A12单片机实现的“基于脉冲宽度调制(PWM)技术的智能温度控制器”
- 微信小程序无法弹出授权登录窗口
- matlab安装到U盘,matlab u盘便携移动版
热门文章
- 北大青鸟php培训怎么样,北大青鸟php培训怎么样
- 电脑软件:主流的压缩软件对比,看完你就会选择了
- python交通流预测算法_使用KNN方法进行的短时交通流预测和结果分析
- ubuntu给手机刷机安卓8.0 ROOT+Xposed+JustTrustMe+Kali NetHunter
- LayaBox2D使用自定义Shader的方法
- Ae导出 计算机内存,ae导出视频太大怎么办-缩小Ae导出视频大小的方法 - 河东软件园...
- MOEA/D的通俗解析--1.MOEA
- 【图算法】(3) 网络的基本静态几何特征(二),附networkx完整代码
- iPhone防止系统自动下载更新
- 英语口语测试评分软件,最客观的英语口语APP亲身测评,这3款软件让你的口语脱颖而出...