Cannot monitor multiple tables with SqlDependency - c#

I have two tables in my db, one that records exceptions, and another that records log messages.
I am leveraging the SqlDependency object to be notified when those tables change so that I can update my web dashboard. I got this working:
public IEnumerable<ElmahException> GetExceptions()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["elmah-sqlserver"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT [ErrorId],[Application],[Host],[Type],[Source],[Message],[User],[StatusCode],[TimeUtc],[Sequence],[AllXml]
FROM [dbo].[ELMAH_Error] ORDER BY [TimeUtc] desc", connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(ELMAHdependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
return reader.Cast<IDataRecord>()
.Select(x => new ElmahException()
{
ErrorId = x.GetGuid(0),
Application = x.GetString(1),
Host = x.GetString(2),
Type = x.GetString(3),
Source = x.GetString(4),
Error = x.GetString(5),
User = x.GetString(6),
Code = x.GetInt32(7),
TimeStamp = x.GetDateTime(8).ToString().Replace("T", " ")
}).ToList();
}
}
}
private void ELMAHdependency_OnChange(object sender, SqlNotificationEventArgs e)
{
Console.Write("Exception table changed!");
}
This is working well, so with the wind in my sails, I then took a crack at doing something similar for the log messages:
public IEnumerable<LogMessage> GetLogMessages()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["elmah-sqlserver"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT [application],[time_stamp],[logLevel],[logger],[message]
FROM [dbo].[LogTable] ORDER BY [time_stamp] desc", connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(NLOGdependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
return reader.Cast<IDataRecord>()
.Select(x => new LogMessage()
{
Application = x.GetString(0),
TimeStamp = x.GetDateTime(1).ToString().Replace("T", " "),
LogLevel = x.GetString(2),
Logger = x.GetString(3),
Message = x.GetString(4)
}).ToList();
}
}
}
private void NLOGdependency_OnChange(object sender, SqlNotificationEventArgs e)
{
Console.Write("Log table has changed!");
}
At this point, I am alerted only to when the log table has changed. With this additional SqlDependency in the mix, ELMAHdependency_OnChange never gets called. If I comment out my GetLogMessages() method, then ELMAHdependency_OnChange is called once more.
It looks like multiple SqlDependency objects are mutually exclusive. Any ideas on how I can monitor two tables at the same time?

