diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs index 1ee4cba196..bdecce6fc1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs @@ -474,5 +474,10 @@ protected virtual DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal i abstract internal bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from); abstract internal void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to); + + virtual internal void Unload() + { + _pruningTimer.Dispose(); + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 3e7db7d945..b7bbba8abc 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -309,6 +309,8 @@ + + @@ -778,7 +780,7 @@ - + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.AssemblyLoadContext.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.AssemblyLoadContext.cs new file mode 100644 index 0000000000..7338b8d4d2 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.AssemblyLoadContext.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Reflection; +using System.Runtime.Loader; + +namespace Microsoft.Data.SqlClient +{ + sealed internal partial class SqlConnectionFactory + { + partial void SubscribeToAssemblyLoadContextUnload() + { + AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).Unloading += SqlConnectionFactoryAssemblyLoadContext_Unloading; + } + + private void SqlConnectionFactoryAssemblyLoadContext_Unloading(AssemblyLoadContext obj) + { + Unload(obj, EventArgs.Empty); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs index 597a140a76..93450aec7b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs @@ -11,12 +11,15 @@ namespace Microsoft.Data.SqlClient { - sealed internal class SqlConnectionFactory : DbConnectionFactory + sealed internal partial class SqlConnectionFactory : DbConnectionFactory { private const string _metaDataXml = "MetaDataXml"; - private SqlConnectionFactory() : base() { } + private SqlConnectionFactory() : base() + { + SubscribeToAssemblyLoadContextUnload(); + } public static readonly SqlConnectionFactory SingletonInstance = new SqlConnectionFactory(); @@ -306,6 +309,20 @@ protected override DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection.ServerVersion, internalConnection.ServerVersion); } + + private void Unload(object sender, EventArgs e) + { + try + { + Unload(); + } + finally + { + ClearAllPools(); + } + } + + partial void SubscribeToAssemblyLoadContextUnload(); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs index 959122bf1c..d82a9fc8fa 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs @@ -11,28 +11,10 @@ namespace Microsoft.Data.SqlClient // for example, some mobile profiles on mono partial class SqlDependencyPerAppDomainDispatcher { - private void SubscribeToAppDomainUnload() + partial void SubscribeToAppDomainUnload() { // If rude abort - we'll leak. This is acceptable for now. AppDomain.CurrentDomain.DomainUnload += new EventHandler(UnloadEventHandler); } - - private void UnloadEventHandler(object sender, EventArgs e) - { - long scopeID = SqlClientEventSource.Log.TryNotificationScopeEnterEvent("SqlDependencyPerAppDomainDispatcher.UnloadEventHandler | DEP | Object Id {0}", ObjectID); - try - { - // Make non-blocking call to ProcessDispatcher to ThreadPool.QueueUserWorkItem to complete - // stopping of all start calls in this AppDomain. For containers shared among various AppDomains, - // this will just be a ref-count subtract. For non-shared containers, we will close the container - // and clean-up. - var dispatcher = SqlDependency.ProcessDispatcher; - dispatcher?.QueueAppDomainUnloading(SqlDependency.AppDomainKey); - } - finally - { - SqlClientEventSource.Log.TryNotificationScopeLeaveEvent(scopeID); - } - } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AssemblyLoadContext.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AssemblyLoadContext.cs new file mode 100644 index 0000000000..47f62fc5ac --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AssemblyLoadContext.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Reflection; +using System.Runtime.Loader; + +namespace Microsoft.Data.SqlClient +{ + // these members were moved to a separate file in order + // to be able to skip them on platforms where AssemblyLoadContext members are not supported + // for example, netstandard + partial class SqlDependencyPerAppDomainDispatcher + { + partial void SubscribeToAssemblyLoadContextUnload() + { + AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).Unloading += SqlDependencyPerAppDomainDispatcher_Unloading; + } + + private void SqlDependencyPerAppDomainDispatcher_Unloading(AssemblyLoadContext obj) + { + UnloadEventHandler(null, EventArgs.Empty); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs index e4f2a4446a..15f2b573e2 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs @@ -94,6 +94,29 @@ private SqlDependencyPerAppDomainDispatcher() Timeout.Infinite); SubscribeToAppDomainUnload(); + SubscribeToAssemblyLoadContextUnload(); + } + finally + { + SqlClientEventSource.Log.TryNotificationScopeLeaveEvent(scopeID); + } + } + + partial void SubscribeToAppDomainUnload(); + + partial void SubscribeToAssemblyLoadContextUnload(); + + private void UnloadEventHandler(object sender, EventArgs e) + { + long scopeID = SqlClientEventSource.Log.TryNotificationScopeEnterEvent("SqlDependencyPerAppDomainDispatcher.UnloadEventHandler | DEP | Object Id {0}", ObjectID); + try + { + // Make non-blocking call to ProcessDispatcher to ThreadPool.QueueUserWorkItem to complete + // stopping of all start calls in this AppDomain. For containers shared among various AppDomains, + // this will just be a ref-count subtract. For non-shared containers, we will close the container + // and clean-up. + var dispatcher = SqlDependency.ProcessDispatcher; + dispatcher?.QueueAppDomainUnloading(SqlDependency.AppDomainKey); } finally { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs index ec3b6549b3..8035b460db 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Reflection; +using System.Runtime.Loader; namespace Microsoft.Data.SqlClient { @@ -10,6 +12,12 @@ internal sealed class SqlDiagnosticListener : DiagnosticListener { public SqlDiagnosticListener(string name) : base(name) { + AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).Unloading += SqlDiagnosticListener_Unloading; + } + + private void SqlDiagnosticListener_Unloading(AssemblyLoadContext obj) + { + Dispose(); } } }