SqlDependency fire correctly, but also at start time - c#

I'm getting some troubles in the last days to use the SQL Dependency.
With the sample in this link I have ALMOST reach my goal. "Almost" because the event get fired correctly, but also at the startup, when I call the Start method of the MyService class. Here there is my code, but as you can see it's the same (or almost) of the link.
I think I can simply insert a bool value, only to avoid the initialitation event, but I think also that another solution would be better.
SQLWatcher:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public enum SqlWatcherNotificationType
{
Blocking,
Threaded // Launch in another thread so SqlWatcher can immediately start monitoring again.
}
public class SqlWatcher : IDisposable
{
private string ConnectionString;
private SqlConnection Connection;
private SqlCommand Command;
private SqlDataAdapter Adapter;
private DataSet Result;
private SqlWatcherNotificationType NotificationType;
public SqlWatcher(string ConnectionString, SqlCommand Command, SqlWatcherNotificationType NotificationType)
{
this.NotificationType = NotificationType;
this.ConnectionString = ConnectionString;
SqlDependency.Start(this.ConnectionString);
this.Connection = new SqlConnection(this.ConnectionString);
this.Connection.Open();
this.Command = Command;
this.Command.Connection = this.Connection;
Adapter = new SqlDataAdapter(this.Command);
}
public void Start()
{
RegisterForChanges();
}
public void Stop()
{
SqlDependency.Stop(this.ConnectionString);
}
public delegate void SqlWatcherEventHandler(DataSet Result);
public event SqlWatcherEventHandler OnChange;
public DataSet DataSet
{
get { return Result; }
}
private void RegisterForChanges()
{
//Remove old dependency object
this.Command.Notification = null;
//Create new dependency object
SqlDependency dep = new SqlDependency(this.Command);
dep.OnChange += new OnChangeEventHandler(Handle_OnChange);
//Save data
Result = new DataSet();
Adapter.Fill(Result);
//Notify client of change to DataSet
switch (NotificationType)
{
case SqlWatcherNotificationType.Blocking:
OnChange(Result);
break;
case SqlWatcherNotificationType.Threaded:
ThreadPool.QueueUserWorkItem(ChangeEventWrapper, Result);
break;
}
}
public void ChangeEventWrapper(object state)
{
DataSet Result = (DataSet)state;
OnChange(Result);
}
private void Handle_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type != SqlNotificationType.Change)
throw new ApplicationException("Failed to create queue notification subscription!");
//Clean up the old notification
SqlDependency dep = (SqlDependency)sender;
dep.OnChange -= Handle_OnChange;
//Register for the new notification
RegisterForChanges();
}
public void Dispose()
{
Stop();
}
}
and this is MyService class:
public class MyService
{
private static SqlWatcher SqlQueueWatcher;
public static void Start()
{
string connS = MainWindow.dbContext.Database.Connection.ConnectionString + "Password=111;";
//Build the command object we want to monitor (don't include a SqlConnection)
SqlCommand cmd = new SqlCommand();
cmd = new SqlCommand("SELECT CODVEI FROM dbo.ArchivioErogazioni");
cmd.CommandType = CommandType.Text;
//Setup the SQLWatcher
SqlQueueWatcher = new SqlWatcher(connS, cmd, SqlWatcherNotificationType.Blocking);
SqlQueueWatcher.OnChange += new SqlWatcher.SqlWatcherEventHandler(QueueSQLWatcher_OnChange);
SqlQueueWatcher.Start();
}
private static void QueueSQLWatcher_OnChange(DataSet Result)
{
//Do something with the updated DataSet object
Debug.WriteLine("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); ---> ENTERS HERE IN THE INIT PHASE
}
public static void Stop()
{
SqlQueueWatcher.Dispose();
}
}
and this is how I symply call it:
MyService.Start();
Is the bool flag a bad idea? Why does it enter in QueueSQLWatcher_OnChange at startup time?

To fix this issue, you need only to set the notification for that SqlCommand to null.
cmd.Notification = null;
Just try this and i think your problem will be resolved. Enter this line before starting the dependency using the SqlWatcher class

Related

SignalR send duplicate message to same user n times n is number of users connected .NetCore

I have SignalR ASP.NET Core project and trying to send message tp specific user which I manage to do so. The problem is same message send n time n=number of tabs or connections when ever there is change from database it send notification to that certain User but duplicate the message.
public class MessageHub: Hub
{
public MessageHub(IConfiguration cc, IHubContext<MessageHub> _mess, UserManager<ApplicationUser> rep)
{
mess = _mess;
_conf = cc;
_usermanager = rep;
RegisterNotification();
}
public void RegisterNotification()
{
using (SqlConnection con = new SqlConnection(conStr))
{
SqlCommand cmd = new SqlCommand(sqlCommand, con);
if (con.State != System.Data.ConnectionState.Open)
{
con.Open();
}
cmd.Notification = null;
SqlDependency sqlDep = new SqlDependency(cmd);
sqlDep.OnChange += sqlDep_OnChange;
using (SqlDataReader reader = cmd.ExecuteReader())
{
// nothing need to add here now
}
}
}
static int count = 1;
private void sqlDep_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change )
{
SqlDependency sqlDep = sender as SqlDependency;
sqlDep.OnChange -= sqlDep_OnChange;
mess.Clients.User("64ed09d7-255f-4aae-a25f-7a50e59943b4").SendAsync("abc", "hello"+count);
count += 1;
RegisterNotification();
}
}
}
as you have mentioned, the problem is RegisterNotification is being called everytime an instance of MessageHub is created. Instead of this, you can create background service and register to SqlDependency there and send the events using IHubContext. And after every notification, you call RegisterNotification again, which might cause the duplication as well.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services
https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext
I haven't tested this code, but here is an example:
public class EventBroadcaster : IHostedService
{
private readonly IHubContext<MessageHub> _hubContext;
private readonly string _connectionString;
public EventBroadcaster(
IConfiguration configuration,
IHubContext<MessageHub> hubContext)
{
_hubContext = hubContext;
_connectionString = // Get Connection String from configuration
}
public Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Started event broadcasting service");
RegisterNotification();
return Task.CompletedTask;
}
public void RegisterNotification()
{
using (SqlConnection con = new SqlConnection(_connectionString))
{
SqlCommand cmd = new SqlCommand(sqlCommand, con);
if (con.State != System.Data.ConnectionState.Open)
{
con.Open();
}
cmd.Notification = null;
SqlDependency sqlDep = new SqlDependency(cmd);
sqlDep.OnChange += sqlDep_OnChange;
using (SqlDataReader reader = cmd.ExecuteReader())
{
// nothing need to add here now
}
}
}
static int count = 1;
private async void sqlDep_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change )
{
SqlDependency sqlDep = sender as SqlDependency;
sqlDep.OnChange -= sqlDep_OnChange;
await _hubContext.Clients.User("64ed09d7-255f-4aae-a25f-7a50e59943b4").SendAsync("abc", "hello"+count);
count += 1;
}
}
public Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Event Broadcasting Service is stopping.");
// TODO: Stop listening to SqlDependency notifications
return Task.CompletedTask;
}

