简体   繁体   中英

How can I have the entry point to a .NET Core program be in a NuGet package it references

I am building a job-running system that has one .NET Core console app as a job runner, and user-extensible jobs - users can reference my NuGet package, write a class that extends my Job type, and implement the Execute method. I want to provide all the console logic in the NuGet package such that referencing it will allow for building a ready-to-run console app. I've been able to use NuGet to publish the required build targets to build the app, but I'm looking for a way to actually load the job dll.

My current solution is:

  1. Job-runner loads all dlls in its working directory via reflection
  2. Job-runner inspects all those dlls for one that implements the required type
  3. Job-runner creates an instance of the job type and executes the job

I would like to avoid step 1, which I believe I would be able to do if the NuGet package simply provided an entry point for a console app. Is it possible to either build a console app without an entry point and supply it via NuGet, or use a class library and have all the requisite dlls loaded in one appdomain?

If I correctly understdood you have following projects:

  • Runner - which is used to execute Jobs
  • JobTypeBaseClass - common class which can be used by Jobs (defined below) and Runner
  • Jobs - tasks which are doing the work defined by different developers

在此处输入图片说明

Currently Jobs are referencing JobTypeBaseClass and Runner using reflection execute work defined in it. This is the correct and wide known approach.

But what I understood your target is to give developers which will be creating the jobs very easily way of starting work. So they should reference the nuget and just like it it should work.

Answering your question: It is not possible to build console app without entry point so without static Main method. But this requirement can be used by you. In the nuget file you can add Install script which will change Main method of the Program.cs file and add line which will invoke required code.

This way after installing nuget job will work. Of course it won't do anything.

在此处输入图片说明

So your nuget should:

  • contain JobTypeBaseClass
  • contain script which will create new Job.cs file which will be class which implements base from JobTypeBaseClass
  • contain script which will change Main method in the Program.cs which will invoke method created above.

How much effort are you willing to go to for this?

The way that Windows knows where the entry point for an executable is in the AddressOfEntryPoint field of the _IMAGE_OPTIONAL_HEADER structure in the PE header of the file. I'm not an expert, but I believe that the dotnet CLI uses this as well. Since the AddressOfEntryPoint field is simply a byte offset in the file, it's impossible to have the entry point in a different file.

One way it could work (I haven't tested) is if your job runner is an exe, not a dll, within the nupkg. That way when each job builds (and if necessary using the dotnet cli, also published), the output folder will contain the job runner exe that is run. The job itself can then be a dll, so that it doesn't need its own entry point. But your job runner will still need to use reflection to load all assemblies, which I agree is a bad idea. It also prevents dotnet run from working on the job task project, which makes debugging much harder for people implementing jobs.

Another option is just to tell job task implementors to include a one line Main method in their exe

public static void Main()
{
  JobRunner.Run();
}

This also requires the JobRunner to use reflection to find jobs, but at least you could use Assembly.GetCallingAssembly() or Assembly.GetEntryAssembly() to avoid loading all dlls. You could change JobRunner.Run() to JobRunner.Run(Type) , or JobRunner.Run<T>() where T : IJob so the job implementors tell the job runner exactly which job to run in their own Main method and avoid step 2 in your job runner. It's similar to how ASP.NET Core applications have a similar, if not identical, Program.Main, so it's not exactly unprecedented.

If you want to make it as easy as possible for your job implementors, you could take inspiration in how .NET Core creates temporary .cs files in the obj/$(Configuration)/ folders. Create your nuget package with the appropriate msbuild .targets file and have your custom msbuild target file create another temporary .cs file in the obj file, which contains the Main() entry point, and have the .cs file included for compilation.

For extra credit, you could use Roslyn to analyse all the non-temporary .cs files in the project, find all the jobs, then in your generated .cs file call your JobRunner's Main method with the type(s) detected, so at run time it doesn't need to use reflection to do step 2.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM