SQL dependency event not being triggered - c#

First time working with SQL Dependency... but after having gone over several examples I feel as I am doing everything correct. I've checked that the Broker is Enabled. I've further checked that my query is correct. I am not receiving any exceptions at all! All and all everything seems as it should work... but it is not, and I have no idea how to begin to troubleshoot it without any exceptions being thrown.
Any help would be VERY much appreciated!
Here is my class:
public class NotificationEvent
{
private delegate void RateChangeNotification(DataTable table);
private SqlDependency dependency;
string ConnectionString = #"ConnectionString";
string UserName = Environment.UserName;
public async void StartNotification()
{
SqlDependency.Start(this.ConnectionString, "UserNotificationsQueue");
SqlConnection connection = new SqlConnection(this.ConnectionString);
await connection.OpenAsync();
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandType = CommandType.Text;
command.CommandText = string.Format("SELECT [NotificationID],[UserFrom],[UserTo],[DateTimeSent],[Notification] FROM [dbo].[PersonnellNotifications]", UserName);
command.Notification = null;
this.dependency = new SqlDependency(command, "Service=PostUserNotificationsQueue;", Int32.MaxValue);
dependency.OnChange += new OnChangeEventHandler(this.SqlDependencyOnChange);
await command.ExecuteReaderAsync();
}
private void SqlDependencyOnChange(object sender, SqlNotificationEventArgs eventArgs)
{
if (eventArgs.Info == SqlNotificationInfo.Invalid)
{
Console.WriteLine("The above notification query is not valid.");
}
else
{
Console.WriteLine("Notification Info: " + eventArgs.Info);
Console.WriteLine("Notification source: " + eventArgs.Source);
Console.WriteLine("Notification type: " + eventArgs.Type);
}
}
public void StopNotification()
{
SqlDependency.Stop(this.ConnectionString, "QueueName");
}
}
I am initializing this from another classes IniatializeComponent() as seen:
private void InitializeComponent()
{
// Initialize SQL Dependancy
ne.StartNotification();
}

I have just tested following in my Code and Its working good. I have simplified your code. Please see if this is working and you are getting a call in OnNotificationChange on Db Change.
public async void RegisterForNotification()
{
var connectionString = #"ConnectionString";
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
var queryString = "Your Query String";
using (var oCommand = new SqlCommand(queryString, connection))
{
// Starting the listener infrastructure...
SqlDependency.Start(connectionString);
var oDependency = new SqlDependency(oCommand);
oDependency.OnChange += OnNotificationChange;
// NOTE: You have to execute the command, or the notification will never fire.
await oCommand.ExecuteReaderAsync();
}
}
}
private void OnNotificationChange(object sender, SqlNotificationEventArgs e)
{
Console.WriteLine("Notification Info: " + e.Info);
//Re-register the SqlDependency.
RegisterForNotification();
}

Are you setting SQLClientPermission? see:
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/enabling-query-notifications
// Code requires directives to
// System.Security.Permissions and
// System.Data.SqlClient
private bool CanRequestNotifications()
{
SqlClientPermission permission =
new SqlClientPermission(
PermissionState.Unrestricted);
try
{
permission.Demand();
return true;
}
catch (System.Exception)
{
return false;
}
}

Related

Call an api from web application periodically ASP.NET Razor