Get the updated database records automatically by a windows form application using c#

I am developing a windows software and i have a database. So what i want is , if the database records are updated my windows application should detect it automatically and retrieve those updated records. can anybody provide a solution for this?
Check SqlDependency Class
in Sql:
ALTER DATABASE SampleDB SET SINGLE_USER;
ALTER DATABASE SampleDB SET ENABLE_BROKER;
ALTER DATABASE SampleDB SET MULTI_USER;
CREATE QUEUE myTableMessages;
CREATE SERVICE myTableNotifications ON QUEUE myTableMessages("http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification");
////
in visual:
using System;
using System.Data;
using System.Data.SqlClient;
namespace NotifyService
{
public class SqlNotifier : IDisposable
{
public SqlCommand SqlCmd { get; set; }
public SqlConnection SqlCon { get; set; }
public void StartMonitoring()
{
SqlDependency.Start(this.SqlCon.ConnectionString);
RegisterDependency();
}
private event EventHandler<SqlNotificationEventArgs> _NewNotification;
public event EventHandler<SqlNotificationEventArgs> NewNotification
{
add { this._NewNotification += value; }
remove { this._NewNotification -= value; }
}
public virtual void OnNewNotification(SqlNotificationEventArgs notification)
{
if (this._NewNotification != null)
this._NewNotification(this, notification);
}
public void RegisterDependency()
{
this.SqlCmd = new SqlCommand(SqlCmd.CommandText,this.SqlCon);
this.SqlCmd.Notification = null;
SqlDependency dependency = new SqlDependency(this.SqlCmd);
dependency.OnChange += this.Dependency_OnChange;
if (this.SqlCon.State == ConnectionState.Closed)
this.SqlCon.Open();
this.SqlCmd.ExecuteReader();
this.SqlCon.Close();
}
private void Dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= new OnChangeEventHandler(Dependency_OnChange);
this.OnNewNotification(e);
RegisterDependency();
}
public void Dispose()
{
SqlDependency.Stop(this.SqlCon.ConnectionString);
}
}
}
///
Use:
using NotifyService;
private SqlNotifier NS = new SqlNotifier();
NS.SqlCon = new SqlConnection(#"Data Source=127.0.0.1;Initial Catalog=SampleDB;Persist Security Info=True;User ID=sa;Password=***");
NS.SqlCmd = new SqlCommand("SELECT id, name FROM dbo.mytable");
NS.StartMonitoring();
NS.NewNotification += new EventHandler<SqlNotificationEventArgs>(DependencyEventHandler);

How to call a method(function) to a form2 which is written in form1 in c#.net?

Of course there are many answers for this question,though,I got no clear Idea for my problem.
I'm working on Visual Studio.I have 2 forms as one is weeklyGVadminview and dailyGVadminview..
I wrote a function(method) in dailyGVadminview which is needed now in another form.....
I'm adding my code here..Any ansers will be appreciated in advance thanks.
namespace weeklyattendance
{
public partial class dailyGvAdminview : Form
{
public dailyGvAdminview()
{
InitializeComponent();
}
private void datechanges()
{
string date = dateTimePicker1.Value.ToString("dd/MM/yyyy");
var connectionstring = ConfigurationManager.ConnectionStrings["attendancemanagement"].ConnectionString;
SqlConnection cn = new SqlConnection(connectionstring);
string query = "select count(Employee_id) from employee_details";
cn.Open();
SqlCommand cmd = new SqlCommand(query, cn);
object result =cmd.ExecuteScalar();
txttotalstaff.Text = (result.ToString());
cn.Close();
}
}
}
Form2 code as follows
namespace weeklyattendance
{
public partial class weeklyGvAdminView : Form
{
public weeklyGvAdminView()
{
InitializeComponent();
}
}
private void dateTimePicker1_ValueChanged_1(object sender, EventArgs e)
{
//here I want to call that function or method
}
}
So I would just create a new class and put the method on that class:
public class DbHandler
{
public string DateChanges()
{
string date = dateTimePicker1.Value.ToString("dd/MM/yyyy");
var connectionstring = ConfigurationManager.ConnectionStrings["attendancemanagement"].ConnectionString;
SqlConnection conn = new SqlConnection(connectionstring);
string query = "select count(Employee_id) from employee_details";
conn.Open();
SqlCommand cmd = new SqlCommand(query, conn);
object result = cmd.ExecuteScalar();
var result = result.ToString();
conn.Close();
return result;
}
}
Then in your classes do this:
var dbHandler = new DbHandler();
var result = dbHandler.DateChanges();
I would try and follow some conventions as well. Makes it easier for other programmers to follow what is going on.
You can create object of dailyGvAdminview and call for that try below code :-
namespace weeklyattendance
{
public partial class weeklyGvAdminView : Form
{
public weeklyGvAdminView()
{
InitializeComponent();
}
}
private void dateTimePicker1_ValueChanged_1(object sender, EventArgs e)
{
var form1 = new dailyGvAdminview();
//// call function using this instance
}
}
If you want to access method from outside then make it to Public as shown below :-
public string DateChanges()
{
}
If you want a really GLOBAL and easy solution do this, first you make a static class.
namespace weeklyattendance
{
public static class Obj
{
public static dailyGvAdminview globalDaily;
}
}
Then in your initialize or onLoad code where you create this class just write
Obj.globalDaily = this;
Now you can access this class anywhere in your program with Obj.globalDaily, you can also add whatever else you want in there and access it like that

Thread Safe Data Access Object C#

I'm trying to make a thread safe Data Access Layer (kind of like a SQL Data Client wrapper). What are some steps I should be making to make this thread safe, while maximizing performance.
For example, if i add a lock on the sqlConn before it closes the connection (since it implements IDisposable); what if the connection is in the middle of a transaction or query?
In summary, I'm trying to accomplish a thread-safe solution; but at the same time, I do not want to risk any critical exceptions, or any delays. Is there any way I can prioritize the closing thread?
public class SQLWrapper : IDisposable
{
private SqlConnection _sqlConn;
public SQLWrapper(string serverName_, string dbName_)
{
SqlConnectionStringBuilder sqlConnSB = new SqlConnectionStringBuilder()
{
DataSource = serverName_,
InitialCatalog = dbName_,
ConnectTimeout = 30,
IntegratedSecurity = true,
};
sqlConnSB["trusted_connection"] = "yes";
this.start(sqlConnSB.ConnectionString);
}
public SQLWrapper(string connString_)
{
this.start(connString_);
}
private void start(string connString_)
{
if (string.IsNullOrEmpty(connString_) == true)
throw new ArgumentException("Invalid connection string");
**lock (this._sqlConn)**
{
this._sqlConn = new SqlConnection(connString_);
this._sqlConn.Open();
}
}
private void CloseConnection()
{
**lock (this._sqlConn)**
{
this._sqlConn.Close();
this._sqlConn.Dispose();
this._sqlConn = null;
}
}
}
The step you should do is:
NOT making it thread safe.
Simple.
Every thread should have it's own copy, with locking / synchronization happening on the database.
THen it will also scale across computers.
This is the standard approach for the last 20 years or so.
So, every thread creates a new SqlWrapper and everything is fine.
The database performs connection pooling for you; lean on it as much as you can. You really shouldn't require locking.
Option 1.
SqlConnection is not encapsulated by the DAO class; appropriate using structures and storage of the connection string is required at the method level.
public class SomeDAO
{
private readonly string _connectionString;
public SomeDAO(string dsn)
{
_connectionString = dsn;
}
public IEnumerable<AssetVO> DoWork()
{
const string cmdText = "SELECT [AssetId] FROM [dbo].[Asset]";
using (var conn = new SqlConnection(_connectionString))
{
conn.Open();
using (var cmd = new SqlCommand(cmdText, conn))
using (var dr = cmd.ExecuteReader())
{
while (dr.Read())
{
yield return new AssetVO
{
AssetId = Guid.Parse(dr["AssetId"].ToString()),
};
}
}
}
}
}
Option 2.
The SqlConnection is a class member; it's state is carefully maintained by helper methods. Using syntax is used for SqlDataReader and SqlCommand.
public class SomeDAO : IDisposable
{
#region backing store
private readonly SqlConnection _connection;
#endregion
public SomeDAO(string dsn)
{
_connection = new SqlConnection(dsn);
}
public SqlConnection OpenConnection()
{
if (_connection.State != ConnectionState.Closed)
_connection.Open();
return _connection;
}
public void CloseConnection()
{
if (_connection.State != ConnectionState.Closed)
_connection.Close();
}
public IEnumerable<AssetVO> DoWork()
{
const string cmdText = "SELECT [AssetId] FROM [dbo].[Asset]";
try
{
using (var cmd = new SqlCommand(cmdText, OpenConnection()))
using (var dr = cmd.ExecuteReader())
{
while (dr.Read())
{
yield return new AssetVO
{
AssetId = Guid.Parse(dr["AssetId"].ToString()),
};
}
}
}
finally
{
CloseConnection();
}
}
#region Implementation of IDisposable
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <filterpriority>2</filterpriority>
public void Dispose()
{
_connection.Dispose();
}
#endregion
}
Both solutions survive a threaded test without the need for explicit locking.
private static volatile bool _done;
private static void Main()
{
#region keyboard interrupt
ThreadPool.QueueUserWorkItem(delegate
{
while (!_done)
{
if (!Console.KeyAvailable) continue;
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.Escape:
_done = true;
break;
}
}
});
#endregion
#region start 3 threads in the pool
ThreadPool.QueueUserWorkItem(DatabaseWorkerCallback);
ThreadPool.QueueUserWorkItem(DatabaseWorkerCallback);
ThreadPool.QueueUserWorkItem(DatabaseWorkerCallback);
#endregion
Thread.Sleep(Timeout.Infinite);
}
private static void DatabaseWorkerCallback(object state)
{
Console.WriteLine("[{0}] Starting", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
while (!_done)
{
using (var dao = new SomeDAO(Properties.Settings.Default.DSN))
{
foreach (var assetVo in dao.DoWork())
Console.WriteLine(assetVo);
}
}
Console.WriteLine("[{0}] Stopping", Thread.CurrentThread.ManagedThreadId);
}

C# asynch SQL inside singleton and delegates

I'm trying to build a class that will be responsible for all operations on database.
Using singleton pattern from here: http://codebender.denniland.com/a-singleton-base-class-to-implement-the-singleton-pattern-in-c/
I've build a class like so:
class DB : SingletonBase<DB>
{
public static readonly string SqlConnectionString = #"Data Source=MYDB;Initial Catalog=PRODUCTS;Integrated Security=True;Asynchronous Processing=true;";
private DB()
{
}
public void loadData()
{
SqlConnection conn = new SqlConnection(SqlConnectionString);
SqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "STATISTICS_1";
cmd.Connection = conn;
conn.Open();
IAsyncResult result = cmd.BeginExecuteReader(new AsyncCallback(HandleCallback), cmd, CommandBehavior.CloseConnection);
}
private void HandleCallback(IAsyncResult result)
{
SqlDataReader dr;
SqlCommand _this = (SqlCommand)result.AsyncState;
if (result.IsCompleted)
{
dr = _this.EndExecuteReader(result);
}
else
dr = null;
DataTable dt = new DataTable();
dt.Load(dr);
dr.Close();
MessageBox.Show("loaded");
}
}
In my main class I'm using this like so:
private void loadStatistics(object sender, EventArgs e)
{
showStatus("loading data");
DB.Instance.loadData();
}
But this will give me only my message box.
What I would like to do is to declare function in my main class that will be called after SQL query return something.
I think that the best way would by using events, but I don't know how to do that proper way.
I would like to do something like this in my main class:
private void loadCompleted(string msg)
{
MessageBox.Show(msg);
}
private void loadStatistics(object sender, EventArgs e)
{
showStatus("loading data");
DB.Instance.loadData(loadCompleted);
}
So that I can specify function that will be called after SQL call is finished.
I don't know if this is the best way of doing this, so any comments, suggestions and solutions are welcome.
What I would like to achieve is to have one class responsible for calling SQL asynchronously and passing data to other functions that will process it.
public delegate void NotifyCallback(string message);
public class ClassWithCommandAndCallback
{
public SqlCommand Sql;
public NotifyCallback Callback;
}
public void loadData(NotifyCallback callback)
{
ClassWithCommandAndCallback ar = new ClassWithCommandAndCallback();
ar.Sql = cmd;
ar.Callback = callback;
IAsyncResult result = cmd.BeginExecuteReader(new AsyncCallback(HandleCallback), ar, CommandBehavior.CloseConnection);
}
private void HandleCallback(IAsyncResult result)
{
ClassWithCommandAndCallback ar = (ClassWithCommandAndCallback)result.AsyncState;
ar.Callback("loaded (SQL was: "+ar.Sql+")");
}

Categories

Resources