假设我们开发了一个C#脚本编辑器,利用Roslyn去执行用户所编写的脚本。这时候,如果用户想要引用一个nuget包,应该如何实现呢?

我们想要引用nuget包的话,只要能得到nuget包及其依赖包的所有程序集和资源文件就可以了。如何引用程序集,可以看这一篇:使用Roslyn脚本化C#时如何调用不包含在运行时中的程序集

朴素思路

一种朴素的思路是:下载所需的nuget包,然后从nuget包中解压出程序集和其他资源文件,再遍历nuget包的所有依赖,执行同样的操作,最后得到程序集,就可以让Roslyn来引用它们了。

这个方法思路简单清晰,但同时,操作起来也比较复杂,要处理好两个问题:

  1. nuget包的所有输出都要放到正确的目录下
  2. nuget包的依赖不能有遗漏

另一种解法

在引用nuget包来编译项目时,nuget相关的程序集都会输出到输出目录下,基于这一点,对本文所述的问题就有另一种解法。这个方法的主要思路是,创建一个空的C#项目,然后在这个项目中引用在C#脚本中需要调用的nuget包,再用dotnet.exe去编译这个项目,那么nuget包及其依赖项所包含的所有程序集和资源文件都会输出到输出目录下面去,那么我们在这个目录下面去执行C#脚本,自然就能够找到所有所需的程序集和资源文件了。

下面我们来详细说明一下这个方法的具体流程。

第一步 创建辅助C#项目

我们需要一个空的C#项目,来辅助我们获取我们所需的nuget包相关的文件。

首先根据csproj文件的模板创建一个新的csproj文件,通过PackageReference标签引用我们想要引入的nuget包。OutputType设置为Exe,这样被引用的nuget的程序集才会输出到目录。

<Project Sdk="Microsoft.NET.Sdk">
<OutputType>Exe</OutputType>
...
<ItemGroup>
<PackageReference Include="newtonsoft.json" Version="13.0.3" />
</ItemGroup>
...
</Project>

然后,像普通项目一样,添加一个Program.cs文件,在文件里写一些简单的代码,注意不要引入其他不必要的包。例如:

using System;

namespace Application
{
class Program
{
static void Main(string[] args)
{
_ = 0;
}
}
}

如果我们选择的.NET和C#版本支持顶级语句和global using特性,那么也可以只写

_ = 0;

第二步 调用dotnet编译

我们可以使用dotnet.exe去编译这个辅助项目,命名如下

dotnet build <csproj file path>

编译成功后,输出目录下面就能找到nuget包输出的程序集和资源文件,下面以一个既有依赖包,又有资源文件的nuget包为例,可以看到,输出目录下包含了nuget包本身的程序集、资源文件,以及依赖包的程序集和资源文件。

我们得到了这些程序集,就可以引用它们了,效果跟我们引用nuget包一样。

代码实战

下面用一个简化版的代码来验证这一方法,在demo中我们通过Rosly来执行一段使用Newtonsoft.Json来执行序列化的C#脚本。

using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting; namespace ConsoleApp1
{
internal class Program
{
static void Main(string[] args)
{
var refs = LoadNuget("Newtonsoft.Json", "13.0.3").GetAwaiter().GetResult(); Console.WriteLine($"Reference assembly:\n"); foreach (var @ref in refs)
{
Console.WriteLine(@ref);
}
Console.WriteLine("\n"); var code2 = @"
using Newtonsoft.Json; record Person(string Name, int Age); var p = new Person(""Jack"", 19); var str = JsonConvert.SerializeObject(p); System.Console.WriteLine(str); "; Console.WriteLine("Script result:\n");
try
{
var metas = refs.Select(r => MetadataReference.CreateFromFile(r));
var options = ScriptOptions.Default.AddReferences(metas);
CSharpScript.Create(code2, options).RunAsync().GetAwaiter().GetResult();
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
} public static async Task<IEnumerable<string>> LoadNuget(string package_id, string version)
{
Directory.CreateDirectory(".restore"); //创建dotnet控制台项目
await Process.Start(@"C:\Program Files\dotnet\dotnet.exe", "new console -o .restore\\program").WaitForExitAsync(); //添加nuget包引用
await Process.Start(@"C:\Program Files\dotnet\dotnet.exe", $"add .restore\\program package {package_id} -v {version}").WaitForExitAsync(); //编译
await Process.Start(@"C:\Program Files\dotnet\dotnet.exe", "build .restore\\program --interactive --nologo -o .restore\\program\\bin").WaitForExitAsync(); //找出输出路径下的dll
var dlls = Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), ".restore\\program\\bin"), "*.dll");
return dlls.Where(d => !Path.GetFileName(d).ToLower().Equals("program.dll"));
}
}
}

输出结果如下:

Reference assembly:

D:\Projects\CSharp Projects\RebootAndScript\ConsoleApp1\bin\Debug\net8.0\.restore\program\bin\Newtonsoft.Json.dll

Script result:

{"Name":"Jack","Age":19}

