Issue of multiple SQL notifications in ASP.Net web application on page refresh - sql

I am facing an issue while using SQL Server Notifications. I am developing a web application in ASP.net where one of the page needs to be notified about new entries in one of the tables in a SQL Server database. I am using SQL Server Notification services along with Signal R to achieve this functionality.All seems to work fine with my web page getting updates about new data entries.
The problem arises when the page using notification is refreshed. I find the no of notification for single entry in database go up by the number of refreshes. So if I refresh the page thrice, I get 3 notifications for one entry. I am bit concerned if this would be a burden on server when the no of connected users increases. Also if there is an error while processing the request to update the page with new entry, the user gets multiple error messages with same text. I tried debugging my code and found out that the on change event of SqlDependency object used is fired multiple time with different IDs every time. Below is brief overview of what my code is doing to use notifications -
I am using SQL Server 2012 and enable_broker is set for the database.
In global.asax, I am using application_start and application_stop events to start and stop SqlDependency.
In page code, I am setting a new SqlDependency object on page load using a command object to monitor the exact data field of the table.
When onchange of SqlDependency object fires, I am notifying the UI using Signal R hub class. Then I remove the OnChange handler of the SqlDependency object, call for SqlDependency.Stop(connectionstring), set SqlDependency object to nothing, call for SqlDependency.Start(connectionstring) and finally set up the SqlDependency object again using the command object for updated data. This whole set to nothing-stop-start-reset object is to continue monitoring the data for changes.
The above steps work fine but when I refresh the page, those are repeated for the number of refreshes. I tried a lot of things by changing code and debugging but nothing seems to resolve the issue. Now I am wondering if it is some setting somewhere that I missed.
Please help me resolve this issue. Also let me know if any other information such as environment, coding details etc are required.
Regards,
Tanmay

This is probably caused by connection pooling. It reurns a notification for each connection open in the pool. You can cancel the pooling for this specific service by changing the Connection String property:
Pooling = False;

i have resolved the following problem by using the below code, its works me.
SingletonDbConnect.cs
public class SingletonDbConnect
{
private static SingletonDbConnect dbInstance;
private static string connString = ConfigurationManager.ConnectionStrings["MyConnection"].ConnectionString;
private readonly SqlConnection conn = new SqlConnection(connString);
private SingletonDbConnect()
{
}
public static SingletonDbConnect getDbInstance()
{
if (dbInstance == null)
{
dbInstance = new SingletonDbConnect();
}
return dbInstance;
}
public SqlConnection getDbConnection()
{
try
{
conn.Close();
conn.Open();
}
catch (SqlException e)
{
}
finally
{
}
return conn;
}
}
SqlDependencyEvent.cs
public class SqlDependencyEvent
{
internal static int PageLoadCounter = 0;
public void getEmailMessagesByEmailId(Guid emailid)
{
SingletonDbConnect conn = SingletonDbConnect.getDbInstance();
using (MembersController.command = new SqlCommand(SQL.emailmessagesbyaccount_sql(), conn.getDbConnection()))
{
MembersController.command.Notification = null;
if (MembersController.dependency == null)
{
MembersController.dependency = new SqlDependency(MembersController.command);
MembersController.dependency.OnChange += new OnChangeEventHandler(emailMessages_OnChange);
}
var reader = MembersController.command.ExecuteReader();
}
PageLoadCounter++;
}
private void emailMessages_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
if (MembersController.dependency != null)
{
MembersController.dependency.OnChange -= emailMessages_OnChange;
}
NotificationHub.EmailUpdateRecords();
SingletonDbConnect conn = SingletonDbConnect.getDbInstance();
using (MembersController.command = new SqlCommand(SQL.emailmessagesbyaccount_sql(), conn.getDbConnection()))
{
MembersController.command.Parameters.Add(new SqlParameter("#emailaccountid", defaultemailid));
MembersController.command.Notification = null;
MembersController.dependency = new SqlDependency(MembersController.command);
MembersController.dependency.OnChange += new OnChangeEventHandler(emailMessages_OnChange);
var reader = MembersController.command.ExecuteReader();
}
PageLoadCounter++;
}
}
}
MembersController.cs
public class MembersController : Controller
{
SingletonDbConnect conn = SingletonDbConnect.getDbInstance();
internal static SqlCommand command = null;
internal static SqlDependency dependency = null;
//
// GET: /Members/
public ActionResult Index()
{
SqlDependency.Stop(conn.getDbConnection().ConnectionString);
SqlDependency.Start(conn.getDbConnection().ConnectionString);
return View();
}
}
its resolved my problem and its working me, even we refresh page more than 1, but SqlDependency will call only once.
i used one of the MembersController for SqlDependency start and stop, its your own logic, you can use the same code in Global.ascx instead of MembersController.cs
i hope it will help you and resolve issue. ask me if you have still any problem thanks.

