Open MaxwellDAssistek opened 1 month ago
Maybe you can file a draft PR to improve it on Avalonia side as you seem to already have an idea where to look and also you have samples to test. If the draft is accepted, you may add a unit test and make it a full PR.
At this point, I'm not even really sure how to begin. Is the approach to create some kind of ObserverRegistry to keep track of all known observers? Maybe there is a better solution? I'm no expert in Avalonia internals. I feel like this is a bit too serious of a design decision to just throw into a draft PR.
Yes, I also really hope to completely uninstall the UserControl. I've found that after each page display, it cannot truly be deleted from memory.
Is your feature request related to a problem? Please describe.
We need to be able to dynamically load, unload and re-load modules containing UserControls throughout the lifecycle of our application. Although issue #13935 was recently closed and we did use the
Unloading
event handler suggested in the sample, it only fixes things for the simplest of modules. As soon as anything even slightly more complex is added, the assembly no longer totally unloads.The biggest issue at first glance is Observables. We need a way to disconnect all observable listeners belonging to an assembly. For example, lets take a rather trivial library like AvaloniaProgressRing, which is basically all implemented using animation styles. The issue is that the animation system itself connects to Observables that get stuck even though the whole UserControl, which contains the progress ring in this case, is removed from the window before unloading the AssemblyLoadContext. Here is what I can see in dotMemory some time after the unload happens:
Describe the solution you'd like
It seems that there is still some more work to be done to make sure we can unload the assembly.
Describe alternatives you've considered
If I try to ignore the memory consequences of not having the module totally unloaded, I encounter another issue. I actually can't even reload any of the module's assemblies later because all
avares
references are still pointing to the old half-unloaded assembly due to how the AssemblyDescriptorResolver functions:https://github.com/AvaloniaUI/Avalonia/blob/b8d8fda4eab0313fe26c5c3b574673d2b5c12521/src/Avalonia.Base/Platform/Internal/AssemblyDescriptorResolver.cs#L28-L29
The problem is that it just gets a list of all assemblies in the entire AppDomain and finds the first assembly with the requested name. The problem is that the half-unloaded assembly is the first one that it finds.
Since AssemblyDescriptorResolver is an internal Avalonia class that I can't extend, the only way I was able to work around the problem is using Harmony to hot-patch the GetAssembly method with a version that resolves assemblies through the correct AssemblyLoadContext.
This is obviously a very bad and fragile workaround, so it would be nice to get the module to fully unload in the first place.
Additional context
No response