SqlClient: Cannot unload a collectible AssemblyLoadContext if System.Data.SqlClient is used
The Assembly we want to load (and unload) needs the ability to connect to a database for which we use Dapper. Dapper internally makes use of System.Data.SqlClient. We find that when we create such an assembly, then we are unable to Unload the collectible AssemblyLoadContext.
The sample source code is available at: https://github.com/stasgaonkar/DynamicLoading
Having followed the steps mentioned in https://docs.microsoft.com/en-us/dotnet/standard/assembly/unloadability#, the following is the output of gcroot
command,
0:000> !dumpheap -type LoaderAllocator
Address MT Size
000002022c46e1f0 00007fff74999410 48
000002022c46e260 00007fff74999538 24
Statistics:
MT Count TotalSize Class Name
00007fff74999538 1 24 System.Reflection.LoaderAllocatorScout
00007fff74999410 1 48 System.Reflection.LoaderAllocator
Total 2 objects
0:000> !gcroot -all 0x000002022c46e1f0
HandleTable:
000002022A9D12A0 (strong handle)
-> 000002022C4E8C48 System.Data.SqlClient.TdsParserStateObjectNative
-> 000002022C46E1F0 System.Reflection.LoaderAllocator
000002022A9D12A8 (strong handle)
-> 000002022C4E00F0 System.Data.SqlClient.TdsParserStateObjectNative
-> 000002022C46E1F0 System.Reflection.LoaderAllocator
000002022A9D12E0 (strong handle)
-> 000002022C4B38C0 System.Data.SqlClient.TdsParserStateObjectNative
-> 000002022C46E1F0 System.Reflection.LoaderAllocator
000002022A9D15F8 (pinned handle)
-> 000002023C461038 System.Object[]
-> 000002022C475768 System.Threading.TimerQueue[]
-> 000002022C475840 System.Threading.TimerQueue
-> 000002022C4ABE88 System.Threading.TimerQueueTimer
-> 000002022C475708 System.Threading.TimerQueueTimer
-> 000002022C475580 System.Threading.TimerCallback
-> 000002022C474D60 System.Data.SqlClient.SqlConnectionFactory
-> 000002022C46E1F0 System.Reflection.LoaderAllocator
Found 4 roots.
So it seems that the TdsParserStateObjectNative is the culprit. What we have also noticed is that there are some “unmanaged/native DLL” that are used (sni.dll
) internally by System.Data.SqlClient
, possibly from within TdsParserStateObjectNative
. Not sure if this is causing any issues.
Is something that we are attempting to do, incorrect?
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 28 (14 by maintainers)
I’m not sure if it’ll be worth doing. If we want to move to managed sni as default then enabling NativeLibrary loading of the pinvokes is a distraction. It’ll all depend on performance gains and timescales and customer demand. If setting the app context is enough for most users then it might be best to leave it as-is.
@Wraith2 I made a separate issue for this with the source code provided.
#1635
@Wraith2 i seem to have resolved my issue now, but what caused it remains a mystery.
@Wraith2 It’s not as complicated as you think. It just uses a standard DllImport to load the appropriate x86/x64 class and then DLL either in the SqlClient DLL’s current folder or in the PATH. It would be awesome if we could reliably unload the SNI DLL when it’s appropriate. I looked into a way to do it before, but didn’t find a simple solution.
All here: https://github.com/dotnet/SqlClient/tree/master/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop
@Wraith2 using your CI build and setting “Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows” did the trick for me. Thanks for your help @Wraith2, and thanks to you @janvorli for explaining the issue.