It is possible to concatenate another SqlStatement using a semicolon.
Here's a snippet from your code, with my changes.
[...]
connection.Open();
var queries = new [] {#"SELECT [application],[time_stamp],[logLevel],[logger],[message] FROM [dbo].[LogTable] ORDER BY [time_stamp] desc",
#"SELECT [ErrorId],[Application],[Host],[Type],[Source],[Message],[User],[StatusCode],[TimeUtc],[Sequence],[AllXml] FROM [dbo].[ELMAH_Error] ORDER BY [TimeUtc] desc"};
using (SqlCommand command = new SqlCommand(string.Join("; ", queries), connection))
{
[...]
It's also important to re-register the SqlDependency once it has called the event. Or else the event is only triggered once..
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = sender as SqlDependency;
if (dependency != null) dependency.OnChange -= dependency_OnChange;
if (e.Type == SqlNotificationType.Change)
{
// Do things
}
SetupDatabaseDependency();
}
SetupDatabaseDependency() would contain the code to set up the SqlDependency.

Use a stored procedure that selects from both tables instead of a query.
CREATE PROCEDURE [dbo].[SQLDependency_TestTable1_TestTable2]
#MaxIdxTestTable1 INT = 1, #MaxIdxTestTable2 INT = 1
AS
-- Don't do this - SQLDependency doesn't like.
--SET TRANSACTION ISOLATION LEVEL READ COMMITTED
-- Don't do this either - SQLDependency doesn't like.
--SELECT MAX(ID) FROM ehmetrology.TestTable1
--SELECT COUNT(ID) FROM ehmetrology.TestTable1
-- See here for a whole list of things SQLDependency doesn't like:
-- stackoverflow.com/questions/7588572/what-are-the-limitations-of-sqldependency/7588660#7588660
SELECT DCIdx FROM TestTable1 WHERE Idx >= #MaxIdxTestTable1
ORDER BY DCIdx DESC;
SELECT DCIdx FROM TestTable2 WHERE Idx >= #MaxIdxTestTable2
ORDER BY DCIdx DESC;
GO
And then do this on the .NET side (pardon the VB):
Using adapter As New SqlDataAdapter(mSQLD_Command)
adapter.Fill(mSQLD_DataSet, SQLD_DATASET_TABLENAME)
End Using
' Reload the dataset that's bound to the grid.
If mSQLD_DataSet.Tables.Count = 2 Then
Dim iTest1Index As Integer = 0
Dim iTest2Index As Integer = 0
If Integer.TryParse(mSQLD_DataSet.Tables(0).Rows(0).Item(0).ToString, iTest1Index) Then
If iTest1Index<> moTest1.MaxDCIdx Then
GetTest1Data(True)
End If
End If
If Integer.TryParse(mSQLD_DataSet.Tables(1).Rows(0).Item(0).ToString, iTest2Index) Then
If iTest2Index <> moTest2.MaxDCIdx Then
GetTest2Data()
End If
End If
End If
By using a stored procedure, you don't have all those records moving around as you do with a consistent select statement. You'll get notified each time either one of the 2 tables are modified, so you have to dig into the result to figure out which one has changed.

Related

Would this code waste resources?

I am using gridview to select multiple records and then play loop over it to individually send each record ID to database and update it but I don't find it very good way to implement because it opens and close connection each time so what is the good way ?
foreach (GridViewRow r in grdViewLastHearingDates.Rows)
{
int CaseHearingID = Convert.ToInt32(r.Cells[0].Text);
CheckBox chkBox = r.FindControl("chkBoxIsConveyed") as CheckBox;
TextBox txtboxConvenienceRemarks = r.FindControl("txtBoxConvenienceRemarks") as TextBox;
string ConvenienceRemarks = txtboxConvenienceRemarks.Text;
MngCaseHearings.UpdateCasesIsConveyed(CaseHearingID, ConvenienceRemarks, chkBox.Checked);
}
MngCaseHearings.UpdateCasesIsConveyed sends and executes this code every time for updating each ID. Please review and give suggestions
public Boolean UpdateCasesIsConveyed(int CaseHearingID, string ConvenienceRemarks, bool IsConveyed)
{
try
{
SqlCommand SqlCom = new SqlCommand("UpdateCasesIsConveyed", DatabaseConnection.OpenConnection());
SqlCom.CommandType = CommandType.StoredProcedure;
SqlCom.Parameters.AddWithValue("#pk_CaseHearings_ID ", CaseHearingID);
SqlCom.Parameters.AddWithValue("#IsConveyed", IsConveyed);
SqlCom.Parameters.AddWithValue("#ConvenienceRemarks", ConvenienceRemarks);
SqlParameter SqlParamReturnStatus = new SqlParameter("#ReturnStatus", SqlDbType.Bit);
SqlCom.Parameters.Add(SqlParamReturnStatus);
SqlParamReturnStatus.Direction = ParameterDirection.Output;
SqlParameter SqlParamReturnStatusMessage = new SqlParameter("#ReturnStatusMessage", SqlDbType.VarChar, -1);
SqlCom.Parameters.Add(SqlParamReturnStatusMessage);
SqlParamReturnStatusMessage.Direction = ParameterDirection.Output;
SqlCom.ExecuteNonQuery();
string ReturnStatusMessage = Convert.ToString(SqlParamReturnStatusMessage);
Boolean ReturnStatus = Convert.ToBoolean(SqlParamReturnStatus.Value);
return ReturnStatus;
}
catch (Exception)
{
throw;
}
finally
{
DatabaseConnection.CloseConnection();
}
this would waste the resources so if any good work around ?
Since the SqlConnection and SqlCommand objects both implement IDisposable you should probably dispose of them when you're done. The simplest way would be via the using statement:
using (var conn = DatabaseConnection.OpenConnection())
using (var SqlCom = new SqlCommand("UpdateCasesIsConveyed", conn))
{
// setup and execute the SP, can return from in here
}
This will ensure that the resources used by the objects are properly closed as soon as you are done. While this isn't strictly necessary - the Dispose method will be called when the garbage collector destroys the objects - it will ensure that you aren't holding open the database objects any longer than necessary. Depending on how often this is called you can end up with resource shortages on the SQL server, excessive handle usage, etc.
In general anything that implements IDisposable should be disposed as soon as practical.
(Sorry, missed the loop part of the question)
This type of create/destroy cycle is fine for single operations, but becomes wasteful when used to update a lot of records. I would put the loop in the middle of the code rather than repeatedly calling this code from outside.
I would create a record class or struct that holds the SP parameters and pass an IEnumerable of that record class to your update method. This way you can do the setup once, process all of the updates, then tear down the database objects after all of the changes have been made. Throw a transaction in too so you can undo it all if one of the records fails.
Something like:
public struct UpdateCaseConveyanceRec
{
public int CaseHearingID;
public string ConvenienceRemarks;
public bool IsConveyed;
}
public bool UpdateCasesIsConveyed(IEnumerable<UpdateCaseConveyanceRec> uopdates)
{
using (SqlConnection conn = DatabaseConnection.OpenConnection())
using (SqlCommand cmd = new SqlCommand("UpdateCasesIsConveyed", conn))
using (SqlTransaction trans = conn.BeginTransaction("UpdateCasesIsConveyed"))
{
cmd.CommandType = CommandType.StoredProcedure;
var pID = cmd.Parameters.Add("#pk_CaseHearings_ID", SqlDbType.Int);
var pConveyed = cmd.Parameters.Add("#IsConveyed", SqlDbType.Bit);
var pRemarks = cmd.Parameters.Add("#ConvenienceRemarks", SqlDbType.VarChar, -1);
var retStatus = cmd.Parameters.Add("#ReturnStatus", SqlDbType.Bit);
retStatus.Direction = ParameterDirection.Output;
var retStatusMsg = cmd.Parameters.Add("#ReturnStatusMessage", SqlDbType.VarChar, -1);
retStatusMsg.Direction = ParameterDirection.Output;
try
{
foreach (var row in updates)
{
pID.Value = row.CaseHearingID;
pConveyed.Value = row.IsConveyed;
pRemarks.Value = row.ConvenienceRemarks;
cmd.ExecuteNonQuery();
if (!Convert.ToBoolean(retStatus))
{
trans.Rollback();
return false;
}
}
trans.Commit();
}
catch ()
{
trans.Rollback();
throw;
}
return true;
}
}
You can then feed that with a LINQ to Objects query:
var source =
from r in grdViewLastHearingDates.Rows.OfType<GridViewRow>()
select new UpdateCaseConveyanceRec
{
CaseHearingID = Convert.ToInt32(r.Cells[0].Text),
ConvenienceRemarks = (r.FindControl("txtBoxConvenienceRemarks") as TextBox).Text;
IsConveyed = (r.FindControl("chkBoxIsConveyed") as CheckBox).Checked
};
bool updated = UpdateCasesIsConveyed(source);

SQLDependency OnChangeEvent not firing

I have the following code to execute a SqlCommand against a database View:
public IEnumerable<PickNote> GetData()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["BTNInternalData_LiveEntities"].ConnectionString))
{
connection.Open();
using (var command = new SqlCommand(#"SELECT [PICKINGROUTEID],[CUSTOMER],[SALESNAME]
FROM [dbo].[PickScreenData] WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, [ACTIVATIONDATETIME])) = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) or [EXPEDITIONSTATUS] = 3", connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
{
var data = reader.Cast<IDataRecord>();
return data.Select(x => new PickNote
{
pickingRouteId = x["PICKINGROUTEID"].ToString()
}).ToList();
}
}
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
// do stuff
}
However, the dependency_OnChange method only gets called at the start of my application and doesn't do so again no matter if the data in my View changes. I've debugged SqlNotificationEventArgs and the Info is Invalid but I'm not sure why as the command query runs fine
Edit
I've changed the query so that it queries the table directly but SqlNotificationEventArgs.Info still says Invalid. Here is the new code:
public IEnumerable<PickNote> GetData()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["AXLive"].ConnectionString))
{
connection.Open();
using (var command = new SqlCommand(#"
SELECT PICKINGROUTEID, EXPEDITIONSTATUS
FROM [dbo].[WMSPICKINGROUTE]
WHERE (EXPEDITIONSTATUS <> 20)
AND (DATEADD(dd, 0, DATEDIFF(dd, 0, [ACTIVATIONDATETIME])) = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
OR [EXPEDITIONSTATUS] = 3)", connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
{
var data = reader.Cast<IDataRecord>();
return data.Select(x => new PickNote
{
pickingRouteId = x["PICKINGROUTEID"].ToString()
}).ToList();
}
}
}
}
WMSPICKINGROUTE is the table where my view was getting the data from before.
According to this MSDN page, you cannot use a SqlDependency against a select statement that references a view. That seems to be the problem. Rewrite your query to hit tables and it should work.

