Tuesday, March 27, 2012

Calling SqlDependency.Stop() in class destructor

Hey guys,

Have you ever tried to call the SqlDependency.Stop() method in a class destructor (C#)? It seems like the finalization process hangs after the call to the SqlDependency.Stop() method (for example the assignment after the SqlDependency.Stop() method call is never executed).

~Program()

{

if (!_finalized)

{

SqlDependency.Stop(NocConnectionString);

_finalized = false;

}

}

I tried to use the ADO.NET 2.0 tracing, and it shows that the SqlCommand.Cancel() method call throws an exception during finalization, but it’s not possible to intercept it. Do you have any clue about it or have you ever experinced the same problem?

Regards,

Dmytro Kryvko

Do you have the dependency object defined i a class variable ? Which modifier do you have defined for the variable, perhaps its not accessible through the destructor ? Which error do you get through the Stop method ?

HTH, Jens Suessmeyer.

http:/www.sqlserver2005.de

|||

Hi Jens,

No, I don't have any dependency objects defined as class variables. The question is different. If you have a following class, where for the test purposes starts the listener for receiving dependency change notifications and then immediately stop it, everything works fine.

class Test

{

private const string ConnectionString = "blah blah blah;";

public Test()

{

SqlDependency.Start(ConnectionString);

SqlDependency.Stop(ConnectionString);

}

}

If you try to stop a listener in the class destructor, the assignment (_finalized = false) after the SqlDependency.Stop() never gets executed. If you try to catch an exception thrown by the SqlDependency.Stop(), nothing is simply caught.

class Test

{

private const string ConnectionString = "blah blah blah;";

private bool _finalized = false;

public Test()

{

SqlDependency.Start(ConnectionString);

}

~Test()

{

if (!_finalized)

{

SqlDependency.Stop(ConnectionString);

_finalized = false;

}

}

}

Thanks a lot for your reply anyway!

Regards,

Dmytro

|||

Hi Dmytro,

If you want to do RAII in CLR you should implement IDisposable and stop the SqlDependency in the Dispose method. Then wrap your class instance in a using statement:

class Program

{

static void Main()

{

using (test = new Test())

{

// Doing work here..

}

}

}

class Test: IDisposable

{

private const string ConnectionString = "blah blah blah;";

private bool _finalized = false;

public Test()

{

SqlDependency.Start(ConnectionString);

}

void Dispose()

{

if (!_finalized)

{

SqlDependency.Stop(ConnectionString);

_finalized = true;

}

}

}

HTH,
~ Remus

|||

Hi Remus,

Thank you very much for your reply. Of course that way of recommend is right. I also experimented with implementing the IDisposable interface and stopping the SqlDependency during the disposal. However I’m talking about the case when it’s NOT POSSIBLE to wrap a class into the using statement.

In the example below I reproduced the case I describe. If you try to set a breakpoint on the (_disposed = true;) statement, it would never get called after something goes wrong during the SqlDependency.Stop() method execution.

class Program

{

static void Main(string[] args)

{

Test.Instance.DoNothing();

}

}

class Test : IDisposable

{

private const string ConnectionString = "blah blah blah;";

private static Test _test = null;

private bool _disposed = false;

private Test()

{

SqlDependency.Start(ConnectionString);

}

~Test()

{

Dispose(false);

}

public static Test Instance

{

get

{

if (null == _test)

_test = new Test();

return _test;

}

}

public void DoNothing()

{

}

private void Dispose(bool disposing)

{

if (!_disposed)

{

SqlDependency.Stop(ConnectionString);

_disposed = true;

}

}

#region IDisposable Members

public void Dispose()

{

Dispose(true);

GC.SuppressFinalize(this);

}

#endregion

}

If you try to enable the ADO.NET 2.0 tracing, you would see that an exception is thrown inside the SqlCommand.Cancel() method that it called at the time of finalization.

Regards,

Dmytro

|||

I don't know what are the various restrictions about the code allowed to run when the GC is evicting objects, but apparently you're hitting one. But the most important think is that the random moments when this happens (GC eviction) doesn't seem to be a good idea to place the SqlDependency.Stop in the first place. If your goal is to stop the SqlDependency infrastructure when the application exists (i.e. when the .exe finishes, or when a ASP app ends), then perhaps you should hook into the appopiate event (like Form.Closing, AppDomain.DomainUnload, Application.ApplicationExit etc).

HTH,
~ Remus

|||This thread is quite old, however it is the only Google hit for "SqlDependency.Stop DomainUnload" ;-)

I'm facing exactly the same problem. I'm creating a data access component that is caching some queries from a SQL Server 2005 database. The cache is invalidated using this shiny new, well not sooo new anymore, SqlDependency approach.

Because this component will be used in ASP.NET as well as in Forms and Windows Service applications, I'm searching for a common way to (internally) call SqlDependency.Stop().

Using a finalizer was my first idea, too, and this didn't work out. My second try was using an event handler for AppDomain.DomainUnload.

After all, this seems to work... But the built-in webserver in VS 2005 will hang for 4-5 minutes with 100% CPU while exeuting SqlDependy.Stop(). In fact, I can't remember of any other process blocking my machine (Pentium M laptop) reproducible so badly that I could hardly bring up Task Manager... I didn't expect this was possible from user-space and even managed code (SQL Server is running on another box.) During this time, even Performance Monitor refuses to log anything, so I can't say if there are a lot of Windows handles or .NET exceptions envolved or whatever...

Calling it from the Application_End event works fine (and takes only a few milliseconds), however this is specific to ASP.NET.

Any ideas?|||

Hi Markus,

I am not aware of any issue like the one you describe. I am not a .net expert by any stretch, but I would approach this like any other performance problem: perfcounters, F1 profiler, monitor traffic to SQL Server etc, to positively identify what is causing the high CPU consumption.

HTH,
~ Remus

|||Thank you very much for your reply. The high CPU usage does not occur on the remote SQL Server, but on my developer machine, so it is definitly a .NET issue. I only know that it happens with SqlDependency in combination with ASP.NET, however I can't yet say what exactly is causing the matter. I'll try to further narraw that down and let you know.

(Btw running anything like perfcounters doesn't help as it doesn't collect any data while SqlDependency.Stop(). At least with a single-core processor the system really hangs completly for a few minutes...)

However I would be glad to hear about any other approach to trigger SqlDependency.Stop than DomainUnload or finalizers.

Anyway, what would be the tradeoff of not stopping at all? While developing this happens all the time e.g. when you stop debugging. Will this leave SQL Server resources like queues etc. open forever, or will it be cleaned up sometime, e.g. with the next SQL Server restart?|||

You can start a perfcounter monitoring using logman.exe, it will capture all data even under most stressful conditions.

Each SqlDependency.Start () creates a service, a queue and a stored proc. It creates a dialog timer (BEGIN CONVERSATION TIMER) with itself. Normaly whenever this timer fires, the SqlDependency thread that has posted a RECEIVE verb will consume it and start another timer. If this timer message ever gets enqueued and there is no RECEIVE posted on the queue, the activated stored procedure associated with the queue will be invoked and this will clean up the service, the queue and the procedure itself. In other words, self-cleaning is provisioned, but the recommended way is still to call SqlDependency.Stop

HTH,
~ Remus

No comments:

Post a Comment