我的数据库中有两个表,一个记录异常,另一个表记录日志消息。
我正在利用SqlDependency
对象在这些表发生更改时收到通知,以便我可以更新我的Web仪表板。我得到了这个工作:
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!");
}
这很好用,所以随着我的风帆,我接下来就为日志信息做了类似的事情:
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!");
}
此时,我只会在日志表发生变化时收到警报。通过混合中的这个额外SqlDependency
,ELMAHdependency_OnChange
永远不会被调用。如果我注释掉我的GetLogMessages()
方法,则会再次调用ELMAHdependency_OnChange
。
看起来多个SqlDependency
个对象是互斥的。关于如何同时监控两个表的任何想法?
答案 0 :(得分:3)
可以使用分号连接另一个SqlStatement。
以下是代码中的代码段,包含我的更改。
[...]
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))
{
[...]
一旦调用该事件,重新注册SqlDependency
也很重要。否则事件只会被触发一次..
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()
将包含设置SqlDependency
。
答案 1 :(得分:0)
使用从两个表而不是查询中选择的存储过程。
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
然后在.NET端执行此操作(原谅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
通过使用存储过程,您没有像使用一致的select语句那样移动所有这些记录。每次修改2个表中的任何一个时,您都会收到通知,因此您必须深入了解结果以确定哪个表已更改。