Memory leak in C# Code - c#

I have a piece of code that going to the database every minute to check if there is any report that need to be run, and if there is any it runs it.
The issue is that my object initiation to a database class creates memory leak. If I look at task mgr "user Object" grown by 5 every time tick executing a code.
private void ReportRunTimer_Tick(object sender, EventArgs e)
{
DataConnection dataConnection = new DataConnection(); *<-- when executing this line User Object increasing.*
try
{
reportsToRun = dataConnection.GetListOfTheReportForReportRunTick();
if (reportsToRun.Count > 0)
foreach (string report in reportsToRun)
{
logs("Starting Automatic report generatin", "Successful");
Thread TicketReportMethodThread = new Thread(() => GenerateReport(report, 1));
TicketReportMethodThread.Start();
}
dataConnection = null;
} catch (Exception ex)
{
logs("Starting Automatic report generatin failed: " +ex.ToString(), "Error");
}
finally
{
reportsToRun.Clear();
}
}
DataConnection class
public List<string> GetListOfTheReportForReportRunTick()
{
List<string> RepoerList = new List<string>();
connString = "Server=xxx;Port=xxx;Database=xxx;Uid=xxx;password=xxxx;SslMode=none";
using (MySqlConnection mySqlConnection = new MySqlConnection(connString))
{
try
{
MySqlCommand mySqlCommand = mySqlConnection.CreateCommand();
mySqlCommand.CommandText = "SELECT reportname FROM reports WHERE nextruntime < NOW()";
mySqlConnection.Open();
MySqlDataReader mySqlDataReader = mySqlCommand.ExecuteReader();
while (mySqlDataReader.Read())
{
RepoerList.Add(mySqlDataReader["reportname"].ToString());
}
}
catch (MySqlException ex)
{
hd.logs("Failed to get reports with reportstorun_tick Error: " + ex.ToString(), "Error");
mySqlConnection.Close();
}
finally
{
mySqlConnection.Close();
mySqlConnection.Dispose();
}
}
return RepoerList;
}
DataConnection dataConnection = new DataConnection(); used in a few more places and this is the only one that causing an issue.
If I replace code in private void ReportRunTimer_Tick with code from public List GetListOfTheReportForReportRunTick() like bellow. Issue no longer exist, any ideas?
private void ReportRunTimer_Tick(object sender, EventArgs e)
{
List<string> reportsToRun = new List<string>();
try
{
connString = "Server=xxx;Port=xxx;Database=xxx;Uid=xxx;password=xxxx;SslMode=none";
using (MySqlConnection mySqlConnection = new MySqlConnection(connString))
{
try
{
MySqlCommand mySqlCommand = mySqlConnection.CreateCommand();
mySqlCommand.CommandText = "SELECT reportname FROM reports WHERE nextruntime < NOW()";
mySqlConnection.Open();
MySqlDataReader mySqlDataReader = mySqlCommand.ExecuteReader();
while (mySqlDataReader.Read())
{
reportsToRun.Add(mySqlDataReader["reportname"].ToString());
}
if (reportsToRun.Count > 0)
foreach (string report in reportsToRun)
{
logs("Starting Automatic report generatin", "Successful");
Thread TicketReportMethodThread = new Thread(() => GenerateReport(report, 1));
TicketReportMethodThread.Start();
}
}
catch (MySqlException ex)
{
logs("Failed to get reports with reportstorun_tick Error: " + ex.ToString(), "Error");
mySqlConnection.Close();
}
finally
{
mySqlConnection.Close();
mySqlConnection.Dispose();
}
}
}
catch (Exception ex)
{
logs("Starting Automatic report generatin failed: " + ex.ToString(), "Error");
}
finally
{
reportsToRun.Clear();
}
}
Issue is caused by
DataConnection dataConnection = new DataConnection(); *<-- when executing this line User Object increasing.*
reportsToRun = dataConnection.GetListOfTheReportForReportRunTick();
But I can't understand why.

I think the memory leak is because you're not disposing your MySqlDataReader. You can do this by just wrapping it in a using statement like this:
using(MySqlDataReader mySqlDataReader = mySqlCommand.ExecuteReader()){
while (mySqlDataReader.Read())
{
RepoerList.Add(mySqlDataReader["reportname"].ToString());
}
}

