Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AssemblyLoadContext.Unload crashes VS2019 debug but runs ok stand-alone #27810

Open
MV10 opened this issue Nov 11, 2019 · 7 comments
Open

AssemblyLoadContext.Unload crashes VS2019 debug but runs ok stand-alone #27810

MV10 opened this issue Nov 11, 2019 · 7 comments

Comments

@MV10
Copy link

@MV10 MV10 commented Nov 11, 2019

I initially posted this over in the ASP.NET Core repo since this involves Blazor server-side, Razor Components and Razor Class Libraries. They suggested I repost here but I certainly understand if this is too far off the map to be supportable. The original issue I posted:


I have a scenario that causes VS2019 16.3.6 with Core 3.0 to crash with HRESULT 0x80070057 (e_invalidarg) in debug, but the same program runs stand-alone indefinitely without problems. Since this involves Blazor, Razor Components in a Razor Class Library, AssemblyLoadContext, and VS, I'm not really sure if it belongs here, or CoreFX, or as a VS issue. Also, I realize I'm way off the map using ALC, reflection against private members, and so on.

Repository here: https://github.com/MV10/BlazorDynamicRCL

To use the repo you'll need to edit the path to the DLL in BlazorApp's NavMenu.razor file (explained below).

The goal was on-demand runtime loading and unloading of Razor Components from Razor Class Libraries into a Blazor server-side app. As occasionally mentioned by others, for large portal-style enterprise apps this is practically a requirement to avoid building and deploying unwieldy "one big compile" monoliths.

The idea was to grab the RenderFragment of a library component to dump content into an otherwise-blank component that was compiled into the Blazor app. Unfortunately the RenderFragment inside ComponentBase is private so I used reflection to get it, but it does work, and from what I can see from diagnostics snapshots I don't have any suspicious references hanging around after unload. But in debug mode VS will crash randomly on the Unload -- sometimes only the 2nd or 3rd time, but occasionally it'll survive 6 or 7 unloads.

The repository contains three simple projects:

"RazorComponentManager" handles the AssemblyLoadContext stuff and is hard-coded to look for a component called RazorComponent.Component. The other two projects reference this one. It's a netcoreapp3.0 library and I had to add a FrameworkReference for ASP.NET Core libs.

"BlazorApp" is just the off-the-shelf Blazor template project with two changes. A new component DynamicLoader.razor was added which takes a path to an assembly to load, NavMenu.razor was updated with a link to the loader component (with a hard-coded path matching the debug build location on my local machine). (I have to mangle up the path since catch-all isn't supported for .razor files, so I just switch slashes to carets, obviously no real project would take any DLL pathname this way!)

"RazorComponent" started out as a Razor Class Library, but I had to change it from netstandard2.0 to netcoreapp3.0 and also added the ASP.NET Core FrameworkReference tag. It contains Component.razor which is just the Razor Component template and nothing more.

Reproducing the crash is simple. Run it, click the "Dynamic RCL" link, click "Home" or one of the other links, alternate a few times, and VS crashes.

image

image

Reference counts seem to look good when I look at diagnostic snapshots after an unload, and memory consumption is rock-steady across many load/unload iterations.

image

@janvorli

This comment has been minimized.

Copy link
Member

@janvorli janvorli commented Nov 11, 2019

cc: @tommcdon

@MV10

This comment has been minimized.

Copy link
Author

@MV10 MV10 commented Nov 11, 2019

Seems to be GC-related. It doesn't crash if I comment out the GC loop (as described in the ALC unload docs here) during the dispose -- I'm guessing because no GC is happening at all for such a small, simple test app.

Edit: Confirmed debug still fails after the automatic GC runs a couple of times.

for (int i = 0; i < 10 && WeakALC.IsAlive; i++)
{
   GC.Collect();
   GC.WaitForPendingFinalizers();
}
@DaveNay

This comment has been minimized.

Copy link

@DaveNay DaveNay commented Dec 4, 2019

@MV10 Have you made any further progress on this concept? We are investigating this exact architecture for our Blazor Server app and I ran into your comments and bug reports during my preliminary research.

@MV10

This comment has been minimized.

Copy link
Author

@MV10 MV10 commented Dec 4, 2019

@DaveNay No, I haven't spent any time on this recently. I sure hope this becomes a supported architecture in some fashion, but for now we're forging ahead with "monolithic app" (still library-based, but ultimately one-big-build).

@DaveNay

This comment has been minimized.

Copy link

@DaveNay DaveNay commented Dec 5, 2019

@DaveNay No, I haven't spent any time on this recently. I sure hope this becomes a supported architecture in some fashion, but for now we're forging ahead with "monolithic app" (still library-based, but ultimately one-big-build).

@MV10 I Played around some with this idea this morning. I was able to create an application that dynamically loads and unloads a module containing a Blazor navigation page. This was achieved through a custom Router component (99.9% identical to the default).

https://github.com/DaveNay/ModularBlazor

Next, I am going to work on dynamic loading and unloading of individual components.

@MV10

This comment has been minimized.

Copy link
Author

@MV10 MV10 commented Dec 5, 2019

@DaveNay thanks for sharing, that's an interesting approach. I remember seeing a couple of issues discussing / requesting better router extensibility (such as aspnet/AspNetCore#11446), so maybe it'll be possible to eventually do the same thing without re-inventing the wheel.

@DaveNay

This comment has been minimized.

Copy link

@DaveNay DaveNay commented Dec 5, 2019

@MV10 Yeah, I ran into the exact scenario described in that issue. I had to copy 11 classes out of the framework in order to implement my slightly modified version. I would fully support the suggestion there that router can be an improvement point in the (near) future.

I'll keep my eye on the situation for sure, but hopefully the approach I am using will work for our needs and be robust enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.