SQLDependency Onchange event always firing without data change - asp.net

I'm using it on a console project.
.NET Framework: 4.5
In my test code, SQLDependency onChange always firing although there is no data changes in Database.
class Program
{
private static string _connStr;
static void Main(string[] args)
{
_connStr = "data source=xxx.xxx.xx.xx;User Id=xxx;Password=xxx; Initial Catalog=xxx";
SqlDependency.Start(_connStr);
UpdateGrid();
Console.Read();
}
private static void UpdateGrid()
{
using (SqlConnection connection = new SqlConnection(_connStr))
{
using (SqlCommand command = new SqlCommand("select msgdtl,msgid From NotifyMsg", connection))
{
command.CommandType = CommandType.Text;
connection.Open();
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
SqlDataReader sdr = command.ExecuteReader();
Console.WriteLine();
while (sdr.Read())
{
Console.WriteLine("msgdtl:{0}\t (msgid:{1})", sdr["msgdtl"].ToString(), sdr["msgid"].ToString());
}
sdr.Close();
}
}
}
private static void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
UpdateGrid();
}
when I start running, onChange event fires and never stop. But there is no change in my databse.

Try to check the SqlNotificationEventArgs object in the dependency_OnChnage method. It looks like you have an error there. I had the same SqlDependency behavior one time and the problem was solved by changing select msgdtl,msgid From NotifyMsg to select msgdtl,msgid From dbo.NotifyMsg (The dbo statement has been added).
But I should warn you: be careful using SqlDependency class - it has the problems with memory leaks. Hovewer, you can use an open source realization of the SqlDependency class - SqlDependencyEx. It uses a database trigger and native Service Broker notification to receive events about the table changes. This is an usage example:
int changesReceived = 0;
using (SqlDependencyEx sqlDependency = new SqlDependencyEx(
TEST_CONNECTION_STRING, TEST_DATABASE_NAME, TEST_TABLE_NAME))
{
sqlDependency.TableChanged += (o, e) => changesReceived++;
sqlDependency.Start();
// Make table changes.
MakeTableInsertDeleteChanges(changesCount);
// Wait a little bit to receive all changes.
Thread.Sleep(1000);
}
Assert.AreEqual(changesCount, changesReceived);
With SqlDependecyEx you are able to monitor INSERT, DELETE, UPDATE separately and receive actual changed data (xml) in the event args object. Hope this help.

