<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于 Roslyn 實(shí)現(xiàn)代碼動(dòng)態(tài)編譯

          共 3901字,需瀏覽 8分鐘

           ·

          2022-06-01 19:26

          基于 Roslyn 實(shí)現(xiàn)代碼動(dòng)態(tài)編譯

          Intro

          之前做的一個(gè)數(shù)據(jù)庫小工具可以支持根據(jù) Model 代碼文件生成創(chuàng)建表的 sql 語句,原來是基于 CodeDom 實(shí)現(xiàn)的,后來改成使用基于 Roslyn 去做了。

          實(shí)現(xiàn)的原理在于編譯選擇的Model 文件生成一個(gè)程序集,再從這個(gè)程序集中拿到 Model (數(shù)據(jù)庫表)信息以及屬性信息(數(shù)據(jù)庫表字段信息),拿到數(shù)據(jù)庫表以及表字段信息之后就根據(jù)數(shù)據(jù)庫類型生成大致的創(chuàng)建表的 sql 語句。

          最近做的一個(gè)小工具 dotnet-exec 也是類似的,將代碼編譯成一個(gè)程序集并通過反射的方式執(zhí)行代碼邏輯,

          分享一下用到的一些代碼

          Sample

          來看一個(gè)最簡單的編譯一段文本為程序集示例:

          //?分析語法樹
          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()
          ????.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();
          //?生成編譯結(jié)果并導(dǎo)出程序集信息到?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()
          ????.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 引用合并,可以參考下面的將多個(gè)文件代碼合并成一段文本,后來發(fā)現(xiàn)自己傻了,改成了上面的用法,直接生成多個(gè)語法樹再生成編譯,推薦使用上面的方式,會(huì)更加的友好和

          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)

          默認(rèn)地編譯會(huì)編譯成一個(gè) dll 程序集,如果包含 Main 方法要生成一個(gè)可執(zhí)行程序可以通過指定 CSharpCompilationOptionsOutputKindOutputKind.ConsoleApplication, 還有很多可以配置的選項(xiàng),有需要可以自己探索一下

          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


          瀏覽 96
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美久久久久久久久久 | 在线视频1区 | 成人精品视频网站 | 久久青青草香蕉手机视频在线 | 欧美激情爱爱 |