C#脚本化(Roslyn):如何在运行时引入nuget包的更多相关文章

  1. 深入理解脚本化CSS系列第四篇——脚本化样式表

    × 目录 [1]CSSStyleSheet [2]CSSRule 前面的话 关于脚本化CSS,查询样式时,查询的是计算样式:设置单个样式时,设置的是行间样式:设置多个样式时,设置的是CSS类名.脚本化 ...

  2. Spark on Yarn运行时加载的jar包

    spark on yarn运行时会加载的jar包有如下: spark-submit中指定的--jars $SPARK_HOME/jars下的jar包 yarn提供的jar包 spark-submit通 ...

  3. iNeuOS工业互联网操作系统,脚本化实现设备运行时长和效率计算与统计

    目       录 1.      概述... 2 2.      实时采集开停状态... 2 3.      增加虚拟设备... 2 4.      脚本统计和计算设备运行时长... 4 5.    ...

  4. 使用Roslyn脚本化C#代码,C#动态脚本实现方案

    [前言] Roslyn 是微软公司开源的 .NET 编译器. 编译器支持 C# 和 Visual Basic 代码编译,并提供丰富的代码分析 API. Roslyn不仅仅可以直接编译输出,难能可贵的就 ...

  5. JavaScript 客户端JavaScript之 脚本化浏览器窗口

    1.计时器 客户端Javascript以全局函数setTimeOut().clearTimeOut().setInterval().clearInterval()提供这一功能.   前者是从运行的那一 ...

  6. JavaScript 客户端JavaScript之 脚本化文档

    客户端JavaScript的存在把静态HTML转变为交互式的Web应用程序,脚本化Web页面的内容正是JavaScript存在的理由.   一个文档对象模型或者说DOM就是一个API,它定义了如何访问 ...

  7. 权威指南之脚本化jquery

    jqury函数 jquery()($())有4种不同的调用方式 第一种是最常用的调用方式是传递css选择器(字符串)给$()方法.当通过这种方式调用时,$()方法会返回当前文档中匹配该选择器的元素集. ...

  8. Javascript学习7 - 脚本化浏览器窗口

    原文:Javascript学习7 - 脚本化浏览器窗口 本节讨论了文档对象模型.客户端Javascript下Window中的各项属性,包括计时器.Location对象.Histroy对象.窗口.浏览器 ...

  9. Java 脚本化编程指南

    Java 脚本化编程指南 Java脚本化API为谁准备? 脚本语言的一些有用的特性是: 方便:大多数脚本语言都是动态类型的.您通常可以创建新的变量,而不声明变量类型,并且您可以重用变量来存储不同类型的 ...

  10. JavaScript权威指南--脚本化CSS

    知识要点 客户端javascript程序员对CSS感兴趣的是因为样式可以通过脚本编程.脚本化css启用了一系列有趣的视觉效果.例如:可以创建动画让文档从右侧“滑入”.创造这些效果的javascript ...

随机推荐

  1. 学习unigui【26】关于Unigui控件的个性化CSS框架

    用unigui写程序,简单的事用户界面不用HTML.因为这个太闹心了.没有什么技术含量,但很闹心. 但是,但是不清楚HTML CSS更闹心.因为养眼的界面是客户起步疑问和要求. 强烈推荐认真读明白大虾 ...

  2. STM32 调试小结

    图片1相关内容 确认芯片连接 使用keil软件打开一个STM32工程文档,编译,无报错 点击魔术棒,弹出配置界面"option for target XXXXX" 点击配置界面的D ...

  3. C# LINQ 快速入门实战指南,建议收藏学习!

    前言 因为咱们的.NET EF Core快速入门实战教程经常会用到 LINQ 去查询和操作 MySQL 中的数据,因此我觉得很有必要对 LINQ 的一些使用技巧.常用方法.特性做一个详细的介绍,让大家 ...

  4. ElasticStack从入门到精通

    什么是ElasticStack ElasticStack早期名称为elk elk代表了三个组件 ElasticSearch 负责数据存储和检索. Logstash 负责数据的采集,将源数据采集到Ela ...

  5. mybatis的输入参数类型

    一.传递简单数据类型 二.传入一个bean对象 三.传入一个包装对象(对象中存放对象)

  6. LeetCode 热题 100

    1. 两数之和 1. 两数之和 class Solution { public int[] twoSum(int[] nums, int target) { int n = nums.length; ...

  7. JVM 垃圾回收调优的主要目标是什么?

    JVM 垃圾回收调优的主要目标 JVM 垃圾回收调优的目标是为了提升应用的性能,优化垃圾回收过程中的停顿时间和吞吐量.调优的核心目标通常包括以下几点: 1. 减少垃圾回收的停顿时间 停顿时间(Stop ...

  8. 『Plotly实战指南』--样式定制基础篇

    在数据可视化的世界中,一个精心设计的图表不仅能准确传达信息,还能提升整体的专业性和吸引力. 而Plotly作为Python中强大的可视化库,提供了丰富的样式定制功能,帮助我们轻松实现这一目标. 本文从 ...

  9. Missing Semester

    计算机教育中缺失的一课 The Missing Semester of Your CS Education 文档:https://missing-semester-cn.github.io/ 网课:h ...

  10. 2docker私有镜像仓库registry

    3 docker私有镜像仓库 3.1准备服务器 重新安装CENTOS7,加大磁盘空间. ip 配置 登录 192.168.168.168 4核虚拟CPU/4G内存/300G磁盘 22/密钥登陆/LCZ ...