there is a custom implementation of SqlDependency that report you the changed table records:
var _con= "data source=.; initial catalog=MyDB; integrated security=True";
static void Main()
{
using (var dep = new SqlTableDependency<Customer>(_con, "Customer"))
{
dep.OnChanged += Changed;
dep.Start();
Console.WriteLine("Press a key to exit");
Console.ReadKey();
dep.Stop();
}
}
static void Changed(object sender, RecordChangedEventArgs<Customer> e)
{
if (e.ChangeType != ChangeType.None)
{
for (var index = 0; index < e.ChangedEntities.Count; index++)
{
var changedEntity = e.ChangedEntities[index];
Console.WriteLine("DML operation: " + e.ChangeType);
Console.WriteLine("ID: " + changedEntity.Id);
Console.WriteLine("Name: " + changedEntity.Name);
Console.WriteLine("Surame: " + changedEntity.Surname);
}
}
}
Here is the link: [https://tabledependency.codeplex.com]

Related

Core 2.1 SignalR and SQLDependency

Is there any Core 2.1 sample available for using SignalR with SQLDependency.
Did enable broker, etc. but never get any dependency onChange event firing. Just the event subscribe is triggered.
When the MS-SQL database table Cities changes on the back-end, I want to see the change reflected right-away on the client web page without having to refresh/reload the page.
//start the dependency when app start in ConfigureServices
SqlDependency.Start(Configuration.GetConnectionString("DefaultConnection"));
using Microsoft.AspNetCore.SignalR;
using SignalR_Test4.Data;
using SignalR_Test4.Hubs;
using System.Collections.Generic;
using System.Data.SqlClient;
namespace SignalR_Test4.Models
{
public class CityRepository
{
private readonly ApplicationDbContext _context;
private readonly IHubContext<CityHub> _hubcontext;
public CityRepository(ApplicationDbContext context, IHubContext<CityHub> hubcontext)
{
_context = context;
_hubcontext = hubcontext;
}
public IEnumerable<City> GetCities()
{
List<City> listOf = new List<City>();
//listOf = _context.Cities;
using (var conn = new SqlConnection(GlobalVar.connectionString))
{
conn.Open();
using (var cmd = new SqlCommand(#"SELECT * FROM Cities", conn))
{
cmd.Notification = null;
SqlDependency dependency = new SqlDependency(cmd);
dependency.OnChange += Dependency_OnChange;
if (conn.State == System.Data.ConnectionState.Closed)
conn.Open();
var reader = cmd.ExecuteReader();
while (reader.Read())
{
listOf.Add(new City { Id = (string)reader["Id"], Name_en = (string)reader["name_en"], CountryId = (string)reader["CountryId"], Code = (string)reader["Code"] });
}
}
}
return listOf;
}
private void Dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
_hubcontext.Clients.All.SendAsync("GetCities");
}
}
}
}
The issue was within the line:
var cmd = new SqlCommand(#"SELECT Id, Name_en, CountryId, Code from [dbo].Cities", conn)
It is required to use the field name (Not the *) and also the 2 part table name convention => [dbo].Cities

Using many sql connection in one code?

I have an ASP.NET web application which is connected to SQL Server.
I have used three connections for each SQL operation. It works very well, however I think this is not an efficient way to do things - can this be written better than it is?
public partial class Home : System.Web.UI.Page
{
SqlConnection co = new SqlConnection(ConfigurationManager.ConnectionStrings["TextConnectionString"].ConnectionString);
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["TextConnectionString"].ConnectionString);
SqlConnection con2 = new SqlConnection(ConfigurationManager.ConnectionStrings["TextConnectionString"].ConnectionString);
protected void Button1_Click(object sender, EventArgs e)
{
using (co)
{
co.Open();
SqlCommand cm = co.CreateCommand();
cm.CommandText = "select...";
cm.ExecuteNonQuery();
}
co.Close();
using (con)
{
con.Open();
SqlCommand cmv = con.CreateCommand();
cmv.CommandText = "insert...";
cmv.ExecuteNonQuery();
}
con.Close();
using (con2)
{
con2.Open();
SqlCommand cmf = con2.CreateCommand();
cmf.CommandText = "delete from...";
cmf.ExecuteNonQuery();
}
con2.Close();
}
}
SqlConnection co = new SqlConnection(ConfigurationManager.ConnectionStrings["TextConnectionString"].ConnectionString);
protected void Button1_Click(object sender, EventArgs e)
{
using (co)
{
co.Open();
SqlCommand cm = co.CreateCommand();
cm.CommandText = "select...";
cm.CommandText += " insert...";
cm.CommandText += " delete from...";
cm.ExecuteNonQuery();
}
co.Close();
}
you can use like this.
You're using the same connection string for each connection? Why do you need three connections? Why not just open and close the same one?
As long as the connection string is the same, you only need one connection.
In general you should prefer to create and open a connection object as close to where you make use of it as possible, and dispose of it as soon as possible afterwards (preferably by making use of a using statement). Connection pooling will take care of ensuring you only actually create a limited number of real connections to the server, despite the large number of SqlConnection objects your code may seem to create.
Within a single method, however, it is reasonable to use a single connection object:
public partial class Home : System.Web.UI.Page
{
string connString = ConfigurationManager.ConnectionStrings["TextConnectionString"].ConnectionString;
protected void Button1_Click(object sender, EventArgs e)
{
using (SqlConnection co = new SqlConnection(connString))
{
co.Open();
using(SqlCommand cm = co.CreateCommand())
{
cm.CommandText = "select...";
cm.ExecuteNonQuery();
}
using(SqlCommand cmv = co.CreateCommand())
{
cmv.CommandText = "insert...";
cmv.ExecuteNonQuery();
}
using(SqlCommand cmf = co.CreateCommand())
{
cmf.CommandText = "delete from...";
cmf.ExecuteNonQuery();
}
}
}
}
(You don't need to explicitly close the connection object, the Dispose (within the using is equivalent)
No use declaring/creating multiple connections when you would be using only one at a time. You can do with just one.
Declare variable as close as possible to its first use, and with minimum scope manageable.
Make things modular and reusable as far as possible.
No need to explicitly close the connection, since the IDisposable interface implementation (and using block) does it anyways. But there is no harm in explicitly closing it.
protected void Button1_Click(object sender, EventArgs e)
{
ExecuteNonQuery("select...", null); // why??
ExecuteNonQuery("insert...", null);
ExecuteNonQuery("delete from...", null);
}
protected void ExecuteNonQuery(string query, SqlParameter[] parameters)
{
using (SqlConnection co = new SqlConnection(ConfigurationManager.ConnectionStrings["TextConnectionString"].ConnectionString))
{
co.Open();
SqlCommand cm = co.CreateCommand();
cm.CommandText = query;
if (parameters != null) cm.Parameters.AddRange(parameters);
cm.ExecuteNonQuery();
}
}
You can also try this.
SqlConnection co = new SqlConnection(ConfigurationManager.ConnectionStrings["TextConnectionString"].ConnectionString);
protected void Button1_Click(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(" Select statement.. ");
sb.AppendLine(" Insert statement ");
sb.AppendLine(" delete statement ");
using (co)
{
co.Open();
SqlCommand cm = co.CreateCommand();
cm.CommandText = sb.Tostring();
cm.ExecuteNonQuery();
}
co.Close();
}

Issue regarding implementing SQL Dependency in Application_Start event

i through i will call SQL Dependency related code Application_Start event and my objective will be completed. my scenario is i have a routine in my web application which i invoke manually just clicking on a button when data updated in sql server table. i was told to make this process automated.
so i go through couple of article on SQL Dependency and through i can make my manual process automated by SQL Dependency because we can monitor table change by SQL Dependency and SQL Dependency can notify us when change done.
please see my code
protected void Application_Start(Object sender, EventArgs e)
{
System.Data.SqlClient.SqlDependency.Stop(connectionString);
System.Data.SqlClient.SqlDependency.Start(connectionString);
RegisterNotification();
}
static SqlDependency dep;
private static void RegisterNotification()
{
string tmpdata = "";
try
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "SELECT ActivityDate FROM [bba-reman].ContentChangeLog";
dep = new SqlDependency(cmd);
dep.OnChange += new OnChangeEventHandler(OnDataChange);
SqlDataReader dr = cmd.ExecuteReader();
{
while (dr.Read())
{
if (dr[0] != DBNull.Value)
{
tmpdata = dr[0].ToString();
}
}
}
dr.Dispose();
cmd.Dispose();
}
}
finally
{
//SqlDependency.Stop(connStr);
}
}
static void OnDataChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dep = sender as SqlDependency;
dep.OnChange -= new OnChangeEventHandler(OnDataChange);
SiteSearch.CreateIndex(false);
RegisterNotification();
}
protected void Application_End(Object sender, EventArgs e)
{
System.Data.SqlClient.SqlDependency.Stop(connectionString);
}
the above code was running fine but i faced a good problem. suppose no visitor is visiting our web site and at the same time if some one change sql data by a win apps then i saw OnDataChange() event is not firing but if at least one visitor is with our site then OnDataChange() event is firing properly.
may be this is default nature of asp.net engine. i could use SQL Dependency in win service or in winform apps but i have some constrain because the routine which i am calling after detection of data change in db that is in our web application.
So please guide me what i can do as a result OnDataChange() event should always fire if no visitor is visiting our web site.
thanks

ASP.NET Cache always returns null

I am using SQLCacheDependency in my ASP.NET application with Query Notifications.
I followed this article to set up my database with success.However whenever I am trying to store data in the cache object.It just does not hold value.It is always null .I am not getting any errors or exceptions.
Here is my code
Global.asax
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
System.Data.SqlClient.SqlDependency.
Start(ConfigurationManager.ConnectionStrings["McdConn"].ToString());
}
void Application_End(object sender, EventArgs e)
{
// Code that runs on application shutdown
System.Data.SqlClient.SqlDependency.
Stop(ConfigurationManager.ConnectionStrings["McdConn"].ToString());
}
public static class CacheManagement
{
public static DataTable CreateCache(string cacheName, string tableName, string query)
{
DataTable dtResult = new DataTable();
try
{
string connectionString = ConfigurationManager.ConnectionStrings["McdConn"].ToString();
dtResult = HttpContext.Current.Cache[cacheName] as DataTable;
if (dtResult == null)
{
dtResult = new DataTable();
using (var cn = new SqlConnection(connectionString))
{
cn.Open();
var cmd = new SqlCommand(query, cn);
cmd.Notification = null;
cmd.NotificationAutoEnlist = true;
SqlCacheDependencyAdmin.EnableNotifications(connectionString);
if (!SqlCacheDependencyAdmin.GetTablesEnabledForNotifications(connectionString).Contains(tableName))
{
SqlCacheDependencyAdmin.EnableTableForNotifications(connectionString,tableName);
}
var dependency = new SqlCacheDependency(cmd);
//SqlDataAdapter ad = new SqlDataAdapter(cmd);
//ad.Fill(dsResult);
SqlDataReader reader = cmd.ExecuteReader();
dtResult.Load(reader);
HttpContext.Current.Cache.Insert(cacheName, dtResult, dependency);
}
}
}
catch (Exception ex)
{
Exception_Log.ExceptionMethod("Web", "CacheManagement.cs", "CacheManagement", ex);
}
return dtResult = HttpContext.Current.Cache[cacheName] as DataTable;
}
}
Code Behind
var dtCachedCategories = HttpContext.Current.Cache["tbl_CategoryMaster_Cached"] as DataTable;
if (dtCachedCategories == null)
{
dtCachedCategories = CacheManagement.CreateCache("tbl_CategoryMaster_Cached","dbo.tbl_CategoryMaster_Languages", "Select * from dbo.tbl_CategoryMaster_Languages");
}
The above always returns null.
Can anyone help me in pointing out what could be missing?
Well there's a lot you can do to debug your code and arrive at a conclusion. It seems like your cached item is getting removed too frequently.
1.) Use CacheItemPriority.NotRemovable to Cache.Insert() to make sure ASP.NET doesn't removes
your item whenever it feels so. use the Insert() method explained here. Check this MSDN
article too.
2.) To find out the reason why your cached item is getting removed , log this removal action using
CacheItemRemovedCallback delegate option of your Cache.Insert() method. Check this Insert method
overload version and also this link.
3.) Make sure your dtresult as well as your reader is not null. Check the lines:
SqlDataReader reader = cmd.ExecuteReader(); & dtResult.Load(reader); , together with your logs.
4.) Check your application Pool recycle time. This link has everything related to App pool settings ( IIS 7 +).
5.) This link has a solution for App pool of IIS 6: http://bytes.com/topic/net/answers/717129-c-asp-net-page-cache-getting-removed-too-soon
Also, try using HttpRuntime.Cache method to see if it works.
System.Web.HttpRuntime.Cache.Insert(cacheName, dtResult, dependency);

