如何让 .NET Core 程序的入口点位于它引用的 NuGet 包中
How can I have the entry point to a .NET Core program be in a NuGet package it references
我正在构建一个作业-运行ning 系统,其中有一个 .NET Core 控制台应用程序作为作业 运行ner,以及用户可扩展的作业 - 用户可以参考我的 NuGet 包,写class 扩展了我的 Job 类型,并实现了 Execute 方法。我想在 NuGet 包中提供所有控制台逻辑,以便引用它可以构建一个现成的 运行 控制台应用程序。我已经能够使用 NuGet 发布构建应用程序所需的构建目标,但我正在寻找一种方法来实际加载作业 dll。
我目前的解决方案是:
- Job-运行ner 通过反射加载其工作目录中的所有 dll
- Job-运行ner 检查所有这些 dll 以寻找实现所需类型的 dll
- Job-运行ner创建作业类型的实例并执行作业
我想避免第 1 步,我相信如果 NuGet 包只是为控制台应用程序提供一个入口点,我就能够做到这一点。是否可以构建一个没有入口点的控制台应用程序并通过 NuGet 提供它,或者使用 class 库并将所有必需的 dll 加载到一个应用程序域中?
如果我正确理解你有以下项目:
- Runner - 用于执行作业
- JobTypeBaseClass - common class 可由 Jobs(定义如下)和 Runner
使用
- 作业 - 完成由不同开发人员定义的工作的任务
目前作业正在引用 JobTypeBaseClass 和 Runner,使用反射执行其中定义的工作。
这是广为人知的正确方法。
但据我了解,您的目标是为开发人员提供开始工作的方式,他们将非常容易地创造工作。所以他们应该引用 nuget,就像它应该工作一样。
回答您的问题:没有入口点就无法构建控制台应用程序,因此没有静态 Main 方法。但是这个要求可以被你使用。在 nuget 文件中,您可以添加安装脚本,这将更改 Program.cs 文件的 Main 方法,并添加将调用所需代码的行。
这种方式在安装nuget job后就可以了。当然,它不会做任何事情。
所以你的 nuget 应该:
- 包含 JobTypeBaseClass
- 包含将创建新 Job.cs 文件的脚本,该文件将 class 从 JobTypeBaseClass
实现基础
- 包含将更改 Program.cs 中的 Main 方法的脚本,该脚本将调用上面创建的方法。
你愿意为此付出多少努力?
Windows 知道可执行文件的入口点位于文件 PE header 中 _IMAGE_OPTIONAL_HEADER 结构的 AddressOfEntryPoint 字段中的方式。我不是专家,但我相信 dotnet CLI 也使用它。由于 AddressOfEntryPoint 字段只是文件中的一个字节偏移量,因此入口点不可能位于不同的文件中。
它可以工作的一种方式(我还没有测试过)是如果你的作业 运行ner 是一个 exe,而不是 nupkg 中的 dll。这样,当每个作业构建时(如有必要,使用 dotnet cli,也已发布),输出文件夹将包含作业 运行ner exe,即 运行。作业本身可以是一个 dll,因此它不需要自己的入口点。但是你的工作 运行ner 仍然需要使用反射来加载所有程序集,我同意这是个坏主意。它还会阻止 dotnet run
处理作业任务项目,这使得执行作业的人员调试起来更加困难。
另一种选择是告诉作业任务实施者在他们的 exe 文件中包含一行 Main 方法
public static void Main()
{
JobRunner.Run();
}
这也需要 JobRunner 使用反射来查找作业,但至少您可以使用 Assembly.GetCallingAssembly()
或 Assembly.GetEntryAssembly()
来避免加载所有 dll。您可以将 JobRunner.Run()
更改为 JobRunner.Run(Type)
或 JobRunner.Run<T>() where T : IJob
,以便工作实施者在他们自己的 Main 方法中准确地告诉工作 运行 哪个工作要 运行 并避免工作中的第 2 步 运行ner。它与 ASP.NET 核心应用程序具有相似(如果不完全相同)Program.Main 的方式相似,因此这并非前所未有。
如果你想让你的工作实施者尽可能轻松,你可以从 .NET Core 如何在 obj/$(Configuration)/
文件夹中创建临时 .cs 文件中获得灵感。创建您的 nuget 包 with the appropriate msbuild .targets file 并让您的自定义 msbuild 目标文件在 obj 文件中创建另一个临时 .cs 文件,其中包含 Main() 入口点,并包含用于编译的 .cs 文件。
为了加分,您可以使用 Roslyn 分析项目中的所有 non-temporary .cs 文件,找到所有作业,然后在生成的 .cs 文件中使用类型调用您的 JobRunner 的 Main 方法( s) 检测到,因此在 运行 时不需要使用反射来执行步骤 2。
我正在构建一个作业-运行ning 系统,其中有一个 .NET Core 控制台应用程序作为作业 运行ner,以及用户可扩展的作业 - 用户可以参考我的 NuGet 包,写class 扩展了我的 Job 类型,并实现了 Execute 方法。我想在 NuGet 包中提供所有控制台逻辑,以便引用它可以构建一个现成的 运行 控制台应用程序。我已经能够使用 NuGet 发布构建应用程序所需的构建目标,但我正在寻找一种方法来实际加载作业 dll。
我目前的解决方案是:
- Job-运行ner 通过反射加载其工作目录中的所有 dll
- Job-运行ner 检查所有这些 dll 以寻找实现所需类型的 dll
- Job-运行ner创建作业类型的实例并执行作业
我想避免第 1 步,我相信如果 NuGet 包只是为控制台应用程序提供一个入口点,我就能够做到这一点。是否可以构建一个没有入口点的控制台应用程序并通过 NuGet 提供它,或者使用 class 库并将所有必需的 dll 加载到一个应用程序域中?
如果我正确理解你有以下项目:
- Runner - 用于执行作业
- JobTypeBaseClass - common class 可由 Jobs(定义如下)和 Runner 使用
- 作业 - 完成由不同开发人员定义的工作的任务
目前作业正在引用 JobTypeBaseClass 和 Runner,使用反射执行其中定义的工作。 这是广为人知的正确方法。
但据我了解,您的目标是为开发人员提供开始工作的方式,他们将非常容易地创造工作。所以他们应该引用 nuget,就像它应该工作一样。
回答您的问题:没有入口点就无法构建控制台应用程序,因此没有静态 Main 方法。但是这个要求可以被你使用。在 nuget 文件中,您可以添加安装脚本,这将更改 Program.cs 文件的 Main 方法,并添加将调用所需代码的行。
这种方式在安装nuget job后就可以了。当然,它不会做任何事情。
所以你的 nuget 应该:
- 包含 JobTypeBaseClass
- 包含将创建新 Job.cs 文件的脚本,该文件将 class 从 JobTypeBaseClass 实现基础
- 包含将更改 Program.cs 中的 Main 方法的脚本,该脚本将调用上面创建的方法。
你愿意为此付出多少努力?
Windows 知道可执行文件的入口点位于文件 PE header 中 _IMAGE_OPTIONAL_HEADER 结构的 AddressOfEntryPoint 字段中的方式。我不是专家,但我相信 dotnet CLI 也使用它。由于 AddressOfEntryPoint 字段只是文件中的一个字节偏移量,因此入口点不可能位于不同的文件中。
它可以工作的一种方式(我还没有测试过)是如果你的作业 运行ner 是一个 exe,而不是 nupkg 中的 dll。这样,当每个作业构建时(如有必要,使用 dotnet cli,也已发布),输出文件夹将包含作业 运行ner exe,即 运行。作业本身可以是一个 dll,因此它不需要自己的入口点。但是你的工作 运行ner 仍然需要使用反射来加载所有程序集,我同意这是个坏主意。它还会阻止 dotnet run
处理作业任务项目,这使得执行作业的人员调试起来更加困难。
另一种选择是告诉作业任务实施者在他们的 exe 文件中包含一行 Main 方法
public static void Main()
{
JobRunner.Run();
}
这也需要 JobRunner 使用反射来查找作业,但至少您可以使用 Assembly.GetCallingAssembly()
或 Assembly.GetEntryAssembly()
来避免加载所有 dll。您可以将 JobRunner.Run()
更改为 JobRunner.Run(Type)
或 JobRunner.Run<T>() where T : IJob
,以便工作实施者在他们自己的 Main 方法中准确地告诉工作 运行 哪个工作要 运行 并避免工作中的第 2 步 运行ner。它与 ASP.NET 核心应用程序具有相似(如果不完全相同)Program.Main 的方式相似,因此这并非前所未有。
如果你想让你的工作实施者尽可能轻松,你可以从 .NET Core 如何在 obj/$(Configuration)/
文件夹中创建临时 .cs 文件中获得灵感。创建您的 nuget 包 with the appropriate msbuild .targets file 并让您的自定义 msbuild 目标文件在 obj 文件中创建另一个临时 .cs 文件,其中包含 Main() 入口点,并包含用于编译的 .cs 文件。
为了加分,您可以使用 Roslyn 分析项目中的所有 non-temporary .cs 文件,找到所有作业,然后在生成的 .cs 文件中使用类型调用您的 JobRunner 的 Main 方法( s) 检测到,因此在 运行 时不需要使用反射来执行步骤 2。