I have strange problem and question about using SqlDependency class in a normal Asp.net MVC application
I have standard controller with code:
public ActionResult Index()
{
RegisterNotification();
return View();
}
public void RegisterNotification()
{
var cs = ConfigurationManager.ConnectionStrings["TestDb"].ConnectionString;
var sql = #"Select Data From dbo.TestTable";
using (var conn = new SqlConnection(cs))
{
conn.Open();
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Notification = null;
var sqlDependency = new SqlDependency(cmd);
sqlDependency.OnChange += SqlDependency_OnChange;
cmd.ExecuteNonQuery();
}
}
}
public void SqlDependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
// call some SignalR method here and re-register notification
RegisterNotification();
}
}
In Global.asax is SqlDepndency initialize (on start and end methods)
Well, in first request everything working great, but after some refresh(full request) SqlDependency_OnChange calling twice and next refresh calling four times and etc.
In console application this code works fine.
Is there something wrong with my code?
(Using Sql 2012 and asp.net MVC 5.2.3 and VisualStudio IISExpress dev server)
Thanks
I think what you'll want to do is create a Singleton that will handle the SqlDependency notifications. Every time someone makes a request, a new instance of your controller is being created, which registers for new notifications. When you're getting multiple notifications, I think you'll find that it's different instances of your controller getting notified.
There's a great example of this here
Related
I'm trying to make use of SqlDependancy in a SignalR project, but I can't seem to get the OnChanged event to fire more than once. It fires initially on the subscribe event, but it never fires again after making changes to the underlying database. I've omitted my SignalR and controller code because the problem seems to lie in the repository class. SqlDependancy.Start() is declared in my Global.asax class.
Watching from the SQL server, I can see a notification queue is created when my application starts, and is terminated when I close as well.
public IEnumerable<Visitor> NotifyAllClients()
{
List<Visitor> visitors = new List<Visitor>();
using (var connection = new SqlConnection(new VisitorLogEntities().Database.Connection.ConnectionString))
{
using (var command = new SqlCommand(#"SELECT * FROM dbo.Visitors", connection))
//using (var command = new SqlCommand(#"SELECT [Id],[AgreeToTerms],[Base64Image],[CheckInDate],[CheckOutTime],[Company],[CountryOfOrigin],[email],[FirstName],[LastName],[IsInBuilding],[MeetingSubject],[MeetingTime],[PatriotHost],[phone],[title] FROM dbo.Visitors", connection))
{
var dependency = new SqlDependency(command);
dependency.OnChange += Database_OnChange;
if (connection.State == System.Data.ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
while (reader.Read())
{
////compile visitor objects
////visitors.add(new Visitor());
}
}
return visitors.OrderByDescending(x => x.CheckInDate);
}
}
private void Database_OnChange(object sender, SqlNotificationEventArgs e)
{
//var dependency = (SqlDependency)sender;
//dependency.OnChange -= Database_OnChange;
////this fires once, with the Type of 'Subscribe', but then never fires on CRUD changes
if (e.Type == SqlNotificationType.Change)
{
VisitorHub.SendVisitors();
}
//NotifyAllClients();
}
edit: lines of code commented out above indicate the changes needed to get this working correctly.
Check this example from msdn http://msdn.microsoft.com/en-US/library/a52dhwx7(v=vs.80).aspx. Download the VS2005_General_en-us.pdf. Page 24636, "Using SqlDependency in a Windows Application" is the section the original link led to. Pay particular attention to step 12 and 13 in the watcher application. In step 12 you will see the removal of the onChange event and then it calls step 13 which sets it up again.
Also, I think you are seeing bad behavior due to your sql statement itself. The sql statement has to follow some rules. See https://technet.microsoft.com/en-us/library/ms181122(v=sql.105).aspx for more info. In particular the Writing Notification Queries section. "The statement may not use the asterisk (*) or table_name.* syntax to specify columns."
I've this piece of code in my page, but it don't run when i make changes in the database, what could be the problem.
This starts well, when i load the page this executes the function twice, but if i send a message to the database this doens't execute.
$(function () {
var chat = $.connection.chatHub;
chat.client.allTalks = function () {
refresh();
};
$.connection.hub.start();
refresh();
});
SERVER SIDE (HUB):
[HubName("chatHub")]
public class ChatHub : Hub
{
public static void AllTalks()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
context.Clients.All.allTalks();
}
}
HANDLER
...
using (SqlCommand command = new
SqlCommand(#"SELECT * FROM [dbo].[chat_talks]", connection)) {
//CONTENT
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
...
}
public void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
ChatHub.AllTalks();
}
GLOBAL.ASAX
protected void Application_Start(object sender, EventArgs e)
{
SqlDependency.Start(ConfigurationManager.ConnectionStrings["ProjectSellerConnection"].ConnectionString);
}
First off, it is redundant to have your first line in your server-side code. There is no need to call for a hubContext inside the Hub. You can just do:
public static void AllTalks()
{
Clients.All.allTalks();
}
I would suggest, perhaps foolishly, to not use SQL Dependency. I would instead suggest using the following technique of calling SignalR (specifically, it will call the client functions):
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
hubContext.Clients.All.allTalks();
You can call this in, for example, actions in MVC and WebAPI Controllers, thus meaning if you've done any database updates in those actions, you can subsequently call clients using this methodology. I know it's not as fancy as SQL Dependency, and perhaps not the answer your looking for, but it will solve your problem - since it appears the problem seems to be with SignalR detecting the database changes.
In other words, this methodology will work, but it's probably not the precise one you are hoping for.
I have developed an application which runs query against a DB located in another machine (let's call il SRV-SQL).
In certain cases the application can fail to communicate with the DB machine so my employer wants me to develop a new module which is capable of switching at run-time between the existent connection (to SRV-SQL) to a local DB instance (which is the very same copy of the one in the DB machine).
I have a couple of questions about that:
which is the best way to detect connection problems so that I can fire an "event" to switch from remote connection to the locale one? Would I need to trap SqlException or there is a better way?
Is there any way to switch between the 2 environments without breaking user's actions?
The second point is less trivial as it is acceptable to logout and ask the user to login again (even though i would try to avoid it).
Please let me know if you need more information... also, sorry but I cannot provide any code as this is a design question!
Thanks for taking the time to answer!
It would be good to abstract away the fact that there could possibly be multiple databases from the rest of your application.
public class WidgetRepository
{
private readonly string _primaryConnectionString;
private readonly string _secondaryConnectionString;
public WidgetRepository(string primaryConnectionString, string secondaryConnectionString)
{
_primaryConnectionString = primaryConnectionString;
_secondaryConnectionString = secondaryConnectionString;
}
public void AddWidget(Widget widget)
{
ExecuteAction(AddWidgetAction(widget));
}
public void UpdateWidget(Widget widget)
{
ExecuteAction(UpdateWidgetAction(widget));
}
private Action<string> AddWidgetAction(Widget widget)
{
return Action<string>(connectionString => {
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "INSERT INTO Widgets(name, price) VALUES(#name, #price)";
command.Parameters.AddWithValue("#name", widget.Name);
command.Parameters.AddWithValue("#price", widget.Price);
command.ExecuteNonQuery();
}
}
});
}
private Action<string> UpdateWidgetAction(Widget widget)
{
// Logic here to update a widget
}
private void ExecuteAction(Action<string> action)
{
try
{
action(_primaryConnectionString);
}
catch (SqlException)
{
action(_secondaryConnectionString);
}
}
}
Usage:
var widgetRepository = new WidgetRepository("dbconn1", "dbconn2");
widgetRepository.AddWidget(new Widget("Cog", 15.99m));
I am trying to get a test application to work using Oracle Change Notification with C#, but I am not receiving the callback notification in my application. Oracle DB version is 11.2.0.1.0. Oracle.DataAccess v.2.112.30. I can confirm the query gets registered in Oracle by viewing SYS.USER_CHANGE_NOTIFICATION_REGS and SYS.USER_CQ_NOTIFICATION_QUERIES. However, nothing ever appears in SYS.DBA_CHANGE_NOTIFICATION_REGS.
The registration persists until I commit a transaction on the table. The registration disappears after a few seconds after the commit and my application does not received the notification.
I have made sure my computer is listening on the correct port and have even tried turning off any firewall that could be blocking the port.
I do have GRANT CHANGE NOTIFICATION to MYSCHEMA, GRANT EXECUTE ON DBMS_CHANGE_NOTIFICATION TO MYSCHEMA, and the JOB_QUEUE_PROCESSES is set to 1.
Questions:
1) Should the registration be visible in SYS.DBA_CHANGE_NOTIFICATION_REGS and, if so, what could be causing it not to be when it is visible in SYS.USER_CHANGE_NOTIFICATION_REGS and SYS.USER_CQ_NOTIFICATION_QUERIES?
2) What could be causing the registration to disappear after a commit?
3) What could be causing the failure of the notification to my application?
Here is the C# code I am using and it is basically the same as from the Oracle website:
using System;
using System.Threading;
using System.Data;
using Oracle.DataAccess.Client;
namespace NotifyTest
{
public class Program
{
public static bool IsNotified = false;
public static void Main(string[] args)
{
string constr = "User Id=mySchema;Password=myPassword;Data Source=myOracleInstance";
OracleDependency dep = null;
try
{
using (var con = new OracleConnection(constr))
{
Console.WriteLine("Registering query...");
var cmd = new OracleCommand("select * from mySchema.NOTIFY_TEST", con);
con.Open();
OracleDependency.Port = 1005;
dep = new OracleDependency(cmd);
dep.OnChange += OnMyNotificaton;
int queryRegistered = cmd.ExecuteNonQuery();
// If oracle returns -1, then the query is successfully registered
if (queryRegistered == -1)
{
Console.WriteLine("Query Registered...");
Console.WriteLine("Listening for Callback...");
}
else
{
Console.WriteLine("There was an error...");
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
// Loop while waiting for notification
while (IsNotified == false)
{
Thread.Sleep(100);
}
}
public static void OnMyNotificaton(object sender, OracleNotificationEventArgs arg)
{
Console.WriteLine("Table change notification event is raised");
Console.WriteLine(arg.Source.ToString());
Console.WriteLine(arg.Info.ToString());
Console.WriteLine(arg.Source.ToString());
Console.WriteLine(arg.Type.ToString());
IsNotified = true;
}
}
}
Just wanted to provide an update as to how I resolved this issue. I changed my Oracle.DataAccess.dll from v.2.112.3.0 to v.2.112.1.2 and it works fine.
In SYS.CHNF$_REG_INFO attribute QOSFLAGS there is QOS_DEREG_NFY, which specifies that the database should unregister the registration on the first notification.
Not sure but the value on job_queue_processes (1) is a bit low. Oracle performs all kinds of maintenance and event handling tasks internally. For this they also use Job slaves. Raise job_queue_processes (default 1000) and check Troubleshooting CQN Registrations
I have a C# program that queries the SQL Server database for some values.
Currently the application queries the database every minutes to make sure that the table is up to date.
What I would like to be able to do is that the query is only done when the database has been changed / updated. How do I notify my program when something has been updated in the database?
Thanks
Polling database is not very elegant solution.
SqlDependency from ADO.NET will be useful in your case. It does not use polling but notification mechanism. The notifications are provided by Service Broker in your database, so will need to enable this service in your databse. The OnChange event will raise when specified table changes(update, delete, insert..)
Here is an example how to use SqlDependency:
void Initialization()
{
// Create a dependency connection.
SqlDependency.Start(connectionString, queueName);
}
void SomeMethod()
{
// Assume connection is an open SqlConnection.
// Create a new SqlCommand object.
using (SqlCommand command=new SqlCommand(
"SELECT ShipperID, CompanyName, Phone FROM dbo.Shippers",
connection))
{
// Create a dependency and associate it with the SqlCommand.
SqlDependency dependency=new SqlDependency(command);
// Maintain the refence in a class member.
// Subscribe to the SqlDependency event.
dependency.OnChange+=new
OnChangeEventHandler(OnDependencyChange);
// Execute the command.
using (SqlDataReader reader = command.ExecuteReader())
{
// Process the DataReader.
}
}
}
// Handler method
void OnDependencyChange(object sender,
SqlNotificationEventArgs e )
{
// Handle the event (for example, invalidate this cache entry).
}
void Termination()
{
// Release the dependency.
SqlDependency.Stop(connectionString, queueName);
}
from http://msdn.microsoft.com/en-us/library/62xk7953.aspx
Here is how to enable Service Broker(note that you will have exclusiveness on the database to do that - best do it after restart of the sql server):
http://blogs.sftsrc.com/stuart/archive/2007/06/13/42.aspx(Broken link)
Possible alternative link: http://technet.microsoft.com/en-us/library/ms166086(v=sql.105).aspx
If you are on SQL Server 2005 and above, you can consider using the SqlDependency object.
It represents a query notification dependency between an application and an instance of SQL Server 2005.
An application can create a SqlDependency object and register to receive notifications via the OnChangeEventHandler event handler.
Refer this link on MSDN for more information
However, do note the caveat that MS puts against its use. It is advised to have a caching layer and then use SQLDependency in coordination with that layer .
SqlDependency was designed to be used in ASP.NET or middle-tier services where there is a relatively small number of servers having dependencies active against the database. It was not designed for use in client applications, where hundreds or thousands of client computers would have SqlDependency objects set up for a single database server.
To get a notify when some record is updated, avoid the application to query the table you cab use TableDependency component (in your specific case SqlTableDependency). Here is an example:
public partial class Window1 : Window
{
private IList<Stock> _stocks;
private readonly string _connectionString =
"data source=.;initial catalog=myDB;integrated security=True";
private readonly SqlTableDependency<Stock> _dependency;
public Window1()
{
this.InitializeComponent();
this.McDataGrid.ItemsSource = LoadCollectionData();
this.Closing += Window1_Closing;
var mapper = new ModelToTableMapper<Stock>();
mapper.AddMapping(model => model.Symbol, "Code");
_dependency = new SqlTableDependency<Stock>(_connectionString, "Stocks", mapper);
_dependency.OnChanged += _dependency_OnChanged;
_dependency.OnError += _dependency_OnError;
_dependency.Start();
}
private void Window1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
_dependency.Stop();
}
private void _dependency_OnError(object sender, TableDependency.EventArgs.ErrorEventArgs e)
{
throw e.Error;
}
private void _dependency_OnChanged(
object sender,
TableDependency.EventArgs.RecordChangedEventArgs<Stock> e)
{
if (_stocks != null)
{
if (e.ChangeType != ChangeType.None)
{
switch (e.ChangeType)
{
case ChangeType.Delete:
_stocks.Remove(_stocks.FirstOrDefault(c => c.Symbol == e.Entity.Symbol));
break;
case ChangeType.Insert:
_stocks.Add(e.Entity);
break;
case ChangeType.Update:
var customerIndex = _stocks.IndexOf(
_stocks.FirstOrDefault(c => c.Symbol == e.Entity.Symbol));
if (customerIndex >= 0) _stocks[customerIndex] = e.Entity;
break;
}
this.McDataGrid.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
{
this.McDataGrid.Items.Refresh();
}));
}
}
}
private IEnumerable<Stock> LoadCollectionData()
{
_stocks = new List<Stock>();
using (var sqlConnection = new SqlConnection(_connectionString))
{
sqlConnection.Open();
using (var sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.CommandText = "SELECT * FROM [Stocks]";
using (var sqlDataReader = sqlCommand.ExecuteReader())
{
while (sqlDataReader.Read())
{
var code = sqlDataReader
.GetString(sqlDataReader.GetOrdinal("Code"));
var name = sqlDataReader
.GetString(sqlDataReader.GetOrdinal("Name"));
var price = sqlDataReader
.GetDecimal(sqlDataReader.GetOrdinal("Price"));
_stocks.Add(new Stock { Symbol = code, Name = name, Price = price });
}
}
}
}
return _stocks;
}
The event handler is triggered for every INSERT UPDATE or DELETE operation done on the table, reporting you the modified value. So, in case you are interested to keep your C# Datatable up to date, you simple can get the fresh data from the event handler.
What I would like to be able to do is that the query is only done when the database has been changed/updated.How do i notify my program when some thing updated in database.
There isn't any means of the database pushing notifications to the application. The application needs to poll the database to check for updates, and then deal with the updates appropriately.
If by "updates to the database" you mean any update by any application, you're out of luck: it's not doable.
If, however, you mean changes made by your app, it's easy: every time you update the DB raise and event and have handlers respond to the event.