Showing posts with label releasing. Show all posts
Showing posts with label releasing. Show all posts

Sunday, March 25, 2012

Cleaning up after myself

How should I go about releasing any objects I've created? I've got a very complex CLR stored proc that creates a dataset, many sqlcommand objects, an adapter, etc, etc. It's used to evaluate high school transcripts for completion of the core subjects. It's a very complex SP that takes a single SSN as a parameter.

Right now it works as expected when I call it from a query window in SQL Mgmt Studio, I can stack 39 calls in a row like:

exec usp_Evaluate_eTranscript '011...'
exec usp_Evaluate_eTranscript '012...'
...
exec usp_Evaluate_eTranscript '039...'

And if I build a little test harness in VB.Net calling it in a populated dataset For ... Next loop, it works just fine for the 39 valid values I have.

But, when I build a cursor in a query window in MgmtStudio I get some strange behavior, that just doesn't really make sense. I'm getting a foreign key violation and I'm not even trying to insert any rows, just updating existing ones.

So, the only possible cause that I haven't eliminated yet is that some objects are still hanging around when the cursor tries to call the SP again.

It's declared:

<Microsoft.SqlServer.Server.SqlProcedure()> _
Public Shared Sub usp_Evaluate_eTranscript(ByVal SSN As String)

And exits with the standard End Sub.

Is there some way I can help the garbage collector along? Kick it into action? Maybe some

Set sqlcommand = Nothing or
sqlcommand.Dispose

dataset.Clear or
dataset.Dispose or somesuch thing?

I'm closing the context connection immediately after each time I use it (I have to so the next object can work).

? You don't have to worry about releasing objects. That's what the garbage collector is for... But it sounds like you're not properly closing your connections. I recommend wrapping all connections in a "Using" block: Using conn As New SqlConnection(connection_string) '... do what you need to do End Using This will automatically call the Dispose() method on the connection when your code is done with it, which will close the connection immediately -- instead of waiting for the GC to come along and finalize the object... And it frees you from having to remember to call Close() all the time. -- Adam MachanicPro SQL Server 2005, available nowhttp://www..apress.com/book/bookDisplay.html?bID=457-- ""R3dD0g"@.discussions.microsoft.com" <"=?UTF-8?B?UjNkRDBn?="@.discussions.microsoft.com> wrote in message news:f9ac67cf-3bcc-4519-86c7-1b44c648ec1f@.discussions.microsoft.com... How should I go about releasing any objects I've created? I've got a very complex CLR stored proc that creates a dataset, many sqlcommand objects, an adapter, etc, etc. It's used to evaluate high school transcripts for completion of the core subjects. It's a very complex SP that takes a single SSN as a parameter. Right now it works as expected when I call it from a query window in SQL Mgmt Studio, I can stack 39 calls in a row like: exec usp_Evaluate_eTranscript '011...'exec usp_Evaluate_eTranscript '012...'...exec usp_Evaluate_eTranscript '039...' And if I build a little test harness in VB.Net calling it in a populated dataset For ... Next loop, it works just fine for the 39 valid values I have. But, when I build a cursor in a query window in MgmtStudio I get some strange behavior, that just doesn't really make sense. I'm getting a foreign key violation and I'm not even trying to insert any rows, just updating existing ones. So, the only possible cause that I haven't eliminated yet is that some objects are still hanging around when the cursor tries to call the SP again. It's declared: <Microsoft.SqlServer.Server.SqlProcedure()> _Public Shared Sub usp_Evaluate_eTranscript(ByVal SSN As String) And exits with the standard End Sub. Is there some way I can help the garbage collector along? Kick it into action? Maybe some Set sqlcommand = Nothing orsqlcommand.Dispose dataset.Clear ordataset.Dispose or somesuch thing? I'm closing the context connection immediately after each time I use it (I have to so the next object can work).|||

I am wrapping all the DB calls inside a Using conn as New SQLConnection ... End Using, additionally, I'm specifically opening and closing the connection inside the using. When I forgot to close it there were problems - I'm hitting the database in about 8 different ways and times.