SqlDependency constant database hits

I have just copied the example from an official Microsoft source http://msdn.microsoft.com/en-us/library/a52dhwx7(v=vs.80).aspx and I am baffled by it. Having run the application, it makes constant database hits even though nothing is using that table? I thought when that table actually changes the event will fire? I don't want it to make constant DB calls every second, that is awful.
Have I done something wrong? I imagine I have not sure what though. Anybody have a link to a good example preferably not MSDN.
Thanks in advance, Onam.
This is the SQL:
return "SELECT [ID],[FromMachine],[FromStore],[FromUser] FROM dbo.Store_Message";
As requested, all code:
public partial class Form1 : Form
{
string connectionString = "server=localhost;database=usicoal;uid=admin;password=";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
GetNames();
}
private bool DoesUserHavePermission()
{
try
{
SqlClientPermission clientPermission = new SqlClientPermission(PermissionState.Unrestricted);
clientPermission.Demand();
return true;
}
catch
{
return false;
}
}
void dep_OnChange(object sender, SqlNotificationEventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new MethodInvoker(GetNames));
}
else
{
GetNames();
}
SqlDependency dep = sender as SqlDependency;
dep.OnChange -= new OnChangeEventHandler(dep_OnChange);
}
private void GetNames()
{
if (!DoesUserHavePermission())
return;
SqlDependency.Stop(connectionString);
SqlDependency.Start(connectionString);
using (SqlConnection cn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = cn.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT ID FROM dbo.[BTE_SIMPLE_STORE_MESSAGE]";
cmd.Notification = null;
SqlDependency dep = new SqlDependency(cmd);
dep.OnChange += new OnChangeEventHandler(dep_OnChange);
cn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
}
}
}
}
}
}
SqlDependency usually works as you expected it. It fires events if something was changed in your db. Constant calls occure if something is wrong with your query. It's important to use the full two part table name (e.g. dbo.TableName).
The MSDN documentation is not as bad - have a look at this more up-to-date article or this one version.
Found that the above code was fine when I:
Right click database select properties
Select options
Set compatibility level to SQL Server 2008(100)
OK
Bang the code works correctly.

SQLDependency_OnChange-Event fires only one single Time

I'm working with SQLDependency to notify me if there is a change in the Database.
After Program Start-Up it works just fine. When I make a first change the Event fires. Wohoo... that's great.
But if I made a second change the event doesn't fire again. I've searched all the web I think but haven't found anything about THIS Problem. Only found problems where the OnChange-Event fires in a Loop.
Can anyone help me?
Here a little code piece:
private void GetStates()
{
if (!DoesUserHavePermission())
return;
SqlDependency.Stop(con);
SqlDependency.Start(con);
using (SqlConnection cn = new SqlConnection(con))
{
using (SqlCommand cmd = cn.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT Bla, Bla2, ..FROM dbo.[BLA3]"
cmd.Notification = null;
cmd.Dispose();
SqlDependency dep = new SqlDependency(cmd);
dep.OnChange += new OnChangeEventHandler(dep_OnChange);
cn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
state.Clear(); //In this Case "state" is a List<string>
while (dr.Read())
{
state.Add(dr.GetString(0) + "|" + dr.GetInt32(3));
}
dr.Dispose();
dr.Close();
}
}
}
}
my OnChange-Event looks like this:
private void dep_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dep = sender as SqlDependency;
dep.OnChange -= this.dep_OnChange;
using (SqlConnection cn = new SqlConnection(con))
{
using (SqlCommand cmd = cn.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT Bla, Bla2, ..FROM dbo.[BLA3]";
cmd.Notification = null;
if (e.Type == SqlNotificationType.Change)
{
if (cn.State != ConnectionState.Open)
{
cn.Open();
}
using (SqlDataReader dr = cmd.ExecuteReader())
{
state.Clear(); // Clear and Refill the stringlist "state"
while (dr.Read())
{
state.Add(dr.GetString(0) + "|" + dr.GetInt32(3));
}
}
}
cn.Close();
}
}
this.GetStates(); //to go ahead and wait for a new change
}
Where is the problem?
I was running into this issue as well. You need to create a new SqlDependency entity (after unsubscribing the existing one from the OnChange event) and then run a new ExecuteReader command. I got the idea from this post:
http://www.codeproject.com/Articles/12335/Using-SqlDependency-for-data-change-events
This usually makes sense, as once you have been notified of a change you will normally want to re-query the data.
After changes happened to the database at the first time, you have to execute the command again and re-subscribe to the event.
The following code is working for me.
class Program
{
static string connectionString = "Server=.;Database=test_sql_dependency;Integrated Security=True;";
static void Main(string[] args)
{
// 1. create database
// 2. enable service broker by executing this sql command on the database.
// alter database test_sql_dependency set enable_broker
// 3. start sql dependency, for some sql server connection string or with queue if you want.
//var queueName = "myFirstQueue";
//SqlDependency.Start(connectionString, queueName);
SqlDependency.Start(connectionString);
// complete the rest of the steps in seperate method to be able to call it again when you need to
// re-subscribe to the event again, becuase by default it will be executed only one time
RegisterSqlDependency();
Console.WriteLine("Listening to database changes...");
Console.ReadLine();
}
static void RegisterSqlDependency()
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
if (connection.State != System.Data.ConnectionState.Open)
{
connection.Open();
}
// 4. create a sql command
// you can't say select *, and also you have to specefy the db owner (dbo.)
SqlCommand command = new SqlCommand("select Id, Name from dbo.Employee", connection);
// 5. create dependency and associtate it to the sql command
SqlDependency dependency = new SqlDependency(command);
// 6. subscribe to sql dependency event
dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);
// 7. execute the command
using (SqlDataReader reader = command.ExecuteReader())
{
}
}
}
static void OnDependencyChange(object sender, SqlNotificationEventArgs e)
{
var InsertOrUpdateOrDelte = e.Info;
//-----------------------------Finally-------------------------
// after you knew that there is a change happened
// you have to unsubscribe the event and execute the command again and then re-subscribe to the event
// 1. unsubscribe the event
SqlDependency dependency = sender as SqlDependency;
dependency.OnChange -= OnDependencyChange;
// 2. re-subscribe to the event and execute the command again
RegisterSqlDependency();
}
}
In your private void dep_OnChange(object sender, SqlNotificationEventArgs e) method after you unsubscribe the dep_OnChange event you should call the private void GetStates() again, to initialize the dep.OnChange event again.
Not sure if that is your problem but you dispose the command right after you have created it:
using (SqlCommand cmd = cn.CreateCommand())
{
...
cmd.Dispose();
It looks like a bug.
In GetStates() :
SqlDependency.Stop(con);
SqlDependency.Start(con);
these lines should be executed only when registering the sql dependency for first time.
Restrict them when you call the method from OnChange event.
Look my friend:
dep.OnChange -= this.dep_OnChange;
you un-fired your event; which is not true;
just delete this line;

Object reference not set to an instance of object

I have a function like this
public void GetTablesWithUpperCaseName()
{
SqlConnectionStringBuilder objConnectionString = new SqlConnectionStringBuilder();
objConnectionString.DataSource = txtHost.Text;
objConnectionString.UserID = txtUsername.Text;
objConnectionString.Password = txtPassword.Text;
objConnectionString.InitialCatalog = Convert.ToString(cmbDatabases.SelectedValue);
SqlConnection sConnection = new SqlConnection(objConnectionString.ConnectionString);
//To Open the connection.
sConnection.Open();
//Query to select table_names that have their names in uppercase letters.
string selectTablesWithUppercaseName = #"SELECT
NAME
FROM
sysobjects
WHERE
UPPER(name) COLLATE Latin1_General_BIN = name COLLATE Latin1_General_BIN
AND
OBJECTPROPERTY(ID,N'IsTable')=1
AND
OBJECTPROPERTY(ID,N'IsMSShipped')=0 ";
//Create the command object
SqlCommand sCommand = new SqlCommand(selectTablesWithUppercaseName, sConnection);
try
{
//Create the dataset
DataSet dsListOfTablesWithUppercaseName = new DataSet("sysobjects");
//Create the dataadapter object
SqlDataAdapter sDataAdapter = new SqlDataAdapter(selectTablesWithUppercaseName, sConnection);
//Provides the master mapping between the sourcr table and system.data.datatable
sDataAdapter.TableMappings.Add("Table", "sysobjects");
//Fill the dataset
sDataAdapter.Fill(dsListOfTablesWithUppercaseName);
//Bind the result combobox with foreign key table names
DataViewManager dvmListOfForeignKeys = dsListOfTablesWithUppercaseName.DefaultViewManager;
dgResultView.DataSource = dsListOfTablesWithUppercaseName.Tables["sysobjects"];
}
catch(Exception ex)
{
//All the exceptions are handled and written in the EventLog.
EventLog log = new EventLog("Application");
log.Source = "MFDBAnalyser";
log.WriteEntry(ex.Message);
}
finally
{
//If connection is not closed then close the connection
if(sConnection.State != ConnectionState.Closed)
{
sConnection.Close();
}
}
}
And another function for counting the rows generated from the previous functions. But this function
Null Reference Exception or Object
reference not set to an instance of
object..
Can anyone help me in this... why it is catching error only for the functions above and working fine for all other similar functions.
private void UpdateLabelText()
{
SqlConnectionStringBuilder objConnectionString = new SqlConnectionStringBuilder();
objConnectionString.DataSource = txtHost.Text;
objConnectionString.UserID = txtUsername.Text;
objConnectionString.Password = txtPassword.Text;
objConnectionString.InitialCatalog = Convert.ToString(cmbDatabases.SelectedValue);
SqlConnection sConnection = new SqlConnection(objConnectionString.ConnectionString);
//To Open the connection.
sConnection.Open();
try
{
int SelectedCellTotal = 0;
int counter;
// Iterate through the SelectedCells collection and sum up the values.
for(counter = 0;counter < (dgResultView.SelectedCells.Count);counter++)
{
if(dgResultView.SelectedCells[counter].FormattedValueType == Type.GetType("System.String"))
{
string value = null;
// If the cell contains a value that has not been commited,
if(dgResultView.IsCurrentCellDirty == true)
{
value = dgResultView.SelectedCells[counter].EditedFormattedValue.ToString();
}
else
{
value = dgResultView.SelectedCells[counter].FormattedValue.ToString();
}
if(value != null)
{
// Ignore cells in the Description column.
if(dgResultView.SelectedCells[counter].ColumnIndex != dgResultView.Columns["TABLE_NAME"].Index)
{
if(value.Length != 0)
{
SelectedCellTotal += int.Parse(value);
}
}
}
}
}
// Set the labels to reflect the current state of the DataGridView.
lblDisplay.Text = "There are Total " + dgResultView.RowCount + cmbOperations.SelectedItem.ToString();
}
catch(Exception ex)
{
//All the exceptions are handled and written in the EventLog.
EventLog log = new EventLog("Application");
log.Source = "MFDBAnalyser";
log.WriteEntry(ex.Message);
}
finally
{
//If connection is not closed then close the connection
if(sConnection.State != ConnectionState.Closed)
{
sConnection.Close();
}
}
}
Also the lblDisplay.Text is not taking proper spaces.
Waiting for reply
OK, I don't really have an answer why you're getting a "null reference exception" - but a few points to throw in, nonetheless:
I would use sys.tables instead of sysobjects and having to specify what type of object to query for
ALWAYS put your disposable SqlConnection and SqlCommand into using(.....) { ...... } blocks. That way, you won't need any finally {..} blocks, and .NET will take care of properly disposing of those objects when they're no longer needed
why do you use a DataSet when you only have a single table inside?? That's just unnecessary overhead - use a DataTable instead!
don't open the SqlConnection that early - wait 'til the very last moment, open it, execute query, close it again right away
actually, when using the SqlDataAdapter, you don't need to open the SqlConnection yourself at all - the SqlDataAdapter will do that for you (and close it again after it is done reading the data)
do not mix the retrieval of the data from the database with the binding to the UI element - this is a very bad practice. From the GetTablesWithUpperCaseName method, you should return something (like a DataTable) to the caller (the UI) and let the UI handle the binding process
along the same lines: that method should not be grabbing stuff from UI elements (like text boxes) itself - pass in those values as method parameters, to get a cleaner code - one that you might actually be able to reuse in another project some day
This is how I think your first method ought to look like
public DataTable GetTablesWithUpperCaseName(string server, string database,
string username, string password)
{
// Create the datatable
DataTable dtListOfTablesWithUppercaseName = new DataTable("tableNames");
SqlConnectionStringBuilder objConnectionString = new SqlConnectionStringBuilder();
objConnectionString.DataSource = server;;
objConnectionString.UserID = username;
objConnectionString.Password = password;
objConnectionString.InitialCatalog = database;
// Define the Query against sys.tables - much easier and cleaner!
string selectTablesWithUppercaseName =
"SELECT NAME FROM sys.tables WHERE UPPER(name) COLLATE Latin1_General_BIN = name COLLATE Latin1_General_BIN AND is_msshipped = 0";
// put your SqlConnection and SqlCommand into using blocks!
using (SqlConnection sConnection = new SqlConnection(objConnectionString.ConnectionString))
using (SqlCommand sCommand = new SqlCommand(selectTablesWithUppercaseName, sConnection))
{
try
{
// Create the dataadapter object
SqlDataAdapter sDataAdapter = new SqlDataAdapter(selectTablesWithUppercaseName, sConnection);
// Fill the datatable - no need to open the connection, the SqlDataAdapter will do that all by itself
// (and also close it again after it is done)
sDataAdapter.Fill(dtListOfTablesWithUppercaseName);
}
catch (Exception ex)
{
//All the exceptions are handled and written in the EventLog.
EventLog log = new EventLog("Application");
log.Source = "MFDBAnalyser";
log.WriteEntry(ex.Message);
}
}
// return the data table to the caller
return dtListOfTablesWithUppercaseName;
}

Categories

Resources