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

Unable to delete unloaded dll after using System.Text.Json.JsonSerializer.Serialize with a Type from the dll. #80420

Closed
frederik5480 opened this issue Jan 10, 2023 · 3 comments

Comments

@frederik5480
Copy link

frederik5480 commented Jan 10, 2023

Description

After creating a new AssemblyLoadContext and loading in a dll to it, I make use of the new types if they are deriving from specific base classes in our project. As a result, I end up doing json serialization for some of these types, however after I have done that, I'm no longer able to delete the dll after unloading it.
If I choose to simply just unload it without doing any serialization, I can delete the file as expected.
As a note, this happens in multiple other scenarios as well, such as MVC Modelbinding (when I use TryUpdateModel) and simply just when a response in a custom controller is of one of these types in the dll. (Minor note that Newtonsoft also gives us the same problem)
From what I can tell there's various caches that caches the Types, however I have no way of clearing these for when I want to unload and delete the dll.
It's also worth noting that the unloading happens just fine and the AssemblyLoadContext is gone, I am also able to load in the same dll (or an updated version) and access the types again after that, while after unloading it it's unaccessible, I simply cant remove the file.

Reproduction Steps

https://github.com/frederik5480/HangingReferenceExample/tree/main/HangingReferenceExample

This is a small console app that enables 4 endpoints to show a trimmed down usecase.
Firstly the 'HangingReferenceExample' project needs to be built, as it has the base class used by the library.
Then the 'GithubExample' needs to be built - this of course results in a GithubExample.dll file, which needs to be moved into the bin folder of the 'HangingReferenceExample' project.
Now the 'HangingReferenceExample' needs to be started.

The app is simple, as it's a small API listening on 4 specific endpoints.
As by default the 'GithubExample' dll is not loaded in, we do that by calling GET 'http://localhost:8080/Load'
Now we should be able to access the types in the dll, so we can call GET 'http://localhost:8080/TestToString' and get a response.
This response is simply just accessing the model from the dll and calling .ToString() on it, to prove that we have accessed it.
We can now call GET 'http://localhost:8080/Unload', which should successfully unload the dll, and we can even enter the /bin directory and successfully delete the dll file.

However, the issue comes up when doing serialization of the object.
So if we repeat the first step of calling GET 'http://localhost:8080/Load'
Now if we call the 4th endpoint, GET 'http://localhost:8080/TestToJson' it will do a serialization of the object and return that, so we should see a JSON response.
If we now continue and call GET 'http://localhost:8080/Unload', it should unload successfully.
However entering the /bin directory, we will now be unable to delete the dll file, as a reference is still kept somewhere.

Expected behavior

The expected behaviour in this case would be that I would be allowed to delete the dll file, as the assembly was unloaded without issues, I would expect no references to be kept.
However since that's not the case, I would like to be enlightened on how the actual expectation for this is, as it's a problem in multiple places, not just JSON serialization.

Actual behavior

Theres references being cached somewhere that's inaccessible for me to get to and clear, resulting in the removal of the dll file being impossible, no matter how long I wait, the GC doesn't clear them either, so the application has to be recycled before the dll can be removed, which is not ideal.

Regression?

No response

Known Workarounds

Not an ideal workaround, but a list of dll names can be stored somewhere and removed when the application recycles.
This works because the AssemblyLoadContext is being unloaded, so a new one with the same dll can be loaded in again and then accessed. I have not found any issue with types clashing in these caches when I upload new versions (or just edited models) in the dlls, but I can't say for sure that it's the case for all the places this caching occurs.

Configuration

No response

Other information

No response

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Jan 10, 2023
@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@ghost
Copy link

ghost commented Jan 10, 2023

Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

After creating a new AssemblyLoadContext and loading in a dll to it, I make use of the new types if they are deriving from specific base classes in our project. As a result, I end up doing json serialization for some of these types, however after I have done that, I'm no longer able to delete the dll after unloading it.
If I choose to simply just unload it without doing any serialization, I can delete the file as expected.
As a note, this happens in multiple other scenarios as well, such as MVC Modelbinding (when I use TryUpdateModel) and simply just when a response in a custom controller is of one of these types in the dll. (Minor note that Newtonsoft also gives us the same problem)
From what I can tell there's various caches that caches the Types, however I have no way of clearing these for when I want to unload and delete the dll.
It's also worth noting that the unloading happens just fine and the AssemblyLoadContext is gone, I am also able to load in the same dll (or an updated version) and access the types again after that, while after unloading it it's unaccessible, I simply cant remove the file.

Reproduction Steps

https://github.com/frederik5480/HangingReferenceExample/tree/main/HangingReferenceExample

This is a small console app that enables 4 endpoints to show a trimmed down usecase.
Firstly the 'HangingReferenceExample' project needs to be built, as it has the base class used by the library.
Then the 'GithubExample' needs to be built - this of course results in a GithubExample.dll file, which needs to be moved into the bin folder of the 'HangingReferenceExample' project.
Now the 'HangingReferenceExample' needs to be started.

The app is simple, as it's a small API listening on 4 specific endpoints.
As by default the 'GithubExample' dll is not loaded in, we do that by calling GET 'http://localhost:8080/Load'
Now we should be able to access the types in the dll, so we can call GET 'http://localhost:8080/TestToString' and get a response.
This response is simply just accessing the model from the dll and calling .ToString() on it, to prove that we have accessed it.
We can now call GET 'http://localhost:8080/Unload', which should successfully unload the dll, and we can even enter the /bin directory and successfully delete the dll file.

However, the issue comes up when doing serialization of the object.
So if we repeat the first step of calling GET 'http://localhost:8080/Load'
Now if we call the 4th endpoint, GET 'http://localhost:8080/TestToJson' it will do a serialization of the object and return that, so we should see a JSON response.
If we now continue and call GET 'http://localhost:8080/Unload', it should unload successfully.
However entering the /bin directory, we will now be unable to delete the dll file, as a reference is still kept somewhere.

Expected behavior

The expected behaviour in this case would be that I would be allowed to delete the dll file, as the assembly was unloaded without issues, I would expect no references to be kept.
However since that's not the case, I would like to be enlightened on how the actual expectation for this is, as it's a problem in multiple places, not just JSON serialization.

Actual behavior

Theres references being cached somewhere that's inaccessible for me to get to and clear, resulting in the removal of the dll file being impossible, no matter how long I wait, the GC doesn't clear them either, so the application has to be recycled before the dll can be removed, which is not ideal.

Regression?

No response

Known Workarounds

Not an ideal workaround, but a list of dll names can be stored somewhere and removed when the application recycles.
This works because the AssemblyLoadContext is being unloaded, so a new one with the same dll can be loaded in again and then accessed. I have not found any issue with types clashing in these caches when I upload new versions (or just edited models) in the dlls, but I can't say for sure that it's the case for all the places this caching occurs.

Configuration

No response

Other information

No response

Author: frederik5480
Assignees: janvorli
Labels:

area-AssemblyLoader-coreclr, untriaged

Milestone: -

@teo-tsirpanis
Copy link
Contributor

Duplicate of #65323.

@teo-tsirpanis teo-tsirpanis closed this as not planned Won't fix, can't repro, duplicate, stale Jan 10, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Jan 10, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Feb 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants