-3

During runtime, I want to download the latest DLL from the NuGet library using .NET Core and load it into the container.

I am looking at a scenario when a set of dependencies has been changed and new package versions are released to nuget.org, after which I will have to download the latest packages and run some tests. Each time the application will verify if it has the latest package version and, if necessary, download and dynamically load the new assembly without the application stopping.

Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68
Riby Varghese
  • 101
  • 1
  • 9
  • You know, that if a version of your dependency is changed - you need to recompile your app, right? – vasily.sib Apr 22 '19 at 09:36
  • The compiled version of the dll may not be compatible with the version of Net on your machine nor the version of Windows on your machine nor the Microprocessor on your machine. So you would have to download source and dynamically compile to ensure the code will run on your machine. – jdweng Apr 22 '19 at 09:50
  • Suppose you have class `A` that uses class `B` from `B.dll,Version=1.0.0.0`. Even though you download and switch (unload) `B.dll,Version=1.0.0.0` in your `AppDomain` to `B.dll,Version=2.0.0.0`, your `A` class still want to use class `B` from `B.dll,Version=1.0.0.0`. To change this, you need to stop your app, recompile it and run again. – vasily.sib Apr 22 '19 at 10:01
  • @vasily.sib That's like saying that using Newtonsoft.Json v12.0.1 will fail when I also use the Azure Blob Storage SDK, because it was compiled against Newtonsoft.Json v10.0.2. I'm not saying it's a good idea to do what the original question asked, but what you wrote is not a technical limitation that would prevent this from working. – zivkan Apr 22 '19 at 15:49
  • Possible duplicate of [Load nuget dependencies at runtime](https://stackoverflow.com/questions/31859267/load-nuget-dependencies-at-runtime) – Lance U. Matthews Apr 22 '19 at 17:01
  • It's not a good duplicate because the only useful answer uses `AppDomain`, which is only available in the .NET Framework, but this question is specifically about .NET Core, which doesn't support App Domain. Also, they use the NuGet.Core package, which is very old and replaced by multiple other packages. Hopefully the APIs didn't change much, but I wouldn't bet on the code working as-is with the new packages. – zivkan Apr 22 '19 at 19:15
  • @zivkan say Newtonsoft.Json v13.0.0 will add new optional string parameter to `JObject.SomeHeavilyUsedMethod()` - this will break Azure Blob Storage SDK and your app, until you update your dependencies and recompile. But you are right, it is not clear from my comment. – vasily.sib Apr 23 '19 at 03:03
  • @zivkan or another real example, that you can test right now - `Prism.Unity` package. Right now it is v7.1.0.431 and depends on `Unity.Container` v5.8.11, which in turn depends on `Unity.Abstractions` v3.3.1. Nuget Package Manager advise you to update `Unity.Abstractions` to v4.1.3 but if you do, this will break your app. – vasily.sib Apr 23 '19 at 03:19
  • In your previous comment you suggested that stopping the app and recompiling would fix the issue. If recompiling the issue would fix the problem, then there's no reason why hot reloading with appropriate binding redirects wouldn't work either. – zivkan Apr 23 '19 at 03:41

1 Answers1

3

I'll start off saying that this doesn't sound like a good idea. You didn't explain the scenario about why you want to do this, so we have no way of suggesting better approaches. If it's a web application that you don't want to have downtime for, a better approach might be to have a load balancer and when you release a new version of your web app, to configure the load balancer to send all new requests to the new contains/version while the existing connections on the old version drain.

If your app is a queue listener, you don't need hot-loading at all. Just have an app which creates a pull request on your source code to update nuget package version numbers when they're available, have the CI automatically build the change and if tests pass you can automate approving/merging the PR then your CD can automatically deploy and decommission the previous version. This would be less risky than automatically loading new versions since you risk the new version having bugs or not being binary compatible and now your app is going to crash.

Anyway, if you really do have a good reason to hot-reload assemblies, on .NET Core you'll need to use AssemblyLoadContext. However, it's such an unusual case to need this, that I can't find any realistic examples on how to use it. All the "examples" I've seen use AssemblyLoadContext.Default which I assume is equivilent to using a single context and therefore won't let you load different versions of the same assembly. So, if you want to go down this path, you're probably going to have to figure it out yourself. Lots of trial and error, debugging and possibly reading the coreclr source code. As some commenters to your question mentioned, you'll also need to handle the case that an assembly was compiled against one version of a dependency, but you're now going to load a different version. In the .NET Framework is uses something called Assembly Binding Redirect. The way most people use this is with an app.config or web.config file, but that won't work for you since it will change at runtime. I'm sure there's an API to deal with it programatically, which you'll also need to figure out. I'm not sure how/if binding redirects are different in .NET Core compared to the .NET Framework, so that's yet another thing you'll need to figure out, but I'm sure a couple of good searches on google will give you an answer to that.

Once you get assembly hot reloading working, you can either read the NuGet server API and implement your own client, or you can use the NuGet client SDK. But the NuGet Client team's primary focus are the tools (Visual Studio integration, dotnet cli, nuget.exe), they just publish the packages for convenience (plus as a way to share the libraries with dotnet cli and mono), so there are no docs on using the SDK. Also note that while the NuGet client team tries not to break the packages binary API, it's a secondary concern when needing to implement features and bug fixes for the tools. The NuGet client SDK track Visual Studio version number, it doesn't use semantic versioning, so when you try to update to newer NuGet client packages, you might find you need to change your code when you otherwise might not expect. It's pretty rare, but I generally recommend the "ports and adapters" pattern, and this is an excellent example of when it's particularly useful. At least with the NuGet client packages, there are blog posts from people who figured it out and shared how they did whatever they needed. Otherwise since NuGet itself is opensource, you can look though NuGet's code to see how it uses the packages internally, and use that as a guide to help you figure out what you want to do. If you go down this path you'll need to do most of what the nuget tools do:

  1. Query a NuGet feed to find what versions of a package are available, then choose which version you want to use and check if that version is already downloaded.
  2. Download and extract the package.
  3. Asset selection. Particularly when a package has libraries for multiple TFMs, you need to know which one to use based on your project's TFM
  4. The NuGet client at this stage would either write the project.assets.json file, used by the .NET SDK, or edit the packages.config and csproj files, depending if the project uses PackageReference or packages.config. In your case this is where you integrate with assembly hot-reload.

Note that in the general case, the process above, or depending on how you implement it a single step, could be recursive because a package can have dependencies. So, the dependencies also need to be retrieved, but a package can have different dependencies depending on which TFM is selected, so you need to figure out do you want to download all dependencies, even ones that will never be used after asset selection, or do you want to do asset selection first to minimise the packages that you download. Also when multiple packages have a dependency on different versions of a single package, you need to make a decision about which version of the package you want to use.

So, that's the high level algorithm you'll need to implement. As I mentioned, most of this doesn't have API docs with examples on exactly how to implement it, and very few, if any, examples exist on the internet. Like I wrote in the first paragraph, I don't think this is a good idea. It's probably easier to automate your CI/CD pipelines, and if necessary automate changes to a load balancer's configuration, rather than making such a complex app. Sometimes orchestrating a bunch of simple apps is easier than making a complex app.

zivkan
  • 12,793
  • 2
  • 34
  • 51