Is it normal firebird server sends all registered event counts on raising any of registered event?
By example on firebirds site I do this:
class FirebirdListenerTest
{
public FirebirdListenerTest()
{
try
{
FbConnectionStringBuilder cs = new FbConnectionStringBuilder();
cs.DataSource = "localhost";
cs.Database = "C:\\FIREBIRD\\TEST.GDB";
cs.UserID = "SYSDBA";
cs.Password = "masterkey";
cs.Charset = "NONE";
FbConnection connection = new FbConnection(cs.ToString());
connection.Open();
FbRemoteEvent revent = new FbRemoteEvent(connection);
revent.AddEvents(new string[] { "text_changed", "text_inserted", "justtest_event" });
// Add callback to the Firebird events
revent.RemoteEventCounts += new FbRemoteEventEventHandler(EventCounts);
// Queue events
revent.QueueEvents();
Console.ReadLine();
connection.Close();
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
static void EventCounts(object sender, FbRemoteEventEventArgs args)
{
Console.WriteLine("Event {0} has {1} counts.", args.Name, args.Counts);
}
}
In such code, if any of events is raised, I always get counts for all events. Is it how it should works?
Yes it is. You can filter it base on Counts property, it can be 0-n.
Related
I need your help. I'm trying to implement SqlDependency in a console app to constantly monitor a database table (ultimately a service broker queue). Here is my code and the problem is that after one execution application exits.
class Program
{
static void Main(string[] args)
{
var listener = new Listener();
listener.Listening();
Console.ReadKey();
}
}
public class Listener
{
const string notificationQuery = "SELECT [ID],[Name] FROM [dbo].[tblUsers]";
const string sampleConnectionString = #"Server = xxx; Database = yyy; Integrated Security = SSPI;";
public void Listening()
{
try
{
SqlClientPermission permission = new SqlClientPermission(PermissionState.Unrestricted);
permission.Demand();
}
catch (Exception ex)
{
}
using (var connection = new SqlConnection(sampleConnectionString))
{
connection.Open();
using (var command = new SqlCommand(notificationQuery, connection))
{
SqlDependency.Start(sampleConnectionString);
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);
var hasChanges = dependency.HasChanges;
command.ExecuteReader();
}
}
}
void OnDependencyChange(object sender, SqlNotificationEventArgs e)
{
Console.WriteLine(sender);
Listening();
}
What I found:
how to keep sql dependency doing the its purpose
but adding Listening(); to EventHandler doesn't help. When I'm debugging program and I add some value to the table event is triggered but this adds only one more execution of the Listening method while I want the app to keep working and monitoring the table all the time. Can someone help?
I'm trying to port my code from an obsolete library called CastleMQ to NetMQ but I'm running into some problems.
I prefer to using polling with a timeout, for reliability - I just found that it worked best for me from trial and error compared to just sitting blocking the port indefinitely.
here is my CastleMQ code
public int ZeroPort;
private void ThreadProc()
{
var ctx = new Context();
try {
using (var repSocket = ctx.CreateSocket(SocketType.Rep))
{
string bindAddress = "tcp://*:"+ZeroPort;
repSocket.Bind(bindAddress);
print2("*** BINDING on {0} ***", bindAddress);
bool quit = false;
while (!quit) {
try {
var polling = new Polling(PollingEvents.RecvReady, repSocket);
polling.RecvReady += (socket) =>
{ // using socket.Recv() here is guaranted to return stuff
var msg = socket.Recv();
var msgStr = Encoding.UTF8.GetString(msg);
print2("[REP:{0}] {1}", bindAddress, msgStr);
switch (msgStr) {
case "positions": {
StringBuilder csv = new StringBuilder();
print2("csv: {0}", csv.ToString());
socket.Send(csv.ToString());
break;
}
default: {
socket.Send("Unrecognized Command: " + msgStr);
break;
}
}
};
polling.Poll(POLL_TIMEOUT_MS); // this returns once some socket event happens
} catch (Exception e) {
if (e is ThreadAbortException) {
quit = true;
print2("\n*** EXITED ***");
} else print2(e.ToString());
}
}
}
} catch (Exception e) {
print2(e.ToString());
} finally {
ctx.Dispose();
}
}
here is what I tried to do and then got lost with NetMQ
private void ThreadProc()
{
try {
string bindAddress = "#tcp://*:" + ZeroPort;
print2("*** BINDING on {0} ***", bindAddress);
using (var repSocket = new ResponseSocket(bindAddress))
using (var poller = new NetMQPoller { repSocket })
{
// bool quit = false;
// while (!quit)
// these event will be raised by the Poller
repSocket.ReceiveReady += (s, a) =>
{
// receive won't block as a message is ready
string msg = a.Socket.ReceiveString(); // defeinition for ReceiveString() can't be found
// send a response
a.Socket.Send("Response"); // it doesn't like "Response", do I need to wrap it in some object?
I'm especially confused as how to add a timeout so I can poll with a timeout in a loop the way my CastleMQ code does.
Any pointers would be much appreciated, thanks
Please follow code:
_Layout:
$(function () {
var connection = $.connection.notificationHub;
//signalr method for push server message to client
connection.client.addNotification = function (who) {
//send notification here
console.info("Send Notification")
};
// Start hub
$.connection.hub.start().done(function () {
console.log("SignalR Started")
});
});
Global.asax.cs:
public class Global : HttpApplication
{
string con = ConfigurationManager.ConnectionStrings["sqlConString"].ConnectionString;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//here in Application Start we will start Sql Dependency
SqlDependency.Start(con);
}
protected void Session_Start(object sender, EventArgs e)
{
NotificationComponent NC = new NotificationComponent();
var currentTime = DateTime.Now;
HttpContext.Current.Session["LastUpdated"] = currentTime;
NC.RegisterNotification(currentTime);
}
protected void Application_End()
{
//here we will stop Sql Dependency
SqlDependency.Stop(con);
}
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MyAction(string parameter)
{
//Database Notification (Table - Contacts) -Add or Update
ctx.SaveChanges();
}
NotificationHub : Hub
private readonly static ConnectionMapping<string> _connections = new ConnectionMapping<string>();
public static void SendNotification(string who)
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
foreach (var connectionId in _connections.GetConnections(who))
{
var result = context.Clients.Client(connectionId);
if (result != null)
{
result.addNotification(who);
}
}
}
Components.cs:
public void RegisterNotification(DateTime currentTime)
{
string conStr = ConfigurationManager.ConnectionStrings["sqlConString"].ConnectionString;
string sqlCommand = #"SELECT [ContactID],[ContactName],[ContactNo] from [dbo].[Contacts] where [AddedOn] > #AddedOn";
//you can notice here I have added table name like this [dbo].[Contacts] with [dbo], its mendatory when you use Sql Dependency
using (SqlConnection con = new SqlConnection(conStr))
{
SqlCommand cmd = new SqlCommand(sqlCommand, con);
cmd.Parameters.AddWithValue("#AddedOn", currentTime);
if (con.State != System.Data.ConnectionState.Open)
{
con.Open();
}
cmd.Notification = null;
SqlDependency sqlDep = new SqlDependency(cmd);
sqlDep.OnChange += sqlDep_OnChange;
//we must have to execute the command here
using (SqlDataReader reader = cmd.ExecuteReader())
{
// nothing need to add here now
}
}
}
//After code `ctx.SaveChanges()`, call the code below (50 times):
void SqlDep_OnChange(object sender, SqlNotificationEventArgs e) //<-- Here Problem
{
//from here we will send notification message to client
NotificationHub.SendNotification("User1586");
//...
//re-register notification
RegisterNotification(DateTime.Now);
//HERE -After this line "RegisterNotification(DateTime.Now);", it returns again to the line: "void SqlDep_OnChange(object sender, SqlNotificationEventArgs e)"
}
User "User1586" is receiving multiple notifications.
The line "void SqlDep_OnChange(object sender, SqlNotificationEventArgs e)" repeats several times.
If you have 50 online users, do 50 times on this line:
void SqlDep_OnChange(object sender, SqlNotificationEventArgs e)
If you have 1000 online users, do 1000 times on this line:
void SqlDep_OnChange(object sender, SqlNotificationEventArgs e).
In other words, user "User1586" receives several notifications.
I followed the example here: http://www.dotnetawesome.com/2016/05/push-notification-system-with-signalr.html
Idea is to send notification to a specific user after database update.
Any solution ?
First of all, Sql dependency is not aware what data has changed. So you should query inside the event handler. If you want to send data corresponding the user id and specifically just for one user, I would recommend you to do like this;
Event Handler
private void SqlDependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Info == SqlNotificationInfo.Insert)
{
RecordInfo info = GetLastInsertedRecord(); //Just a custom entity
if(info.UserId > 0)
NotificationHub.SendNotification(info.UserId);
}
RegisterNotification(DateTime.Now);
}
Hub
public static List<UserConnection> ListUser { get; set; }
public static void SendNotification(string who)
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
// Get specific user from connected ones.
string Id = ListUser.Find(x => x.UserId == who).ConnectionId;
context.Clients.Client(Id).addNotification(who); // or another data
}
//Add every connected users to the list
public override Task OnConnected()
{
ListUser = new List<UserConnection>();
var us = new UserConnection();
us.UserId = Context.QueryString["UserId"];
us.ConnectionId = Context.ConnectionId;
ListUser.Add(us);
return base.OnConnected();
}
UPDATE Client
$(function () {
var connection = $.connection.notificationHub;
//Pass the userId here as querystring
$.connection.hub.qs = "UserId=" + $("#labelHoldsUserId").val();
//signalr method for push server message to client
connection.client.addNotification = function (who) {
//send notification here
console.log(who + " sends message");
};
// Start hub
$.connection.hub.start().done(function () {
console.log("SignalR Started")
});
})
It appears that the both the OnChange handler and the SqlDependency instance are only good for one event. After the event is fired and you unsubscribe the handler, you need to register your handler to a NEW SqlDependency object.
Please see the link here for full details: http://msdn.microsoft.com/en-us/library/a52dhwx7(v=vs.80).aspx
I created a Sql server database, in which I added a table named user. Then I executed this script
ALTER DATABASE [TestNotification] SET ENABLE_BROKER
I'd like to use SqlDependency class to notify a winforms application when the user table were changed.
namespace Watcher
{
public partial class Form1 : Form
{
private int changeCount = 0;
private const string statusMessage = "{0} changes have occurred.";
private DataSet dataToWatch = null;
private SqlConnection connection = null;
private SqlCommand command = null;
public Form1()
{
InitializeComponent();
button1.Enabled = CanRequestNotifications();
this.FormClosed += Form1_FormClosed;
}
void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
SqlDependency.Stop(GetConnectionString());
if (connection != null)
{
connection.Close();
}
}
private bool CanRequestNotifications()
{
// In order to use the callback feature of the
// SqlDependency, the application must have
// the SqlClientPermission permission.
try
{
SqlClientPermission perm =
new SqlClientPermission(
PermissionState.Unrestricted);
perm.Demand();
return true;
}
catch
{
return false;
}
}
private void button1_Click(object sender, EventArgs e)
{
changeCount = 0;
label1.Text = String.Format(statusMessage, changeCount);
//SqlDependency.Stop(GetConnectionString());
SqlDependency.Start(GetConnectionString());
if (connection == null)
{
connection = new SqlConnection(GetConnectionString());
}
if (command == null)
{
command = new SqlCommand(GetSQL(), connection);
}
if (dataToWatch == null)
{
dataToWatch = new DataSet();
}
GetData();
}
private string GetConnectionString()
{
return #"Data Source=BILOG-PRT-12\SQLEXPRESS; Initial Catalog=TestNotification;Integrated Security=True";
}
private string GetSQL()
{
return "Select [id],[nom],[prenom],[age] from [dbo].[user]";
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
MessageBox.Show("modification Occurred");
ISynchronizeInvoke i = (ISynchronizeInvoke)this;
if (i.InvokeRequired)
{
OnChangeEventHandler tempDelegate =new OnChangeEventHandler(dependency_OnChange);
object[] args = { sender, e };
i.BeginInvoke(tempDelegate, args);
return;
}
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= dependency_OnChange;
++changeCount;
label1.Text = String.Format(statusMessage, changeCount);
GetData();
}
private void GetData()
{
//dataToWatch.Clear();
//command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
if (connection.State != ConnectionState.Open) connection.Open();
using (var dr = command.ExecuteReader())
{
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
}
}
}
}
All the broker service are running :
I launched the application, Then I clicked into the button and finally I go the Sql Server management studio And I inserted a new row. The problem is that the message box in the application is not shown so the c# application is not notified by SQL Server!!!
So I need to know :
Why this happens?
Which step I forget ?
How can I resolve this issue?
There's quite a few limitations with SqlDependency. To quote one relevant issue:
The projected columns in the SELECT statement must be explicitly stated, and table names must be qualified with two-part names.Notice that this means that all tables referenced in the statement must be in the same database.
(see https://msdn.microsoft.com/library/ms181122.aspx for the full list)
You have to explicitly use two-part name (e.g. dbo.user instead of just user).
Also, you need to execute the command. It doesn't just start working automagically :) Adding a simple using (var dr = command.ExecuteReader()) {} should be enough.
I've created windows application which uses an remote online MYSQL database.For connection i've created a DataConnector() Class. Everytime when i want to connect I create an object of DataConnector() class.
Actually I want to show the progressbar during the connection takes place to database, i mean the progressbar should be on the top of application, after connection successful the progressbar should close automatically.
need some idea how to do it...I've tried with "backgroundworker" but facing prob as the function inside the class returns "MySqlConnection" type.
Here is my DataConnector() Class..
namespace omg
{
class DataConnector
{
bool connected = false;
MySqlConnection connection = null;
MySqlCommand command;
string connectionstring = "server=127.0.0.1;database=online_trading_system;UserId=root;Password=phanny";
public MySqlConnection connect()
{
try
{
connection = new MySqlConnection(connectionstring);
connection.Open();
connected = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return connection;
}
MessageBox.Show("connected with the databse");
// connection.Close();
//connected = false;
return connection;
}
public void close()
{
connection.Close();
connected = false;
MessageBox.Show("connection Closed");
}
}
}
Your BackgroundWorker approach was correct. Here's a short sample:
private void OpenConnectionButton_Click() {
var bw = new BackgroundWorker();
bw.DoWork += (sender, e) => {
// this will happen in a separate thread
var connector = new DataConnector();
connector.connect(); // this might take a while
e.Result = connector;
}
bw.RunWorkerCompleted += (sender, e) => {
// We are back in the UI thread here.
// close the progress bar
...
if (e.Error != null) // if an exception occurred during DoWork,
MessageBox.Show(e.Error.ToString()); // do your error handling here
else {
var connector = (DataConnector)e.Result;
// do something with your connector
}
};
// show the progress bar
...
bw.RunWorkerAsync(); // start the background worker
}