Related

Register Hibernate 5 Event Listeners

I am working on a legacy non-Spring application, and it is being migrated from Hibernate 3 to Hibernate 5.6.0.Final (latest at this time). I have generally never used Hibernate Event Listeners in my work, so this is quite new to me, and I am studying these in Hibernate 5.
Currently in some test class we have defined the code this way for Hibernate 3:
protected static Configuration createSecuredDatabaseConfig() {
Configuration config = createUnrestrictedDatabaseConfig();
config.setListener("pre-insert", "com.app.server.services.db.eventlisteners.MySecurityHibernateEventListener");
config.setListener("pre-update", "com.app.server.services.db.eventlisteners.MySecurityHibernateEventListener");
config.setListener("pre-delete", "com.app.server.services.db.eventlisteners.MySecurityHibernateEventListener");
config.setListener("pre-load", "com.app.server.services.db.eventlisteners.EkoSecurityHibernateEventListener");
return config;
}
This is obviously no longer valid, and I believe I need to create a Hibernate Integrator, which I have done.
public class MyEventListenerIntegrator implements Integrator {
#Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
EventListenerRegistry eventListenerRegistry = serviceRegistry.getService(EventListenerRegistry.class);
eventListenerRegistry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(new MySecurityHibernateEventListener());
eventListenerRegistry.getEventListenerGroup(EventType.PRE_UPDATE).appendListener(new MySecurityHibernateEventListener());
eventListenerRegistry.getEventListenerGroup(EventType.PRE_DELETE).appendListener(new MySecurityHibernateEventListener());
eventListenerRegistry.getEventListenerGroup(EventType.PRE_LOAD).appendListener(new MySecurityHibernateEventListener());
}
So, now I believe the next step is to add this to the session via the registry builder. I am using this website to help me:
https://www.boraji.com/hibernate-5-event-listener-example
Because we were using older Hibernate 3, we had code to create our session factory as follows:
protected static SessionFactory buildSessionFactory(Database db)
{
if (db == null) {
throw new NullPointerException("Database specifier cannot be null");
}
try {
Configuration config = createSessionFactoryConfiguration(db);
String url = config.getProperty("connection.url");
String user = config.getProperty("connection.username");
String password = config.getProperty("connection.password");
try {
String dbDriver = config.getProperty("hibernate.connection.driver_class");
Class.forName(dbDriver);
Connection conn = DriverManager.getConnection(url, user, password);
}
catch (SQLException error) {
logger.info("Didn't find driver, on QA or production, so it's okay to assume we have DB connection");
error.printStackTrace();
}
SessionFactory sessionFactory = config.buildSessionFactory();
sessionFactoryConfigs.put(sessionFactory, config); // Cannot recover config from factory instance, must be stored.
return sessionFactory;
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
logger.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
}
}
The link that I referred to above has a much different way of creating the sessionfactory. So, I'll be testing that out to see if it works in our app.
Without Spring handling our sessions and transactions, in this app it is coded by hand the way it was done before Spring, and I haven't seen that kind of code in years.
I solved this issue with the help from the link I provided above. However, I didn't copy exactly what they did, but some of it helped. My solution is as follows:
protected static SessionFactory createSecuredDatabaseConfig() {
Configuration config = createUnrestrictedDatabaseConfig();
BootstrapServiceRegistry bootstrapRegistry =
new BootstrapServiceRegistryBuilder()
.applyIntegrator(new EkoEventListenerIntegrator())
.build();
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder(bootstrapRegistry).applySettings(config.getProperties()).build();
SessionFactory sessionFactory = config.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
This was it. I tried multiple different ways to register the events without the BootstrapServiceRegistry, but none of those worked. I did have to create the integrator. What I did NOT include was the following:
MetadataSources sources = new MetadataSources(serviceRegistry )
.addPackage("com.myproject.server.model");
Metadata metadata = sources.getMetadataBuilder().build();
// did not create the sessionFactory this way
sessionFactory = metadata.getSessionFactoryBuilder().build();
If I had gone further and use this method to create the sessionFactory, then all of my queries would have been complaining about not being able to find the parameterName, which is something else.
The Hibernate Integrator and this method to create the sessionFactory is all for the unit tests. Without registering these events, one unit test would fail, and now it doesn't. So, this solves my problem for now.

Genesys Platform : Get Call Details From Sip Server

I want to get Call Details from Genesys Platform SIP Server.
And Genesys Platform has Platform SDK for .NET .
Anybod has a SIMPLE sample code which shows how to get call details using Platform SDK for .NET [ C# ] from SIP Server?
Extra Notes:
Call Details : especially i wanted to get AgentId for a given call
and
From Sip Server : I am not sure if Sip Server is the best candiate to
take call details. So open to other suggestions/ alternatives
You can build a class that monitor DN actions. Also you watch specific DN or all DN depending what you had to done. If its all about the call, this is the best way to this.
Firstly, you must define a TServerProtocol, then you must connect via host,port and client info.
var endpoint = new Endpoint(host, port, config);
//Endpoint backupEndpoint = new Endpoint("", 0, config);
protocol = new TServerProtocol(endpoint)
{
ClientName = clientName
};
//Sync. way;
protocol.Open();
//Async way;
protocol.BeginOpen();
I always use async way to do this. I got my reason thou :) You can detect when connection open with event that provided by SDK.
protocol.Opened += new EventHandler(OnProtocolOpened);
protocol.Closed += new EventHandler(OnProtocolClosed);
protocol.Received += new EventHandler(OnMessageReceived);
protocol.Error += new EventHandler(OnProtocolError);
Here there is OnMessageReceived event. This event where the magic happens. You can track all of your call events and DN actions. If you go genesys support site. You'll gonna find a SDK reference manual. On that manual quiet easy to understand there lot of information about references and usage.
So in your case, you want agentid for a call. So you need EventEstablished to do this. You can use this in your recieve event;
var message = ((MessageEventArgs)e).Message;
// your event-handling code goes here
switch (message.Id)
{
case EventEstablished.MessageId:
var eventEstablished = message as EventEstablished;
var AgentID = eventEstablished.AgentID;
break;
}
You can lot of this with this usage. Like dialing, holding on a call inbound or outbound even you can detect internal calls and reporting that genesys platform don't.
I hope this is clear enough.
If you have access to routing strategy and you can edit it. You can add some code to strategy to send the details you need to some web server (for example) or to DB. We do such kind of stuff in our strategy. After successful routing block as a post routing strategy sends values of RTargetPlaceSelected and RTargetAgentSelected.
Try this:
>
Genesyslab.Platform.Contacts.Protocols.ContactServer.Requests.JirayuGetInteractionContent
JirayuGetInteractionContent =
Genesyslab.Platform.Contacts.Protocols.ContactServer.Requests.JirayuGetInteractionContent.Create();
JirayuGetInteractionContent.InteractionId = "004N4aEB63TK000P";
Genesyslab.Platform.Commons.Protocols.IMessage respondingEventY =
contactserverProtocol.Request(JirayuGetInteractionContent);
Genesyslab.Platform.Commons.Collections.KeyValueCollection keyValueCollection =
((Genesyslab.Platform.Contacts.Protocols.ContactServer.Events.EventGetInteractionContent)respondingEventY).InteractionAttributes.AllAttributes;
We are getting AgentID and Place as follows,
Step-1:
Create a Custome Command Class and Add Chain of command In ExtensionSampleModule class as follows,
class LogOnCommand : IElementOfCommand
{
readonly IObjectContainer container;
ILogger log;
ICommandManager commandManager;
public bool Execute(IDictionary<string, object> parameters, IProgressUpdater progress)
{
if (Application.Current.Dispatcher != null && !Application.Current.Dispatcher.CheckAccess())
{
object result = Application.Current.Dispatcher.Invoke(DispatcherPriority.Send, new ExecuteDelegate(Execute), parameters, progress);
return (bool)result;
}
else
{
// Get the parameter
IAgent agent = parameters["EnterpriseAgent"] as IAgent;
IIdentity workMode = parameters["WorkMode"] as IIdentity;
IAgent agentManager = container.Resolve<IAgent>();
Genesyslab.Desktop.Modules.Core.Model.Agents.IPlace place = agentManager.Place;
if (place != null)
{
string Place = place.PlaceName;
}
else
log.Debug("Place object is null");
CfgPerson person = agentManager.ConfPerson;
if (person != null)
{
string AgentID = person.UserName;
log.DebugFormat("Place: {0} ", AgentID);
}
else
log.Debug("AgentID object is null");
}
}
}
// In ExtensionSampleModule
readonly ICommandManager commandManager;
commandManager.InsertCommandToChainOfCommandAfter("MediaVoiceLogOn", "LogOn", new
List<CommandActivator>() { new CommandActivator()
{ CommandType = typeof(LogOnCommand), Name = "OnEventLogOn" } });
enter code here
IInteractionVoice interaction = (IInteractionVoice)e.Value;
switch (interaction.EntrepriseLastInteractionEvent.Id)
{
case EventEstablished.MessageId:
var eventEstablished = interaction.EntrepriseLastInteractionEvent as EventEstablished;
var genesysCallUuid = eventEstablished.CallUuid;
var genesysAgentid = eventEstablished.AgentID;
.
.
.
.
break;
}

How to cancel HttpClient GET Web request

Is it possible to cancel HttpClient GET web request in Windows 8. I am looking for a solution to cancel my web request if the user press back key from the page. In my app i am using a static class for creating web request.
Alos i am using MVVM Light, and static viewmodels inside the app.
In the current situation, even if the user press the back button,
the vm stay alive and the call back reaches and executes in the VM.
So i am looking for a solution to cancel the request on back press.
Try this
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
await HttpGetRequest();
}
public CancellationTokenSource cts = new CancellationTokenSource();
private async Task HttpGetRequest()
{
try
{
DateTime now = DateTime.Now;
var httpClient = new HttpClient();
var message = new HttpRequestMessage(HttpMethod.Get, "https://itunes.apple.com/us/rss/toppaidapplications/limit=400/genre=6000/json");
var response = await httpClient.SendAsync(message, cts.Token);
System.Diagnostics.Debug.WriteLine("HTTP Get request completed. Time taken : " + (DateTime.Now - now).TotalSeconds + " seconds.");
}
catch (TaskCanceledException)
{
System.Diagnostics.Debug.WriteLine("HTTP Get request canceled.");
}
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
cts.Cancel();
}
The answer by #Farhan Ghumra is correct. Since we have moved to .Net 6 just like to add the following to the answer.
After you’re completely done with the CancellationTokenSource, dispose it directly E.g use cts.Dispose() (because you’ll most likely be using this with UI code and needing to share it with click event handling code, which means you wouldn’t be able to dispose of it with a using block)
More information can be found here.