Related

Restore SQL Server database failed SMO C#

I'm trying to restore a SQL-Server database but I don't know why it's throwing exception which message is "Restore failed for server '.'".
I'm executing two methods. One is defines as CheckDatabase and second one is RestoreDatabase. If I execute RestoreDatabase method first it works fine, but if I execute it after CheckDatabase method it explodes
This is my CheckDatabase():
private static void CheckDatabase(string period)
{
string connString = Config.ConnectionStrings.ConnectionStrings["connString"].ConnectionString;
string controlProcesoCommand = "select top 5 * from ControlProceso order by FechaInicio desc;";
using (SqlConnection conn = new SqlConnection(connString))
using (SqlCommand command = new SqlCommand(controlProcesoCommand, conn))
{
try
{
conn.Open();
var reader = command.ExecuteReader();
while (reader.Read())
{
int lastPeriodDb = int.Parse(reader["Periodo"].ToString());
int actualPeriod = int.Parse(period);
if (period.Equals(reader["Periodo"].ToString()))
throw new Exception(Resources.Messages.Period_already_processed);
else if (lastPeriodDb > actualPeriod)
throw new Exception(Resources.Messages.Period_saved_after_actual_period);
else break;
}
reader.Close();
}
catch (Exception ex)
{
throw ex;
}
finally
{
conn.Close();
conn.Dispose();
command.Cancel();
command.Dispose();
}
}
}
And this is my RestoreDatabase():
private static void RestoreDatabase()
{
try
{
SqlConnection conn = new SqlConnection(Config.ConnectionStrings.ConnectionStrings["connString"].ConnectionString);
string dbName = conn.Database;
string restoreFilePath = Config.AppSettings.Settings["RestoreFilePath"].Value;
Server myServer = new Server(conn.DataSource);
Database itauDatabase = new Database(myServer, dbName);
Restore dbRestore = new Restore();
dbRestore.Action = RestoreActionType.Database;
dbRestore.Database = itauDatabase.Name;
dbRestore.Devices.AddDevice(restoreFilePath, DeviceType.File);
dbRestore.ReplaceDatabase = true;
dbRestore.SqlRestore(myServer);
}
catch (Exception ex)
{
throw ex;
}
}
Each method works fine when I execute them separated.
Thanks!

C# proplem with 32bit executable