|||

Avoid calling the Dispose() method.

There seems to be a lot of confusion about the SqlConnection.Close() and SqlConnection.Dispose() methods.

For SqlConnection objects, 'closing' a connection simply returns it to the connection pool - the connection is not really closed. This behavior is desirable. Opening and closing connections is a processor intensive task, which is why connection pooling is so important for writing scalable applications. If you call Dispose() on a connection object, it is first returned to the connection pool. This is because the Dispose() method includes a call to the Close() method. Once the connection object is back in the pool, it releases all unmanaged resources (i.e., the database handle). By releasing its database handle, the connection is rendered useless, and is simply kicked out of the connection pool.

Calling Dispose() effectively destroys the purpose of connection pooling, which is to promote connection reuse. It is hard to imagine a valid reason for calling Dispose() at all. If you really don't want to use connection pooling, you can simply disable it in your connection string (easier to turn it back on from here if you change your mind later). If you want to cleanup after you're done using a connection, simply call the Close() method. The connection is returned to the connection pool, and will be available for reuse next time you application calls the Open() method.

In general, if an object provides both Open() and Close() methods, then it should be obvious the designers intended the pair to be used used together. If you 'open' an object, then logically you would want to 'close' it after you're done.

I hope this helps clear some of the confusion surrounding Close() and Dispose(). For more information, please consult the .NET Framework documentation.

|||

CyberGuru wrote:

Calling Dispose() effectively destroys the purpose of connection pooling

