SqlDependency OnChange event firing multiple times - c#

I'm have a comment system on a project where you can view different pages and they have their own comments. I'm trying to use signalR with SqlDependency to automatically pull new comments on the page.
My problem is that if multiple people have a connection open with SqlDependency the number of "onchange" events called from the sqlnotification start getting multiplied. Instead of the onchange even getting called once it will be called multiple times for each user. (Even if they are not viewing the same comments page)
I've pretty much exhausted all searching here with the most common response being that I need to unsubscribe the event when it's called like this:
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= new OnChangeEventHandler(dependency_OnChange);
if (e.Info.ToString().ToLower().Trim() == "insert")
{
MyHub.Show();
}
}
This didn't seem to change anything for me so I'm lost on what the best way to prevent this would be.
GetData method:
[WebMethod]
public IEnumerable<DUpdates> GetData(string dnum, string date)
{
if (Common.IsValidInt32(dnum)) //just a function that check for valid int
{
using (var connection =
new SqlConnection(ConfigurationManager.ConnectionStrings["SConnectionString"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT [Comment] FROM dbo.DUpdates WHERE (DID = " + dnum + ") AND (Deleted = 0) AND CreateDate > #Date ORDER BY CreateDate DESC", connection))
{
command.Parameters.Add("#Date", SqlDbType.DateTime);
command.Parameters["#Date"].Value = date;
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
{
return reader.Cast<IDataRecord>().Select(x => new DUpdates()
{
comment = x.GetString(0)
}).ToList();
}
}
}
}
JS stuff:
$(function() {
var job = $.connection.myHub;
job.client.displayStatus = function () {
getData();
};
$.connection.hub.start();
getData();
});
function getData() {
var params = (new URL(document.location)).searchParams;
var id = params.get("id");
var dt = $("#accessdate").val();
$.ajax({
url: 'dupdatesservice.asmx/GetData',
data: JSON.stringify({dnum: id, date: dt}),
contentType: "application/json; charset=utf-8",
dataType: "json",
type: "POST",
success: function (data) {
if (data.d.length > 0) {
$("#testdiv").prepend(data.d[0].comment);
}
}
});
}
Can provide other code if needed.

The issue here is that I was creating a new SqlDependency for each user that was on a page (or same user with multiple windows). So if 2 windows were open for a page, then it would check for notifications twice and send the response twice if there was something new. Because of the ajax request, now all of those SqlDependencies were doubled so I'd get 4 responses the next time, then 8 and so on.
What I decided to do instead was to essentially change my pages to private chats using signalr and just ditched the SqlDependency stuff. So now if a user goes to one page, they are connected with anyone else on the page and anytime someone submits a "comment" it also gets sent to other people viewing the page.

Related

How to stop page from reloading when asp button is pressed?

I have created form with many fields that on page load loads information about a specific user. When the user click on the button update the page seems to be reloading and then sends the sames informations to the server.
I have looked at similaire answers on stackoverflow but none seem to work for me.
Here is my front end.
<asp:TextBox ID="BirthDate" type="text" runat="server" class="form-control"
placeholder="fecha nacimiento" aria-describedby="inputGroupPrepend"
required="required"></asp:TextBox>
<asp:Button class="btn btn-primary" ID="update" runat="server" Text="actualizar" AutoPostback="false" OnClick="update_Click"/>
and here is my back end.
//This is where the textboxes are populated
protected void Page_Load(object sender, EventArgs e)
{
string employeID = gestiondeempleados.empleadoID;
string queryStr = "SELECT empleado_id,nombreusuario,nombre Where empleado_id =" + employeID;
using (conn = new MySql.Data.MySqlClient.MySqlConnection(connString))
{
using (cmd = new MySql.Data.MySqlClient.MySqlCommand(queryStr, conn))
{
conn.Open();
using (reader = cmd.ExecuteReader())
{
if (reader.Read())
{
firstname.Text = reader.GetString(reader.GetOrdinal("nombre"));
}
}
}
}
//This is where the change textbox should be send to the database
protected void update_Click(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
string UpdateStatement1;
UpdateStatement1 = "update erp_empleados set " +
"nombre = '" + firstname.Text "' where empleado_id = " +
gestiondeempleados.empleadoID;
debugmsg.Text = UpdateStatement1;
//MySql connection code....
}
}
I want the changed text field to be send and not same text field I have loaded initial.
I suggest to use Ajax request instead of asp.net web forms events, this is the standard and modern approach to submit data without postback in any web applications framework.
You need to create a web method and call it , you can search more "asp.net web method ajax jquery calling"
$('#btnSave').click(function (e) {
e.preventDefault(); //
var element = this;
$.ajax({
url: "YOUR_WEB_METHOD_URL",
type: "POST",
data: JSON.stringify({ 'Options': someData}),
dataType: "json",
traditional: true,
contentType: "application/json; charset=utf-8",
success: function (data) {
if (data.status == "Success") {
alert("Done");
$(element).closest("form").submit(); //<------------ submit form
} else {
alert("Error occurs on the Database level!");
}
},
error: function () {
alert("An error has occured!!!");
}
});
});
Or you can use update panel (Very easy approach and you will use same asp.net events but I'm not recommended this approach because its inject a lot of not clear scripts to your page and its not the best practice from security side and maybe partially not working with some modern versions of browsers).
To use update panel please take look on below link
https://www.c-sharpcorner.com/UploadFile/f50501/use-updatepanel-control-in-Asp-Net/
I figured it out.All I hate to do was adding !IsPostBack were the variable where initialized. I am not sure why doe could anyone clarify the reason.
//This is where the textboxes are populated
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string employeID = gestiondeempleados.empleadoID;
string queryStr = "SELECT empleado_id,nombreusuario,nombre Where empleado_id =" + employeID;
using (conn = new MySql.Data.MySqlClient.MySqlConnection(connString))
{
using (cmd = new MySql.Data.MySqlClient.MySqlCommand(queryStr, conn))
{
conn.Open();
using (reader = cmd.ExecuteReader())
{
if (reader.Read())
{
firstname.Text = reader.GetString(reader.GetOrdinal("nombre"));
}
}
}
}
}

ASP.NET / Webforms / C# - populating 2nd dropdown based on 1st dropdown without postback with code behind binding

Please help.
I have 3 dropdowns :
1. Country
2. Port
3. Company Name
Once 1st dropdown (countries) is selected, 2nd dropdown should be populated with a list of specific ports, then based on 1st and 2nd dropdown, the 3rd dropdown will be populated also.
this is a one time key-in. Meaning once selection is done, the user will save it in db and the value should remain in the dropdown unless the user change.
Right now, i'm using OnSelectedIndexChanged which is very slow because of the postback.
let me know if there's any other way of doing.
Thanks,
Jack
there could have several ways to achieve this. One of the ways is using WebService [WebMethod]. Ajax and JSON.
//You databinding method must be public and add [WebMethod] attribute
[WebMethod]
public static List<ListItem> GetCustomers()
{
string query = "SELECT CustId, CustName FROM Customers";
string constr = ConfigurationManager.ConnectionStrings["constr"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand(query))
{
List<ListItem> custListItem = new List<ListItem>();
cmd.CommandType = CommandType.Text;
cmd.Connection = con;
con.Open();
using (SqlDataReader sdr = cmd.ExecuteReader())
{
while (sdr.Read())
{
custListItem.Add(new ListItem
{
Value = Convert.ToString(sdr["CustId"]),
Text = Convert.ToString(sdr["CustName"])
});
}
}
con.Close();
return custListItem;
}
}
}
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
$(function () {
$.ajax({
type: "POST",
url: "CustomerList.aspx/GetCustomers",
data: '{}',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (r) {
var ddlCustomers = $("[id*=ddlCustomers]");
ddlCustomers.empty().append('<option selected="selected" value="0">Please select</option>');
$.each(r.d, function () {
ddlCustomers.append($("<option></option>").val(this['Value']).html(this['Text']));
});
}
});
});
</script>
You basically have 2 options, preload your values into a js structure (counties, ports and companies), or use a ajax/xmlhttprequest call to load just the relevant information (just the ports for a specific country). If preloading the values, you can either mix it in with your html in the body of a script tag, or have it be a seperate file that is loaded via a src attribute.
Which is best to use will vary based upon your user base and data size, and how frequently the data changes.

asp.net web service to interact with database

Sorry Im new to this approach.
I wanted to write various web servers at my sql server using .net c#.
to perform various operations to my database, e.g update customer, delete customer, select customer etc..
I am writing a client side web app (to deploy eventually on a mobile) that will send and retrieve data to these web services.
I am unsure of the methods that allow me to do this from my client that allow authentication and are reasonably secure.
So far I am thinking of Jquery AJAX?
Ajax will allow you to exchange data between server and client within a page without reloading it. It sends and receives data using HTTP messages.
Setup REST service and add jQuery to HTML page. At this point you have a communication channel. Now you have to think about security model.
You may require client to send username+password or authentication token in every request. You also may place these credentials anywhere within HTTP message (cookie, header, body).
If it's your first experience with ASP.NET, try to use standard Forms Authentication (it stores authentication token in ASPX_AUTH cookie) or any .NET builtin authentication models.
See articles below to see examples:
Security in ASP.NET
Secure a Web API with Individual Accounts and Local Login in ASP.NET Web API 2.2
first Create ASMX File
second Create Class inside or outside the ASMX File
public class NationalityInfo
{
public int ID { get; set; }
public string Name { get; set; }// any Other
}
[WebMethod]
[ScriptMethod]
public List<NationalityInfo> LoadNat()
{
string Conn = System.Configuration.ConfigurationManager.ConnectionStrings["GPS_TrackingConnectionString"].ConnectionString;
List<NationalityInfo> NatInformation = new List<NationalityInfo>();
DataSet ds;
using (SqlConnection con = new SqlConnection(Conn))
{
using (SqlCommand cmd = new SqlCommand("select * from Nationality", con))
{
con.Open();
cmd.Connection = con;
cmd.CommandType = CommandType.Text;
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
ds = new DataSet();
da.Fill(ds);
}
}
}
try
{
if (ds != null)
{
if (ds.Tables.Count > 0)
{
if (ds.Tables[0].Rows.Count > 0)
{
foreach (DataRow dr in ds.Tables[0].Rows)
{
NatInformation.Add(new NationalityInfo()
{
ID = Convert.ToInt32(dr["Id"]),
Name = dr["Name"].ToString()
});
}
}
}
}
}
catch (Exception ex)
{
throw ex;
}
return NatInformation;
}
in the client side
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "Query.asmx/LoadNat",
data: "{}",
dataType: "json",
success: function (Result) {
Result = Result.d;
$.each(Result, function (key, value) {
var id=value.ID;
var Name=value.Name;
});
},
error: function (Result) {
}
});
Note in the client side you get object (Array ) not json
For saving (Get values from client to server)
function SaveNat() {
var Nat = {};
Nat.ID = txtID.value;
Nat.Name = txtName.value;
var NatVar = JSON.stringify(Nat);
$.ajax({
type: "POST",
url: "Query.asmx/SaveNat",
data: '{Nat: ' + NatVar + '}',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
alert('Saved');
}
});
}
Server Save Function
[WebMethod(EnableSession = true)]
[ScriptMethod]
public void SaveNat(NationalityInfo Nat)
{
string Conn = ""// Your Connection
using (SqlConnection con = new SqlConnection(Conn))
{
using (SqlCommand cmd = new SqlCommand())
{
//Save }
}
}
as for security you must not sent user name and passwor in plain text it will be visible in the Browser trafic
you should send it encrypted and decrypt it on your server

Why SignalR change function hitting many many times?

I try to develop an asp.net mvc application and also trying to use signalr. The problem is that i have two tables that control user notificitaions in project. I have a Notification table and also NotificationUser table which is many to many table of notification and user tables. I am trying achive that if a user create a notification to another user in system I try to show a pop-up that confirm user with a simple message like 'Hey!New notification received'. The problem is javascript change function of signalr hitting so many times.All step i used in signalR listed below
the stored procedure
ALTER PROCEDURE [dbo].[GetNotifications]
#userid int
AS
BEGIN
select n.Ntf_Title,Ntf_Description,n.Ntf_Date from dbo.SysNotifications n INNER JOIN dbo.SysNotificationUser u on n.Ntf_ID =u.NtU_NtfID where NtU_UserID=#userid AND NtU_IsRead=0
END
The Hub
[HubName("signalRHub")]
public class NtfHub : Hub
{
[HubMethodName("notifyChanges")]
public static void NotifyChanges()
{
var context = GlobalHost.ConnectionManager.GetHubContext<NtfHub>();
context.Clients.All.notifyChanges();
}
}
The StartUp Class
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
The Partial View
[HttpGet]
public ActionResult GetNtf()
{
//NtfRepo rp = new NtfRepo(this.HttpContext);
string connectionString = ConfigurationManager.ConnectionStrings["conn"].ConnectionString;
int userid =id;
using (SqlConnection sqlcon = new SqlConnection(connectionString))
{
using (SqlCommand sqlcom = new SqlCommand("[GetNotifications]", sqlcon))
{
sqlcon.Open();
sqlcom.CommandType = CommandType.StoredProcedure;
sqlcom.Parameters.AddWithValue("#userid", userid);
sqlcom.Notification = null;
SqlDependency dependancy = new SqlDependency(sqlcom);
dependancy.OnChange += dependancy_OnChange;
var reader = sqlcom.ExecuteReader();
var ntf= reader.Cast<IDataRecord>()
.Select(e => new PopulateNtfBar()
{
Title = e.GetString(0),
Description = e.GetString(1),
TimeDiff = FindDifferenceTime(e.GetDateTime(2))
}).ToList();
return PartialView("~/Views/Shared/CheckNotification.cshtml", ntf);
}
}
}
At Last,The Script
$(function () {
var notification = $.connection.signalRHub;
// Create a function that the hub can call to broadcast messages.
notification.client.notifyChanges = function () {
getData();
toastr.warning("Hey,You have Ntf");
};
// Start the connection.
$.connection.hub.start().done(function () {
getData();
}).fail(function (e) {
});
});
function getData() {
var tbl = $("#header_notification_bar")
$.ajax({
url: '#Url.Action("GetNtf","Home")',
contentType: 'application/html ; charset:utf-8',
type: 'GET',
dataType: 'html'
}).success(function (result) {
tbl.empty().append(result);
}).error(function () {
});
}
notification.client.notifyChanges hitting so many times if a user create a notification.Where is the problem? Any idea? i cannot optimize it
EDIT 1
I am calling NtfHub.NotifyChanges in controller.
void dependancy_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
NtfHub.NotifyChanges();
}
}
Although I think that SqlDependency is wrong approach for this feature, you could try to solve this specific problem this way:
Add parameter "subscribeToNotifications" to your controller action
public ActionResult GetNtf(bool subscribeToNotifications)
Create SqlDependency only if it is True.
Then subscribe to notifications only on hub started (this will prevent creation of multiple SqlDependencies for same user):
$(function () {
var notification = $.connection.signalRHub;
// Create a function that the hub can call to broadcast messages.
notification.client.notifyChanges = function () {
getData(false);
toastr.warning("Hey,You have Ntf");
};
// Start the connection.
$.connection.hub.start().done(function () {
getData(true);
}).fail(function (e) {
});
});
function getData(subscribeToNotifications) {
var tbl = $("#header_notification_bar")
$.ajax({
url: '#Url.Action("GetNtf","Home")' + '?subscribeToNotifications=' + subscribeToNotifications,
contentType: 'application/html ; charset:utf-8',
type: 'GET',
dataType: 'html'
}).success(function (result) {
tbl.empty().append(result);
}).error(function () {
});
}
But be aware that every page refresh will still create new listener without managing subscriptions on server side.
Option 2 is to create single SqlDependency (on server app start) omitting userId parameter - anyway you are sending notification to all users no matter which one got the message.
Option 3 - the real solution is get rid of SqlDependency at all and send notification only to specific user (recipient of the message)
The reason is that you are not unsubscribing from the dependancy_OnChange event, a sqldependency trigger is a one shot execution, so you have to subscribe to the new one every time it fires, what you are not doing is unsubscribing from the previous event handler, so when you subscribe to the new one, you now have multiple handlers for the same trigger.
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = sender as SqlDependency;
if (dependency != null) dependency.OnChange -= dependency_OnChange;
//Recall your SQLDependency setup method here.
SetupDependency();
}