Hy,
I'm trying to call api from my web application every hour and store data in my database.
This is my Code to call
public class GetNationFromAPI
{
public static async Task GetNation()
{
try
{
string connectionString = ConfigurationManager.ConnectionStrings["PW"].ConnectionString;
string url = ConfigurationManager.GetSection("url").ToString();//Get Url from appsettings.json
var response = await GetApi.GetClient.GetStringAsync(url);
var obj = JsonConvert.DeserializeObject<RootDataModel>(response);
using SqlConnection con = new SqlConnection(connectionString);
string deleteQuery = "Delete From Nation";
SqlCommand com = new SqlCommand(deleteQuery, con);
con.Open();
com.ExecuteNonQuery();
con.Close();
string nationtblname = ConfigurationManager.GetSection("nationtblname").ToString();//Get Table name from appsettings.json
string nationquery = string.Format("insert into {0} (Nation_Id, Nation, Alliance_Id, Alliance, Score, VacMode, Alliance_Position, soldiers, tanks, aircraft, ships) " +
"Values (#nation_Id, #nation, #alliance_Id, #alliance, #alliance_Position, #score, #vacMode,#v_mode_turns, #soldiers, #tanks, #aircraft, #ships)", nationtblname);
foreach (var nations in obj.data)
{
SqlCommand comm = new SqlCommand(nationquery, con);
con.Open();
comm.Parameters.AddWithValue("#nation_Id", nations.nation_id);
comm.Parameters.AddWithValue("#nation", nations.nation);
comm.Parameters.AddWithValue("#alliance_Id", nations.alliance_id);
comm.Parameters.AddWithValue("#alliance", nations.alliance);
comm.Parameters.AddWithValue("#alliance_Position", nations.alliance_position);
comm.Parameters.AddWithValue("#score", nations.score);
comm.Parameters.AddWithValue("#vacMode", nations.v_mode);
comm.Parameters.AddWithValue("#v_mode_turns", nations.v_mode_turns);
comm.Parameters.AddWithValue("#soldiers", nations.soldiers);
comm.Parameters.AddWithValue("#tanks", nations.tanks);
comm.Parameters.AddWithValue("#aircraft", nations.aircraft);
comm.Parameters.AddWithValue("#ships", nations.ships);
comm.ExecuteNonQuery();
con.Close();
};
}
catch
{
throw new Exception();
}
}
}
Initially I did it from console application and it worked fine however I want my web app to do automatically every hour so I tried this in startup class of web application.
public class Startup
{
private static Timer atimer;
public static async Task Main()
{
atimer = new Timer
{
Interval = 3600000
};
atimer.Elapsed += await OnTimedEventAsync();
atimer.AutoReset = true;
atimer.Enabled = true;
}
private static async Task<ElapsedEventHandler> OnTimedEventAsync()
{
GetApi.InitializeClient();
await GetNationFromAPI.GetNation();
throw new Exception();
}
When I start my application and prompted to localhost and try to view nation. I get object reference not set to an instance of object instead of blank web page.

How to improve sqlite write performance in C#

I'm using sqlite to save log and meet write performance issue.
string log = "INSERT INTO Log VALUES ('2019-12-12 13:43:06','Error','Client','This is log message')"
public int WriteLog(string log)
{
return ExecuteNoQuery(log);
}
public int ExecuteNoQuery(string command)
{
int nResult = -1;
try
{
using (SQLiteConnection dbConnection = new SQLiteConnection(ConnectString))
{
dbConnection.Open();
using (SQLiteCommand dbCommand = dbConnection.CreateCommand())
{
dbCommand.CommandText = command;
nResult = dbCommand.ExecuteNonQuery();
}
}
}
catch (Exception e)
{
// Output error message
}
return nResult;
}
Search in google, transaction could improve the write performance significantly, but unfortunately I don't know when a log message will come, I could not combine the log message. Is there any other way to improve my log write performance?
I tried to add a timer to my code and commit transaction automatically. But I don't think it's a good way to speed up log write performance.
public class DatabaseManager : IDisposable
{
private static SQLiteTransaction transaction = null;
private SQLiteConnection dbConnection = null;
private static Timer transactionTimer;
private long checkInterval = 500;
private DatabaseManager(string connectionString)
{
dbConnection = new SQLiteConnection(connectionString);
dbConnection.Open();
StartTransactionTimer();
}
public void Dispose()
{
if(transaction != null)
{
transaction.Commit();
transaction = null;
}
dbConnection.Close();
dbConnection = null;
}
private void StartTransactionTimer()
{
transactionTimer = new Timer();
transactionTimer.Interval = checkInterval;
transactionTimer.Elapsed += TransactionTimer_Elapsed;
transactionTimer.AutoReset = false;
transactionTimer.Start();
}
private void TransactionTimer_Elapsed(object sender, ElapsedEventArgs e)
{
StartTransation();
transactionTimer.Enabled = true;
}
public void StartTransation()
{
try
{
if (dbConnection == null || dbConnection.State == ConnectionState.Closed)
{
return;
}
if (transaction != null)
{
transaction.Commit();
transaction = null;
}
transaction = dbConnection.BeginTransaction();
}
catch(Exception e)
{
LogError("Error occurs during commit transaction, error message: " + e.Message);
}
}
public int ExecuteNoQuery(string command)
{
int nResult = -1;
try
{
using (SQLiteCommand dbCommand = dbConnection.CreateCommand())
{
dbCommand.CommandText = command;
nResult = dbCommand.ExecuteNonQuery();
}
}
catch (Exception e)
{
LogError("Error occurs during execute sql no result query, error message: ", e.Message);
}
return nResult;
}
}
This started out as a comment, but it's evolving to an answer.
Get rid of the GC.Collect(); code line.
That's not your job to handle garbage collection - and you're probably degrading performance by using it.
No need to close the connection, you're disposing it in the next line anyway.
Why are you locking? Insert statements are usually thread safe - and this one doesn't seem to be an exception of that rule.
You are swallowing exceptions. That's a terrible habit.
Since you're only ever insert a single record, you don't need to return an int - you can simply return a bool (true for success, false for failure)
Why you don't use the entity framework to do the communications with the database?
For me is the easiest way. It's a Microsoft library so you can sure that the performance is very good.
I made some work with entity framework and sqlite db's and everything works very well.
Here an example of use:
var context = new MySqliteDatabase(new SQLiteConnection(#"DataSource=D:\\Mydb.db;cache=shared"));
var author = new Author {
FirstName = "William",
LastName = "Shakespeare",
Books = new List<Book>
{
new Book { Title = "Hamlet"},
new Book { Title = "Othello" },
new Book { Title = "MacBeth" }
}
};
context.Add(author);
context.SaveChanges();
The type of MySqliteDatabase can be created automatically using database first approach or with Code First approach. You have a lot of information and examples on the internet.
Here the link to the official documentation:
https://learn.microsoft.com/en-us/ef/ef6/

OracleDependency with ASP.NET MVC 3

I want to use OracleChangeNotifications in an ASP.NET MVC 3 App. I have created a simple console application with the example code from here, and it works as expected. If I change the registered database table, a notification gets fired in the console application.
Then I created an ASP.NET MVC 3 app with the same sample code but the MVC app is not getting any notifications. Im using oracle 11g. Apparently the listener gets registered in the oracle database. If I run the query:
SELECT * FROM user_change_notification_regs;
I get the following result:
REGID: 127
REGFLAGS: 4
CALLBACK: net8://(ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=59747)?PR=0
OPERATIONS_FILTER: 0
CHANGELAG: 0
TIMEOUT: 48556
TABLE_NAME: MyTable
My guess is that IIS is somehow blocking the callback, but I cannot figure out why?
Any ideas?
Heres is the code I am using:
using System;
using System.Data;
using NLog;
using Oracle.DataAccess.Client;
namespace CacheTestWebApp.Services
{
public class Notification
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
private const string ConnectionString = "<connection_string>";
private const string TableName = "MyTable";
private const string QueryString = "select * from " + TableName;
public static void RegisterNotification()
{
try
{
using (var con = new OracleConnection(ConnectionString))
{
con.Open();
var cmd = new OracleCommand(QueryString, con);
var dependency = new OracleDependency();
dependency.OnChange += dependency_OnChange;
dependency.AddCommandDependency(cmd);
cmd.Notification.IsNotifiedOnce = false;
cmd.AddRowid = true;
cmd.ExecuteNonQuery();
con.Close();
con.Dispose();
}
}
catch (Exception e)
{
_logger.Error(e.Message);
}
}
private static void dependency_OnChange(object sender, OracleNotificationEventArgs eventArgs)
{
// handle notification
}
}
}
Notification.RegisterNotification() is executed in Application_Start() in the Global.asax.cs:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
Notification.RegisterNotification();
}

MySql executing a MySqlScript even when the connection has been closed

Is it possible for mysql to execute a script even when the connection has been closed?
I am using mysql community server , through a .NET connector API.
Was using c# to test out the API.
I have the following static class
using System;
using System.Data;
using MySql.Data;
using MySql.Data.MySqlClient;
public static class DataBase
{
static string connStr = "server=localhost;user=root;port=3306;password=*******;";
static MySqlConnection conn;
public static bool Connect()
{
conn = new MySqlConnection(connStr);
try
{
conn.Open();
}
catch (Exception Ex)
{
ErrorHandler(Ex);
return false;
}
return true;
}
public static int ExecuteScript(string scripttext) // returns the number of statements executed
{
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandText = scripttext;
MySqlScript script;
int count= 0;
try
{
script = new MySqlScript(conn, cmd.CommandText);
script.Error += new MySqlScriptErrorEventHandler(script_Error);
script.ScriptCompleted += new EventHandler(script_ScriptCompleted);
script.StatementExecuted += new MySqlStatementExecutedEventHandler(script_StatementExecuted);
count = script.Execute();
}
catch (Exception Ex)
{
count = -1;
ErrorHandler(Ex);
}
return count;
}
# region EventHandlers
static void script_StatementExecuted(object sender, MySqlScriptEventArgs args)
{
string Message = "script_StatementExecuted";
}
static void script_ScriptCompleted(object sender, EventArgs e)
{
string Message = "script_ScriptCompleted!";
}
static void script_Error(Object sender, MySqlScriptErrorEventArgs args)
{
string Message = "script_Error: " + args.Exception.ToString();
}
# endregion
public static bool Disconnect()
{
try
{
conn.Close();
}
catch (Exception Ex)
{
ErrorHandler(Ex);
return false;
}
return true;
}
public static void ErrorHandler(Exception Ex)
{
Console.WriteLine(Ex.Source);
Console.WriteLine(Ex.Message);
Console.WriteLine(Ex.ToString());
}
}
and I am using the following code to test out this class
using System;
using System.Data;
namespace Sample
{
public class Sample
{
public static void Main()
{
if (DataBase.Connect() == true)
Console.WriteLine("Connected");
if (DataBase.Disconnect() == true)
Console.WriteLine("Disconnected");
int count = DataBase.ExecuteScript("drop database sample");
if (count != -1)
{
Console.WriteLine(" Sample Script Executed");
Console.WriteLine(count);
}
Console.ReadKey();
}
}
}
I noticed that even though I have closed my MySql connection using Disconnect() - which i have defined, mysql continues to execute the command i give next and no error is generated.
I feel like I am doing something wrong, as an error should be generated when i try to execute a script on a closed connection.
Is it a problem in my code/logic or some flaw in mysql connector?
I did check through the mysql workbench whether the command was executed properly and it was.
This is a decompile of MySqlScript.Execute code....
public unsafe int Execute()
{
......
flag = 0;
if (this.connection != null)
{
goto Label_0015;
}
throw new InvalidOperationException(Resources.ConnectionNotSet);
Label_0015:
if (this.query == null)
{
goto Label_002A;
}
if (this.query.Length != null)
{
goto Label_002C;
}
Label_002A:
return 0;
Label_002C:
if (this.connection.State == 1)
{
goto Label_0047;
}
flag = 1;
this.connection.Open();
....
As you can see, when you build the MySqlScript the connection passed is saved in an internal variable and before executing the script, if the internal connection variable is closed, the code opens it. Not checked but I suppose that it also closes the connection before exiting (notice that flag=1 before opening)
A part from this I suggest to change your code to avoid keeping a global MySqlConnection object. You gain nothing and risk to incur in very difficult bugs to track.
static string connStr = "server=localhost;user=root;port=3306;password=*******;";
public static MySqlConnection Connect()
{
MySqlConnection conn = new MySqlConnection(connStr);
conn.Open();
return conn;
}
This approach allows to write code that use the Using Statement
public static int ExecuteScript(string scripttext) // returns the number of statements executed
{
using(MySqlConnection conn = Database.Connect())
using(MySqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = scripttext;
....
}
}
The Using statement will close and dispose the connection and the command freeing valuable resources and also in case of exception you will be sure to have the connection closed and disposed

Update data using n-tier does nothing

I have got 3-tier where carry out my code in business layer I run code for update
public override bool LoadProperties2List(string TypeOfOperation)
{
SortedList Sl = new SortedList();
Sl.Add("#CommandType", TypeOfOperation);
Sl.Add("#UserName",UserName);
Sl.Add("#SecondarySchool",SecondarySchool);
Sl.Add("#University",University);
Sl.Add("#Qualification",Qualification);
Sl.Add("#JobTitle",JobTitle);
Sl.Add("#Company",Company);
Sl.Add("#PhotoUrl", PhotoUrl);
ProcedureName = "MangeUserInfo";
if (db.RunProcedure(ProcedureName, Sl) == 1)
return true;
else
return false;
}
public bool updateUser(string User, string SecondaryS, string Unvi, string Qua, string jobtitle, string company)
{
this.UserName = User;
this.SecondarySchool = SecondaryS;
this.University = Unvi;
this.Qualification = Qua;
this.JobTitle = jobtitle;
this.Company = company;
if (Update())
return true;
else
return false;
}
and in data access layer
public void ConnectDB(CommandType CT,string ProNameSQl)
{
cn = new SqlConnection("Data Source=.;Initial Catalog=Conversation;Integrated Security=True");
cmd = new SqlCommand();
cmd.Connection = cn;
cmd.CommandType = CT;
cmd.CommandText = ProNameSQl;
cn.Open();
}
public int RunProcedure(string ProcedureName, SortedList Paraval)
{
ConnectDB(CommandType.StoredProcedure, ProcedureName);
for (int x = 0; x < Paraval.Count; x++)
{
try
{
cmd.Parameters.AddWithValue(Paraval.GetKey(x).ToString(), Paraval.GetByIndex(x));
}
catch
{
;
}
}
return ExceNoneQuery();
}
and then in another layer I use this method to call procedure process kind and run
public bool Update()
{
return LoadProperties2List("u");
}
at last layer presentation layer
I do that
protected void btnsave_Click(object sender, EventArgs e)
{
//upadate info
bool Result = false;
UsersInfo Upd = new UsersInfo();
try
{
Result = Upd.updateUser(username, TxtSecondarySchool.Text, TxtUniversity.Text, TxtQualification.Text, TxtJobTitle.Text, TxtCompany.Text);
if (Result==true)
lblMessage.Text = "Record Updated Successfully.";
else
lblMessage.Text = "Record couldn't updated";
}
catch (Exception ee)
{
lblMessage.Text = ee.Message.ToString();
} finally
{
Upd = null;
}
}
When I run the code only the result is
lblMessage.Text = "Record couldn't updated";
What is the error which makes it not to work correctly?
I also find something strange that the textboxes doesn't take the new values it pass the same value despite change why? I need help
The error is that the textbox loads in a routine in the Page's Startup event, with the routine placed outside the If IsNotPostback loop. So, the default value just reloads every time the page is refreshed, and thus appears to be 'unchangeable'.

Categories