How to fetch liferay entity through custom-finder in custom plugin portlet?

How can we fetch liferay entities through custom-finder using custom SQL?
Following is my sql query written in default.xml (I have trimmed down the query to the bare minimum so that the logic remains simple. Since it included a few functions and joins we couldn't use DynamicQuery API ):
SELECT
grp.*
FROM
Group_
WHERE
site = 1
AND active_ = 1
AND type_ <> 3
Relevant code in MyCustomGroupFinderImpl.java:
Session session = null;
try {
session = openSession();
// fetches the query string from the default.xml
String sql = CustomSQLUtil.get(FIND_ONLY_ACTIVE_SITES);
SQLQuery sqlQuery = session.createSQLQuery(sql);
sqlQuery.addEntity("Group_", GroupImpl.class);
// sqlQuery.addEntity("Group_", PortalClassLoaderUtil.getClassLoader().loadClass("com.liferay.portal.model.impl.GroupImpl"));
return (List<Group>) QueryUtil.list(sqlQuery, getDialect(), 0, QueryUtil.ALL_POS);
}
catch (Exception e) {
throw new SystemException(e);
}
finally {
closeSession(session);
}
This above code won't work as the GroupImpl class is present in portal-impl.jar and this jar cannot be used in custom portlet.
I also tried using sqlQuery.addEntity("Group_", PortalClassLoaderUtil.getClassLoader().loadClass("com.liferay.portal.model.impl.GroupImpl"))
But this above code throws exception:
com.liferay.portal.kernel.exception.SystemException:
com.liferay.portal.kernel.dao.orm.ORMException:
org.hibernate.MappingException:
Unknown entity: com.liferay.portal.model.impl.GroupImpl
But the same code works for our custom-entity, if we write sqlQuery.addEntity("MyCustomGroup", MyCustomGroupImpl.class);.
Thanks
I found out from the liferay forum thread that instead of session = openSession();
we would need to fetch the session from liferaySessionFactory as follows to make it work:
// fetch liferay's session factory
SessionFactory sessionFactory = (SessionFactory) PortalBeanLocatorUtil.locate("liferaySessionFactory");
Session session = null;
try {
// open session using liferay's session factory
session = sessionFactory.openSession();
// fetches the query string from the default.xml
String sql = CustomSQLUtil.get(FIND_ONLY_ACTIVE_SITES);
SQLQuery sqlQuery = session.createSQLQuery(sql);
// use portal class loader, since this is portal entity
sqlQuery.addEntity("Group_", PortalClassLoaderUtil.getClassLoader().loadClass("com.liferay.portal.model.impl.GroupImpl"));
return (List<Group>) QueryUtil.list(sqlQuery, getDialect(), 0, QueryUtil.ALL_POS);
}
catch (Exception e) {
throw new SystemException(e);
}
finally {
sessionFactory.closeSession(session); // edited as per the comment on this answer
// closeSession(session);
}
Hope this helps somebody on stackoverflow, also I found a nice tutorial regarding custom-sql which also uses the same approach.

Pros & cons bean vs SSJS?

I was trying to build a bean that always retrieves the same document ( a counter document), gets the current value, increment it and save the document with the new value. Finally it should return the value to the calling method and that would get me a new sequential number in my Xpage.
Since the Domino objects cannot be serialized or singleton'ed what's the benefit creating a bean doing this, over creating a SSJS function doing the exact same thing?
My bean must have calls to session, database, view and document, which then will be called every time.
The same within the SSJS-function except for session and database.
Bean:
public double getTransNo() {
try {
Session session = ExtLibUtil.getCurrentSession();
Database db = session.getCurrentDatabase();
View view = db.getView("vCount");
view.refresh();
doc = view.getFirstDocument();
transNo = doc.getItemValueDouble("count");
doc.replaceItemValue("count", ++transNo);
doc.save();
doc.recycle();
view.recycle();
} catch (NotesException e) {
e.printStackTrace();
}
return transNo;
}
SSJS:
function getTransNo() {
var view:NotesView = database.getView("vCount");
var doc:NotesDocument = view.getFirstDocument();
var transNo = doc.getItemValueDouble("count");
doc.replaceItemValue("count", ++transNo);
doc.save();
doc.recycle();
view.recycle();
return transNo;
}
Thank you
Both pieces of code are not good (sorry to be blunt).
If you have one document in your view, you don't need a view refresh which might be queued behind a refresh on another view and be very slow. Presumably you are talking about a single sever solution (since replication of the counter document would for sure lead to conflicts).
What you do in XPages is to create a Java class and declare it as application bean:
public class SequenceGenerator {
// Error handling is missing in this class
private double sequence = 0;
private String docID;
public SequenceGenerator() {
// Here you load from the document
Session session = ExtLibUtil.getCurrentSession();
Database db = session.getCurrentDatabase();
View view = db.getView("vCount");
doc = view.getFirstDocument();
this.sequence = doc.getItemValueDouble("count");
this.docID = doc.getUniversalId();
Utils.shred(doc, view); //Shred currenDatabase isn't a good idea
}
public synchronized double getNextSequence() {
return this.updateSequence();
}
private double updateSequence() {
this.sequence++;
// If speed if of essence I would spin out a new thread here
Session session = ExtLibUtil.getCurrentSession();
Database db = session.getCurrentDatabase();
doc = db.getDocumentByUnid(this.docID);
doc.ReplaceItemValue("count", this.sequence);
doc.save(true,true);
Utils.shred(doc);
// End of the candidate for a thread
return this.sequence;
}
}
The problem for the SSJS code: what happens if 2 users hit that together? At least you need to use synchronized there too. Using a bean makes it accessible in EL too (you need to watch out not to call it too often). Also in Java you can defer the writing back to a different thread - or not write it back at all and in your class initialization code read the view with the actual documents and pick the value from there.
Update: Utils is a class with static methods:
/**
* Get rid of all Notes objects
*
* #param morituri = the one designated to die, read your Caesar!
*/
public static void shred(Base... morituri) {
for (Base obsoleteObject : morituri) {
if (obsoleteObject != null) {
try {
obsoleteObject.recycle();
} catch (NotesException e) {
// We don't care we want go get
// rid of it anyway
} finally {
obsoleteObject = null;
}
}
}
}