Database Change Notifications in ASP.NET using SignalR and SqlDependency

I'm a serious MVC & SignalR newbie!
I found this tutorial online that shows how to use Database Change notifications and display the data in an MVC app. The issue I'm having is replicating the MVC side of things. I've managed I think to work through the tutorial correctly and spin up the application, however, I'm getting an Undefined connection and the app bombs out.
Does anyone know where I might find the associated source files for this example, or has anyone managed to successfully implement this and can shed some light on the configuration of this from an MVC point of view.
Thanks!
To display real time updates from the SQL Server by using SignalR and SQL Dependency I've done these steps:
Step 1: Enable Service Broker on the database
The following is the query that need to enable the service broker
ALTER DATABASE BlogDemos SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE ;
Step 2: Enable SQL Dependency
//Start SqlDependency with application initialization
SqlDependency.Start(connString);
Step 3: Create the hub Class
public class MessagesHub : Hub
{
private static string conString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ToString();
public void Hello()
{
Clients.All.hello();
}
[HubMethodName("sendMessages")]
public static void SendMessages()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MessagesHub>();
context.Clients.All.updateMessages();
}
}
Step 4: Get the Data from the Repository
Create MessagesRepository to get the messages from the database when data is updated.
public class MessagesRepository
{
readonly string _connString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
public IEnumerable<Messages> GetAllMessages()
{
var messages = new List<Messages>();
using (var connection = new SqlConnection(_connString))
{
connection.Open();
using (var command = new SqlCommand(#"SELECT [MessageID], [Message], [EmptyMessage], [Date] FROM [dbo].[Messages]", connection))
{
command.Notification = null;
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
while (reader.Read())
{
messages.Add(item: new Messages { MessageID = (int)reader["MessageID"], Message = (string)reader["Message"], EmptyMessage = reader["EmptyMessage"] != DBNull.Value ? (string) reader["EmptyMessage"] : "", MessageDate = Convert.ToDateTime(reader["Date"]) });
}
}
}
return messages;
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
MessagesHub.SendMessages();
}
}
}
Step 5: Register SignalR at startup class
app.MapSignalR();
Step 6: then use the method to show real time at your view
<script src="/Scripts/jquery.signalR-2.1.1.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var notifications = $.connection.messagesHub;
//debugger;
// Create a function that the hub can call to broadcast messages.
notifications.client.updateMessages = function () {
getAllMessages()
};
// Start the connection.
$.connection.hub.start().done(function () {
alert("connection started")
getAllMessages();
}).fail(function (e) {
alert(e);
});
});
function getAllMessages()
{
var tbl = $('#messagesTable');
$.ajax({
url: '/home/GetMessages',
contentType: 'application/html ; charset:utf-8',
type: 'GET',
dataType: 'html'
}).success(function (result) {
tbl.empty().append(result);
}).error(function () {
});
}
</script>
Hope this helps :)
You should provide your code, it's easier to figure out what the problem is that way.
From what you have mentioned, I can think of only two things.
1) The tutorial you used, it's using SignalR 1.0. If you are using SignalR 2.0, you should not follow the tutorial exactly.
A few things changed in SignalR 2.0, you can read about it using below link:
http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/upgrading-signalr-1x-projects-to-20
2) If you are using SignalR 1.0, make sure you include the "jquery" file correctly, and if it's correct, try to change the hub proxy path like below:
<script src="/signalr/Hubs"></script>
Hope it helps

Categories

Resources