C#脚本化(Roslyn):如何在C#脚本中引入nuget包
假设我们开发了一个C#脚本编辑器,利用Roslyn去执行用户所编写的脚本。这时候,如果用户想要引用一个nuget包,应该如何实现呢?
我们想要引用nuget包的话,只要能得到nuget包及其依赖包的所有程序集和资源文件就可以了。如何引用程序集,可以看这一篇:使用Roslyn脚本化C#时如何调用不包含在运行时中的程序集
朴素思路
一种朴素的思路是:下载所需的nuget包,然后从nuget包中解压出程序集和其他资源文件,再遍历nuget包的所有依赖,执行同样的操作,最后得到程序集,就可以让Roslyn来引用它们了。
这个方法思路简单清晰,但同时,操作起来也比较复杂,要处理好两个问题:
- nuget包的所有输出都要放到正确的目录下
- 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):如何在C#脚本中引入nuget包的更多相关文章
- 如何在Bash脚本中引入alias
更多精彩内容,请关注微信公众号:后端技术小屋 alias的使用 在日常开发中,为了提高运维效率,我们会用alias(命令别名)来定义命令的简称.比如在~/.bash_profile中添加: alias ...
- 【idea】idea如何在maven工程中引入jar包
在pom.xml文件中引入所有代码包后,项目右键--maven--reimport </dependencies>
- 转:如何在 LoadRunner 脚本中做关联 (Correlation)
如何在 LoadRunner 脚本中做关联 (Correlation) 当录制脚本时,VuGen会拦截client端(浏览器)与server端(网站服务器)之间的对话,并且通通记录下来,产生脚本.在V ...
- 如何在Python脚本中调用外部命令(就像在linux shell或Windows命令提示符下输入一样)
如何在Python脚本中调用外部命令(就像在linux shell或Windows命令提示符下输入一样) python标准库中的subprocess可以解决这个问题. from subprocess ...
- 技术分享:如何在PowerShell脚本中嵌入EXE文件
技术分享:如何在PowerShell脚本中嵌入EXE文件 我在尝试解决一个问题,即在客户端攻击中只使用纯 PowerShell 脚本作为攻击负荷.使用 PowerShell 运行恶意代码具有很多优点, ...
- 2.Jmeter 如何在jsr223 脚本中停止测试任务
Jmeter 如何在jsr223 脚本中停止测试任务 在可以直接引用ctx的变量的processor中可以执行如下脚本即可. (例如jsr223 postprocessor中) ctx.getEngi ...
- 如何在batch脚本中嵌入python代码
老板叫我帮他测一个命令在windows下消耗的时间,因为没有装windows那个啥工具包,没有timeit那个命令,于是想自己写一个,原理很简单: REM timeit.bat echo %TIME% ...
- 如何在VBS脚本中显示“选择文件对话框”或“选择目录对话框”
.选择文件[XP操作系统,不能用于Win2000或98],使用“UserAccounts.CommonDialog”对象向用户显示一个标准的“文件打开”对话框 Set objDialog = Crea ...
- 如何在shell脚本中导出数组供子进程使用
功能说明:设置或显示环境变量. 语 法:export [-fnp][变量名称]=[变量设置值] 补充说明:在shell中执行程序时,shell会提供一组环境变量.export可新增,修改或删除环境变量 ...
- 如何在shell脚本中获取当前用户名?
答:使用环境变量USER即可 如在脚本中打印当前用户名; #!/bin/sh echo "user name = ${USER}"
随机推荐
- prometheus表达式常用公式
1. _over_time() 下面的函数列表允许传入一个区间向量,它们会聚合每个时间序列的范围,并返回一个瞬时向量 avg_over_time(range-vector) : 区间向量内每个度量指标 ...
- WebSocket网络通信
WebSocket 网络通信 导入依赖: <!-- WebSocket依赖 --> <dependency> <groupId>log4j</groupId& ...
- 直播预约丨《袋鼠云大数据实操指南》No.4:数据服务API实战解读,助力企业数字化跃迁
近年来,新质生产力.数据要素及数据资产入表等新兴概念犹如一股强劲的浪潮,持续冲击并革新着企业数字化转型的观念视野,昭示着一个以数据为核心驱动力的新时代正稳步启幕. 面对这些引领经济转型的新兴概念,为了 ...
- C#常规操作
线程池:ThreadPool.SetMaxThreads(辅助线程数,活跃线程数) ThreadPool.SetMinThreads(最小空闲辅助线程,最小空闲线程) ********* 检查是否按下 ...
- Java源码分析系列笔记-18.Semaphore
目录 1. 是什么 2. 原理分析 2.1. uml 3. 公平信号量 3.1. 是什么 3.2. 使用 3.3. 原理分析 3.3.1. 构造方法 3.3.1.1. 公平Sync 3.3.2. ac ...
- CF958E1 题解
Problem 原题链接 Meaning 在二维平面内,有位置不同且不存在三点共线的 \(R\) 个红点和 \(B\) 个黑点,判断是否能用一些互不相交的线段连接每一个点,使得每条线段的两端都分别是黑 ...
- Window 下bat管理员身份运行文件
@echo off%1 mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe&qu ...
- wifi转串口的模块
wifi转串口的模块ZLSN7046T是上海卓岚生产的一款多功能wifi转串口模块.它能够将wifi信号转化为串口信号,且支持多种功能,邮票孔封装,体积小巧可以外置天线或者内置天线.ZLAN7046T ...
- 窗口小部件基础编写V1.0----没有Service
实现窗口小部件,访问手机储存卡指定目录中的图片文件,然后随机选择一张在窗口的小部件中显示.图片路径使用List存储,适合初级Android学习者参考.本系统无服务,不能保证进程长存. 新建一个空的布局 ...
- react开发组件并发包到npm
Toast组件 import ReactDomCli from 'react-dom/client'; import './style.css' import React from 'react'; ...