how to use FileSystemWatcher using asp.net(C#)

SENARIO: i have few folders on my FTP server, belongs to particular user. Suppose i have 10GB total space and i assign 1GB to each user i.e can accomodate 10 users having 1GB each.
now users can add/delete/edit any type of file to utilize the storage space. All i need to do is to restrict users not to exceed 1gb space for their file storage. For this i want to use FileSystemWatcher to notify me that a user had created/deleted/edited a file so that i can minimize the space from 1gb incase of creation of a file or add a space incase of deletion.
this is the piece of coding using FSW. when user gets loged-in with proper id and password, respective folder is opened (present at FTP server) where he can add/delete/edit any type of file and according to that i hav to monitor d space ulitilized by him.
but d problem is the event handlers (written in console). i dont understand what happens when this code is being runned... i dontknow how to use FSW class so that i can monitor d changes user is making in his folder.
please help ... THANX
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
public class _Default: System.Web.UI.Page {
public class ClsFileSystemWatcher {
public static void OnChanged(object source, FileSystemEventArgs e) {
Console.WriteLine("File "+e.FullPath+" :"+e.ChangeType);
}
public static void OnDeleted(object source, FileSystemEventArgs e) {
Console.WriteLine("File "+e.FullPath+" :"+e.ChangeType);
}
public static void OnCreated(object source, FileSystemEventArgs e) {
Console.WriteLine("File "+e.FullPath+" :"+e.ChangeType);
}
public static void OnRenamed(object source, RenamedEventArgs e) {
Console.WriteLine("File "+e.OldFullPath+" [Changed to] "+e.FullPath);
}
public static void OnError(object source, ErrorEventArgs e) {
Console.WriteLine("Error "+e);
}
public void FileWatcher(string InputDir) {
using (FileSystemWatcher fsw = new FileSystemWatcher()) {
fsw.Path = InputDir;
fsw.Filter = #"*";
fsw.IncludeSubdirectories = true;
fsw.NotifyFilter = NotifyFilters.FileName|NotifyFilters.Attributes|NotifyFilters.LastAccess|NotifyFilters.LastWrite|NotifyFilters.Security|NotifyFilters.Size|NotifyFilters.CreationTime|NotifyFilters.DirectoryName;
fsw.Changed += OnChanged;
fsw.Created += OnCreated;
fsw.Deleted += OnDeleted;
fsw.Renamed += OnRenamed;
fsw.Error += OnError;
fsw.EnableRaisingEvents = true;
//string strOldFile = InputDir + "OldFile.txt";
//string strNewFile = InputDir + "CreatedFile.txt";
//// Making changes in existing file
//using (FileStream stream = File.Open(strOldFile, FileMode.Append))
//{
// StreamWriter sw = new StreamWriter(stream);
// sw.Write("Appending new line in Old File");
// sw.Flush();
// sw.Close();
//}
//// Writing new file on FileSystem
//using (FileStream stream = File.Create(strNewFile))
//{
// StreamWriter sw = new StreamWriter(stream);
// sw.Write("Writing First line into the File");
// sw.Flush();
// sw.Close();
//}
//File.Delete(strOldFile);
//File.Delete(strNewFile);
// Minimum time given to event handler to track new events raised by the filesystem.
Thread.Sleep(1000);
}
}
}
private DAL conn;
private string connection;
private string id = string.Empty;
protected void Page_Load(object sender, EventArgs e) {
connection = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Documents and Settings\\project\\Desktop\\BE prj\\dbsan.mdb;Persist Security Info=False";
conn = new DAL(connection);
////*** Opening Respective Folder of a User ***////
DirectoryInfo directories = new DirectoryInfo(#"C:\\Inetpub\\ftproot\\san\\");
DirectoryInfo[] folderList = directories.GetDirectories();
if (Request.QueryString["id"] != null) {
id = Request.QueryString["id"];
}
string path = Path.Combine(#"C:\\Inetpub\\ftproot\\san\\", id);
int folder_count = folderList.Length;
for (int j = 0; j < folder_count; j++) {
if (Convert.ToString(folderList[j]) == id) {
Process p = new Process();
p.StartInfo.FileName = path;
p.Start();
}
}
ClsFileSystemWatcher FSysWatcher = new ClsFileSystemWatcher();
FSysWatcher.FileWatcher(path);
}
}
Each time you reload the page you create new FSW - in that case you won't get any events raised, because from the point of newly created FSW nothing was changes. Try to preserve your FileSystemWatcher object in the Session state.
So flow would look like:
User logs in – you create FSW and preserve it in Session
User reloads the page – get FSW from Session (do not create new one)
You should create a worker role (service) for this type of thing. I think it is not appropriate to have something like this inside of a page.

Resources