I wrote this code using Visual Studio 2015:
static string strconnect = "Dsn=mx86";
static public string strDoc_key = "";
static public bool bPicFounded = false;
OdbcDataAdapter dr = new OdbcDataAdapter();
DataSet ds = new DataSet();
Thread thread = null;
static public OdbcConnection dataConnection = new OdbcConnection(strconnect);
static public string strInitialDirectory = "";
public frmMain()
{
InitializeComponent();
try
{
if (dataConnection.State != ConnectionState.Open)
dataConnection.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void frmMain_Load(object sender, EventArgs e)
{
if (dataConnection.State != ConnectionState.Open)
{
this.Invoke(new MethodInvoker(delegate { this.Close(); }));
}
try
{
// this.Invoke(new MethodInvoker(delegate
// {
DataTable mydt = new DataTable();
using (OdbcCommand ord = new OdbcCommand("", dataConnection))
{
ord.CommandText = "SELECT `AnläggningsNr` GroupID ,`Beskrivning` GroupName from `Utrustningar` " +
"WHERE (((`NivåUpp`)='TOP') AND ((`Enhet`)='00'))";
mydt.Load(ord.ExecuteReader());
cboGroups.SelectedIndexChanged -= cboGroups_SelectedIndexChanged;
if (mydt.Rows.Count > 0)
{
cboGroups.ValueMember = "GroupID";
cboGroups.DisplayMember = "GroupName";
cboGroups.DataSource = mydt.DefaultView;
cboGroups.SelectedIndex = 0;
cboGroups_SelectedIndexChanged(null, null);
}
cboGroups.SelectedIndexChanged += cboGroups_SelectedIndexChanged;
}
}
catch (Exception ex)
{
MessageBox.Show("a)Message is: " + ex.Message);
}
}
When I make x86 build, the combobox is filled with system.data.datarowview but when I make x64 build, it works fine.
What could be the reason for this problem?
When I replace
Display member value from
cboGroups.DisplayMember = "GroupName";
To
cboGroups.DisplayMember = mydt.columns[1].columnname
And make the same with value member it works good and give me the expected results
So when I try to debug to know the difference between the two expressions
I notice that
The string "GroubName" come with null terminated character like "GroubName\0" so mydt.columns[1].column name give the right column name with null terminated character
This is what I noticed
Ithink the proplem in odbc driver which give me this type of errors

Connect to database in sepaerate method of my SqlCommand

I have a form that checks whether values are in a database before adding them. Each field is in a different table, and to keep everything clean, I have a checkExists method for each field. Is there a way to have a separate method that connects to the database, so that I don't have to connect in every field method?
I'd like to do something like this so that my code is less messy:
public void SetConnection()
{
SqlConnection myConnection =
new SqlConnection("user id=[username];" +
"password=[password];" +
"server=[server];" +
"database=[db_name];");
try
{
myConnection.Open();
}
catch(Exception e)
{
Console.WriteLine("Unable to Connect");
}
}
public Boolean CheckData_Company(string[] items)
{
Class_DB set_conn = new Class_DB();
try
{
set_conn.SetConnection();
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
//check that item does not already exist
string query_string = "SELECT * FROM CR_Company WHERE ([CompanyName] = #companyName";
SqlCommand check_Company = new SqlCommand(query_string, set_conn);
check_Company.Parameters.AddWithValue("#CompanyName", items[0]);
int CompanyExist = (int)check_Company.ExecuteScalar();
if(CompanyExist > 0)
{
return true;
}
else
{
return false;
}
}
But I get a
local variable set_conn
Argument 2: Cannot Convert from Class_DB to System.Data.SqlClient.SqlConnection
I understand the error, so what can I do to return the correct value, or do I have to establish a connection within my CheckData_Comany() method?
Your method SetConnection should be returning SqlConnection back like:
public SqlConnection SetConnection()
{
SqlConnection myConnection = new SqlConnection("user id=[username];" +
"password=[password];" +
"server=[server];" +
"database=[db_name];");
try
{
myConnection.Open();
}
catch(Exception e)
{
Console.WriteLine("Unable to Connect");
}
return myConnection;
}
and then you can have something like:
SqlConnection connection = set_conn.SetConnection();
and then pass it in SqlCommand constructor as parameter :
SqlCommand check_Company = new SqlCommand(query_string, connection);
Your complete method implementation would become :
public Boolean CheckData_Company(string[] items)
{
bool Exists = false;
Class_DB set_conn = new Class_DB();
SqlConnection connection = null;
try
{
connection = set_conn.SetConnection();
//check that item does not already exist
string query_string = "SELECT * FROM CR_Company WHERE ([CompanyName] = #companyName";
SqlCommand check_Company = new SqlCommand(query_string, set_conn);
check_Company.Parameters.AddWithValue("#CompanyName", items[0]);
int CompanyExist = (int)check_Company.ExecuteScalar();
if(CompanyExist > 0)
Exists = true;
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
finally
{
connection.Close();
}
return Exists;
}
and important thing to note is do not forget the close the connection finally by calling connection.Close(), otherwise it might cause eating up the resources that shouldn't happen when we are done with querying the database and we should release the resources occupied.

OpenFileDialog loop application only after a DB query

I have an difficult situation :
this is my form :
the first button '...' is a btnAllegato. Code :
private void btnAllegato_Click(object sender, EventArgs e)
{
try
{
using (OpenFileDialog openFileDialog1 = new OpenFileDialog())
{
string path = string.Empty;
openFileDialog1.Title = "Seleziona richiestaIT (PDF)..";
openFileDialog1.Filter = ("PDF (.pdf)|*.pdf");
openFileDialog1.FilterIndex = 1;
openFileDialog1.FileName = "";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
//salva l'intero path
path = openFileDialog1.FileName;
//nome file + estensione
string temp = openFileDialog1.SafeFileName;
//elimina l'estensione del file con IgnoreCase -> case Unsensitive
temp = Regex.Replace(temp, ".pdf", " ", RegexOptions.IgnoreCase);
//datatime + replace
string timenow = System.DateTime.Now.ToString();
//replace data da gg//mm/aaaa ss:mm:hh -----> ad gg-mm-aaaa_ss-mm-hh
timenow = timenow.Replace(":", "-").Replace("/", "-");//.Replace(" ", " ");
_url = #"\\192.168.5.7\dati\SGI\GESTIONE IT\RichiesteIT\" + temp + timenow + ".pdf";
System.IO.File.Copy(path, _url);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
after i have a button Inserisci >> (btnInserisci)
with this button i Create a DB Query to insert data...
private void btnInserisci_Click(object sender, EventArgs e)
{
try
{
if ((_IDRichiedente != -1) && (_data != string.Empty) && (_url != string.Empty))
{
MessageBox.Show(_url);
QueryAssist qa = new QueryAssist();
string query = "INSERT INTO RICHIESTA_IT(ID_Risorsa, descrizione_richiesta, modulo_pdf, data_richiesta) VALUES('" + _IDRichiedente + "', '" + txtBreveDescrizione.Text + "', '" + _url + "', '" + _data + "');";
MessageBox.Show(query);
qa.runQuery(query);
else
{
MessageBox.Show("Selezionare il richiedente,data o allegato!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
where
private int _IDRichiedente = -1;
private string _data = String.Empty;
private string _url = string.Empty;
is a fields of class.
QueryAssist is my class that connect, run query and disconnect to Access DB.
code :
class QueryAssist
{
System.Data.OleDb.OleDbConnection _OleDBconnection;
public QueryAssist()
{
this._OleDBconnection = null;
}
private bool connectionDB()
{
string connection = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=\"\\\\192.168.5.7\\dati\\Scambio\\Sviluppo\\Impostazioni temporanea db Censimento\\CensimentoIT.accdb\"";
try
{
_OleDBconnection = new System.Data.OleDb.OleDbConnection(connection);
_OleDBconnection.Open();
return true;
}
catch(Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
return false;
}
}
private void disconnectDB()
{
try
{
_OleDBconnection.Close();
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
public System.Data.DataTable runQuery(string query)
{
try
{
if (connectionDB())
{
System.Data.DataTable dataTable = new System.Data.DataTable();
System.Data.OleDb.OleDbCommand sqlQuery = new System.Data.OleDb.OleDbCommand(query, _OleDBconnection);
System.Data.OleDb.OleDbDataAdapter adapter = new OleDbDataAdapter(sqlQuery);
adapter.Fill(dataTable);
disconnectDB();
return dataTable;
}
}
catch(Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
return null;
}
public int countRowsQueryResult(string query)
{
try
{
if (connectionDB())
{
System.Data.DataTable dataTable = new System.Data.DataTable();
System.Data.OleDb.OleDbCommand sqlQuery = new System.Data.OleDb.OleDbCommand(query, _OleDBconnection);
System.Data.OleDb.OleDbDataAdapter adapter = new OleDbDataAdapter(sqlQuery);
adapter.Fill(dataTable);
disconnectDB();
return dataTable.Rows.Count;
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
return -1;
}
}
At firt time ... The application work good. I selected a file and other data and I click on button 'Inserisci>>' and all working good.
Next step when i want to insert other data ... when i click on '...' button for attachment a file i have the loop OpenFileDialog
To close, i must kill the process.
I have [STAThread] set on main of the program.
Connect to NAS isn't a problem ... I have try in local .. and i have the same problem..
If i click on btn '...' to OpenFileDialg then not click on button 'Inserisci>>'
OpenFileDialog work good for all time ...
But if i click on button 'Inserisci>>' on the next click on button '...' to OpenFileDialog application loop..
Sorry for bad english ..I'm here for clarification
The use of the runQuery method with an INSERT statement could be the cause of your problems. To insert a record you should use an OleDbCommand with the ExecuteNonQuery. A Fill method is used to fill a DataTable.
The fact that the record is inserted anyway happens because the underlying command used to fill the DataTable (ExecuteReader) ignores its sql command text and executes what you have passed. However after that the Fill method expects to fill a DataTable and not having a select statement could be potentially the cause of your problems.
I would use a different method when you need to Update/Delete or Insert new data
public int runNonQuery(string query)
{
try
{
if (connectionDB())
{
OleDbCommand sqlQuery = new OleDbCommand(query, _OleDBconnection);
int rows = sqlQuery.ExecuteNonQuery();
disconnectDB();
return rows;
}
}
catch(Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
return -1;
}
}
There are other problems in your code and are all connected to the way in which you concatenate together the string to form an sql statement. This is know as the worst practice possible with database code. If you take a bit of your time to investigate how to write a parameterized query you will avoid a lot of future problems.

C# Unable to detach database

I have a class that creates a database that saves the mdf file in a specified location. Then it copies tables from an existing database. Then creates stored procedures from an sql file. Then detaches the database created from the start once the process is done. My problem is that my detach method won't work throwing an exception saying that the database is in use. I have disposed my connections properly.
This is in-line with my previous question.
Here is my class:
Event
private void btnFullBackup_Click(object sender, EventArgs e)
{
progressBar.Value = 0;
lblStatus.Text = "Starting full backup...";
CreateDB("FULL");
progressBar.Value = 20;
lblStatus.Text = "Copying tables...";
CopyTables("FULL");
progressBar.Value = 60;
lblStatus.Text = "Creating stored procedures...";
CreateStoredProcedures("FULL");
progressBar.Value = 70;
progressBar.Value = 80;
DetachBackup("FULL");
lblStatus.Text = "Done";
progressBar.Value = 100;
MessageBox.Show("Backup was created successfully", "",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
Methods used:
void CreateDB(string type)
{
//define and browse location to save mdf
lblStatus.Text = "Creating pysical database...";
FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
folderBrowserDialog.ShowDialog();
lblStatus.Text = "Checking folder permission...";
string selectedFolder = folderBrowserDialog.SelectedPath + "\\";
newBackupLocation = selectedFolder;
//check permission
if (WriteAccessToFolder(selectedFolder) == false)
{
MessageBox.Show("The folder you have chosen does not have write permission", "Monytron",
MessageBoxButtons.OK, MessageBoxIcon.Error);
folderBrowserDialog.ShowDialog();
return;
}
//create DB
lblStatus.Text = "Creating database...";
string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
var query = GetDbCreationQuery(selectedFolder, type);
using (var conn = new SqlConnection(connectionString))
using (var command = new SqlCommand(query, conn))
{
try
{
conn.Open();
command.ExecuteNonQuery();
folderBrowserDialog.Dispose();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
if ((conn.State == ConnectionState.Open))
{
conn.Close();
}
}
}
}
void CopyTables(string backupDBName)
{
string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
var query = CopyQuery(backupDBName + DateTime.Now.ToString("yyyyMMdd"));
using (var conn = new SqlConnection(connectionString))
using (var command = new SqlCommand(query, conn))
{
try
{
conn.Open();
command.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
if ((conn.State == ConnectionState.Open))
{
conn.Close();
}
}
}
}
void CreateStoredProcedures(string type)
{
string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
using (var conn = new SqlConnection(connectionString + ";database=" + type + DateTime.Now.ToString("yyyyMMdd")))
{
string spLocation = File.ReadAllText("CreateStoredProcedures.sql");
Server server = new Server(new ServerConnection(conn));
try
{
server.ConnectionContext.ExecuteNonQuery(spLocation);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
bool DetachBackup(string backupDBName)
{
string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
var builder = new SqlConnectionStringBuilder(connectionString);
string serverName = builder.DataSource;
string dbName = builder.InitialCatalog;
try
{
Server smoServer = new Server(serverName);
smoServer.DetachDatabase(backupDBName + DateTime.Now.ToString("yyyyMMdd"), false);
return true;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return false;
}
}
The Connection to the database is in most cases placed in a pool after using. This way you can re-connect quickly using the same connection string, but on the other hand, I suspect this pool of connections is blocking you from detaching a database.
You can probably do something like this:
Put use master as the last statement in each query against database before you close the connection, or
Modify connection string so it doesn't use pooling (uid=...; pwd=...; pooling=false;)
Hope it helps.
You should first kill connections to the database if you want to keep connection pooling. You can do it setting the database in single user access with rollback_immediate clause before calling the detach method.
Have a look here to use C#:
Is there a way to set the DB as Single User Mode in C#?
Or here to run T-SQL script:
https://serverfault.com/questions/76432/how-can-i-detach-a-database-that-is-in-use

Categories