What make you think that a disposed connection does not remain available in the connection pool? All the available documentation would suggest that it does (e.g.:http://msdn2.microsoft.com/en-us/library/8xx3tyca.aspx), and all tests I've run would seem to indicate the same. Do you have any evidence to the contrary?

CyberGuru wrote:

In general, if an object provides both Open() and Close() methods, then it should be obvious the designers intended the pair to be used used together. If you 'open' an object, then logically you would want to 'close' it after you're done.

In most cases, "Close" methods simply provide a facade for disposition. They're generally meant to make folks who like Open/Close symmetry feel comfortable, not to provide clean-up mechanism that differs from Dispose. Designing a Close method that is meant to be called instead of the Dispose method for a class that implements IDisposable would probably be considered a design error by most folks, particularly given that it interferes with use of the using statement.

sqlsql

Thursday, March 22, 2012

class that uses sql server isnt releasing memory

I'm running into a problem where the class I'm running seems to eat up a lot of memory with sql server. When it's done running, the memory usage never goes down in taskmanager. I can't figure out where the memory leak might be. Here's the code that is being called. Does anyone see a reason why it would continue to eat memory as it runs and then not release it? Thanks.

using System;using System.Collections.Generic;using System.Text;using System.Data;using System.Data.SqlClient;namespace QueryLoadTester{ class JobSeeker { private string ConStr = @."Data Source=server;Initial Catalog=db;Integrated Security=True"; public JobSeeker() { } #region UpdateJobSeeker public void UpdateJobSeeker(string JobSeekerId) { string qry = "SELECT top 100 dbo.JobSeeker.JobSeekerID, dbo.JobSeeker.SiteId, dbo.JobSeeker.PositionTitle, dbo.JobSeeker.LocationID, dbo.JobSeeker.CurrentSalary, " + "dbo.JobSeeker.DesiredSalary, dbo.JobSeeker.MinSalary, dbo.JobSeeker.CurrentHourly, dbo.JobSeeker.MinHourly, dbo.JobSeeker.Comments, " + "dbo.JobSeeker.Resume, dbo.JobSeeker.WillRelocate, dbo.JobSeeker.DateAdded, dbo.JobSeeker.LastModified FROM dbo.JobSeeker " + "where dbo.JobSeeker.Active=1 and dbo.JobSeeker.samplejobseeker=0 "; if (JobSeekerId.Length > 0) qry += " and dbo.JobSeeker.JobSeekerID=" + JobSeekerId; qry += " and dbo.JobSeeker.JobSeekerID not in (Select JobSeekerId from JobSeekerFullTextSearch)"; SqlConnection cnInsert = new SqlConnection(ConStr); SqlDataAdapter adp = new SqlDataAdapter(qry, cnInsert); cnInsert.Open(); DataSet dsJobSeekers = new DataSet(); adp.Fill(dsJobSeekers); adp.Dispose(); adp = null; if (dsJobSeekers.Tables[0].Rows.Count > 0) { string jid = string.Empty; string degree, degreegroup; StringBuilder sb = new StringBuilder(); SqlCommand cmdInsert = new SqlCommand(); cmdInsert.Connection = cnInsert; foreach (DataRow dr in dsJobSeekers.Tables[0].Rows) { jid = dr["JobSeekerID"].ToString(); if (JobSeekerId.Length > 0) { cmdInsert.CommandText = "Delete from JobSeekerFullTextSearch where JobSeekerId=" + JobSeekerId; cmdInsert.ExecuteNonQuery(); } DataSet dsExtras = GetJobSeekerExtras(jid, cnInsert); SqlTransaction trans = cnInsert.BeginTransaction(); cmdInsert.Transaction = trans; degree = degreegroup = string.Empty; #region insert record into fulltextsearch try { sb.Remove(0, sb.Length); sb.Append("Insert into JobSeekerFullTextSearch (JobSeekerId, PositionTitle, LocationID, CurrentSalary, "); sb.Append("DesiredSalary, MinSalary, CurrentHourly, MinHourly, CommentsResume, SiteId, WillRelocate, DateAdded, LastModified) values ("); sb.Append(jid); sb.Append(",'"); sb.Append(dr["PositionTitle"].ToString().Replace("'", "''")); sb.Append("',"); sb.Append("'"); sb.Append(dr["LocationID"].ToString().Replace("'", "''")); sb.Append("',"); sb.Append(Nullify(dr["CurrentSalary"])); sb.Append(","); sb.Append(Nullify(dr["DesiredSalary"])); sb.Append(","); sb.Append(Nullify(dr["MinSalary"])); sb.Append(","); sb.Append(Nullify(dr["CurrentHourly"])); sb.Append(","); sb.Append(Nullify(dr["MinHourly"])); sb.Append(",'"); sb.Append(dr["Comments"].ToString().Replace("'", "''")); sb.Append(" "); sb.Append(dr["Resume"].ToString().Replace("'", "''")); sb.Append("',"); sb.Append(dr["SiteId"].ToString()); sb.Append(","); sb.Append(Convert.ToInt32(Convert.ToBoolean(dr["WillRelocate"].ToString()))); sb.Append(",'"); sb.Append(dr["DateAdded"].ToString()); sb.Append("','"); sb.Append(dr["LastModified"].ToString()); sb.Append("')"); cmdInsert.CommandText = sb.ToString(); cmdInsert.ExecuteNonQuery(); #region dsExtras insert //degree info if (dsExtras.Tables.Count > 0 && dsExtras.Tables[0].Rows.Count > 0) { degree = dsExtras.Tables[0].Rows[0][0].ToString(); degreegroup = dsExtras.Tables[0].Rows[0][1].ToString(); if (degree.Length > 0 || degreegroup.Length > 0) { sb.Remove(0, sb.Length); sb.Append("Update JobSeekerFullTextSearch set DegreeLevel='"); sb.Append(degree); sb.Append("', DegreeLevelGroup="); sb.Append(degreegroup); sb.Append(" where JobSeekerId="); sb.Append(jid); cmdInsert.CommandText = sb.ToString(); cmdInsert.ExecuteNonQuery(); } } //disciplines info if (dsExtras.Tables.Count > 1 && dsExtras.Tables[1].Rows.Count > 0) { sb.Remove(0, sb.Length); foreach (DataRow d in dsExtras.Tables[1].Rows) { sb.Append(d[0].ToString()); sb.Append(","); } if (sb.ToString().Length > 0) { cmdInsert.CommandText = "Update JobSeekerFullTextSearch set DisciplineIdList='" + sb.ToString().Substring(0, sb.ToString().Length - 1) + "' where JobSeekerId=" + jid; cmdInsert.ExecuteNonQuery(); } } //industries info if (dsExtras.Tables.Count > 2 && dsExtras.Tables[2].Rows.Count > 0) { sb.Remove(0, sb.Length); foreach (DataRow d in dsExtras.Tables[2].Rows) { sb.Append(d[0].ToString()); sb.Append(","); } if (sb.ToString().Length > 0) { cmdInsert.CommandText = "Update JobSeekerFullTextSearch set IndustryIdList='" + sb.ToString().Substring(0, sb.ToString().Length - 1) + "' where JobSeekerId=" + jid; cmdInsert.ExecuteNonQuery(); } } //jobtypes info if (dsExtras.Tables.Count > 3 && dsExtras.Tables[3].Rows.Count > 0) { sb.Remove(0, sb.Length); foreach (DataRow d in dsExtras.Tables[3].Rows) { sb.Append(d[0].ToString()); sb.Append(","); } if (sb.ToString().Length > 0) { cmdInsert.CommandText = "Update JobSeekerFullTextSearch set JobTypeIdList='" + sb.ToString().Substring(0, sb.ToString().Length - 1) + "' where JobSeekerId=" + jid; cmdInsert.ExecuteNonQuery(); } } #endregion trans.Commit(); Console.WriteLine("Insert for " + jid); } catch (Exception exc) { trans.Rollback(); Console.WriteLine(jid + " - " + exc.ToString()); } finally { trans.Dispose(); trans = null; } #endregion dsExtras.Clear(); dsExtras.Dispose(); dsExtras = null; }//end foreach cmdInsert.Dispose(); cmdInsert = null; } cnInsert.Close(); cnInsert.Dispose(); cnInsert = null; GC.Collect(); } #endregion #region GetJobSeekerExtras private static DataSet GetJobSeekerExtras(string JobSeekerId, SqlConnection cn) { string qry = "Select JobSeekerDegree.DegreeLevel, VDegreeLevels.DegreeGroup from JobSeekerDegree inner join " + " VDegreeLevels on JobSeekerDegree.DegreeLevel=VDegreeLevels.DegreeLevel where JobSeekerId=" + JobSeekerId + ";" + "Select DisciplineID from JobSeekerDiscipline where JobSeekerId=" + JobSeekerId + ";" + "Select IndustryID from JobSeekerIndustry where JobSeekerID=" + JobSeekerId + ";" + "Select JobTypeID from JobSeekerJobType where JobSeekerID=" + JobSeekerId; DataSet ds = new DataSet(); SqlDataAdapter adp = new SqlDataAdapter(qry, cn); adp.Fill(ds); adp.Dispose(); adp = null; return ds; } #endregion #region Nullify private static string Nullify(object p) { if (p != System.DBNull.Value) return p.ToString(); else return "null"; } #endregion }}
GC.Collect(); is killing your code, you DON'T call that method in C# the CLR(common langauge runtime) calls it, any code calling GC.Collect(), must be C++ in .NET. The correct way to do it is very easy in C# wrap all those code blocks with the second USING statement that calls dispose automatically. If you must do it manually then call Dispose Bool. Hope this helps.|||GC.Collect doesn't seem to make a difference when it all runs. I was just trying that to see if it would release all or some of the memory when the class terminated. I'll try the using statement, but since I'm already disposing of all the objects, isn't that basically doing the same thing?|||

No it is not to quote C# and Asp.net expert Jeff Prosise GC.Collect can do really nasty stuff to your application. The USING statement on the other hand calls Dispose automatically if you place it after the Preprocessor directives. You can call Dispose Bool or Call Close and then Dispose. Hope this helps.

|||I just updated the code to use the using statements, but the memory is still not being released. Any other ideas? Thanks.|||

The first thing to do is try click on show execution cost before running your SQL statements in either Management studio or Query Analyzer. If it did not show high cost then try the profiler under tools to run trace on the statements to see what is killing the code. Another option is to open the connection late and close it quickly. Hope this helps.