假设我们开发了一个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. 【Java】可比较泛型建数组传递报强转类型错误解决方案

    问题 可比较泛型怎么新建数组? 自己写基于AVL树的散列表时,在自动扩容的时候需要遍历AVL树的Key,所以需要AVL树提供一个方法返回一个Key数组以遍历,初始实现如下: /** * 用于辅助遍历K ...

  2. 【杂谈】死锁?NO,时间跳跃!

    在日常开发或线上运维中,我们经常会遇到各种数据库异常,例如超时.死锁等.但有些问题,表面看似平常,背后却藏着意想不到的原因. 今天就分享一次由服务器时间跳跃引发的 MySQL 获取锁超时问题的排查过程 ...

  3. Java高效合并Excel报表实战:GcExcel让数据处理更简单

    前言:为什么需要自动化合并Excel? 在日常办公场景中,Excel报表合并是数据分析的基础操作.根据2023年企业办公效率报告显示: 财务人员平均每周花费6.2小时在Excel合并操作上 人工合并的 ...

  4. Spring基于注解的事务管理

    Spring基于注解的事务管理 源码 代码测试 pom.xml <?xml version="1.0" encoding="UTF-8"?> < ...

  5. SpringCloud——自定义断言工厂

    目录 场景:用户的请求头中需要有指定的用户名和密码才能访问. 断言工厂 参考系统AfterRoutePredicateFactory写法. package com.zjw.factory; impor ...

  6. 鸿蒙NEXT开发实战教程—小红书app

    幽蓝君最近发现小红书是个好东西,一定要多逛 今天就浅浅模仿一下小红书app,主要是底部tab栏和主页部分. 首先看一下tabbar,由于中间有一个红色按钮的存在,所以这里我使用自定义导航栏来实现,自定 ...

  7. 智表 ZCELL 插件快速入门指南(原创)

    一.认识智表 ZCELL 插件 智表 ZCELL 是一款免费的前端 EXCEL 插件,它凝聚了多年工作经验与成果,旨在为用户带来极致的操作体验.该插件具备诸多优势,如轻量体积小,在浏览器端效率高:使用 ...

  8. 操作系统:Linux如何实现进程与进程调度

    Linux如何表示进程 在Cosmos中,设计了一个thread_t数据结构来代表一个进程,Linux也同样是用一个数据结构表示进程. Linux进程的数据结构 在Linux系统下,把运行中的应用程序 ...

  9. 我的白板工具支持 Markdown 转思维导图啦!

    朋友们好,我的开源在线白板工具又更新啦,这次支持了 markdown 文本转思维导图的功能,可以将多级标题.多级列表等转化为思维导图,这次对于 markdown 转思维导图功能的主要识别父子关系 + ...

  10. GStreamer开发笔记(四):ubuntu搭建GStreamer基础开发环境以及基础Demo

    前言   本篇开始gstreamer的编程学习,先搭建基础的环境,跑通一个基础的Demo对GStreamer编程有个初步的了解.   Demo         GStreamer   GStreame ...