Have hard time to get push notifications from server when using MSSQL scalling.
Message flow is one way from server to subscribers (are distributed by groups)
Push messages are not received on UI with the following host configuration:
GlobalHost.DependencyResolver.UseSqlServer(new SqlScaleoutConfiguration(ConfigurationManager.ConnectionStrings["SignalR"].ConnectionString));
Messages are created in SignalRDb and records are present in tables. However they do not reach UI. With SQL scaling disabled all messages propagates to UI successfully.
Here is my Notification code:
public void OnNext(ResultModel value)
{
Clients.Group(group).notify(value);
}
OnNext method always executes witho or without Scalling also group and value are correct. No exceptions thrown on UI or beckend. Below is UI part:
var hubProxy = $.connection.visitsHub;
hubProxy.client.notify = function (updatedResult) {
console.log(updatedResult.Id);
});
};
Any help is appreciated.
Related
I'm developing a class library that contains generic methods for these scenarios:
Live support chat (1 on 1 private text chat, with many admins and guests)
Rooms with many users where you can send broadcast and private messages
These two features above are already implemented and now it's necessary for my application to save messages.
My question is, what is the best way to store chat conversations in a SQL database:
Everytime I click send, I insert the message in the database?
Create a List for each user and everytime I click send, the message is saved on the list of the user who sent the message. Then if a user disconnects, I'm going to iterate the list of messages and for each message insert all of them in the db.
Are there other solutions?
What I'm doing now is the following. I have this method which is located on my Hub class:
public void saveMessagetoDB(string userName, string message)
{
var ctx = new TestEntities1();
var msg = new tbl_Conversation {Msg = message};
ctx.tbl_Conversation.Add(msg);
ctx.SaveChanges();
}
I call this method saveMessagetoDB on my client side HTML file like this:
$('#btnSendMessage').click(function () {
var msg = $("#txtMessage").val();
if (msg.length > 0) {
var userName = $('#hUserName').val();
// <<<<<-- ***** Return to Server [ SaveMessagetoDB ] *****
objHub.server.saveMessagetoDB(userName, msg);
SignalR is great for a chat application and you wouldn't even need to store anything in SQL unless you want to create a transcript of the chat at a later time (which may not even be necessary).
I suggest getting the chat working with SignalR first (don't do anything with sql). Then once that is working you can put SQL logging as necessary in your signalR hub.
It most likely makes the most sense to write to sql on each message.
If you decide to store the chats in a database, then you will need to insert/update the messages as they happen.
If you are using the PersistentConnection then you can hook into the OnReceivedAsync event and insert / update data from that event:
protected override Task OnConnectedAsync(IRequest request, string connectionId)
{
_clients.Add(connectionId, string.Empty);
ChatData chatData = new ChatData("Server", "A new user has joined the room.");
return Connection.Broadcast(chatData);
}
Or in the SignalR class that inherits from Hub, you can persist to the Db right before you have notified any clients.
I'm using Channel API (Java) with Google App Engine for my web application. I have implemented a Token-reusing-mechanism for not exceeding the Channel API Quotas that fast.
This means, that my implementation reuses an existing channel for a user that refreshes the page as long as the expiration time of the token received by the ChannelService.createChannel() call, is not over.
When refreshing my page I get the following exception (with x starting at 0 and increasing for every refresh). However, my page continues to work as intended. Is there a way to avoid the exception being thrown? Or can I just ignore the exception?
com.google.appengine.api.channel.dev.LocalChannelFailureException: Client connection with ID connection-x not found.
at com.google.appengine.api.channel.dev.Channel.getClientMessageQueue(Channel.java:79)
at com.google.appengine.api.channel.dev.ChannelManager.getNextClientMessage(ChannelManager.java:300)
at com.google.appengine.api.channel.dev.LocalChannelServlet.doGet(LocalChannelServlet.java:120)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
...
Im reusing tokens with the following classes:
When calling ChannelService.createChannel() I save the expiration date and the generated token in an Entity called "Channel"
public class Channel {
private String id;
private String token;
private Date expiration;
}
Then I have a ChannelService class that returns a valid Channel with its get() method. The channelDAO is a class that just uses a Map for storing Channels. So there is no database persistence, which would keep a token alive over a server restart.
public Channel get(String clientId) {
Calendar calendar = Calendar.getInstance();
Channel channel = channelDAO.get(clientId);
if (channel == null || calendar.getTime().after(channel.getExpiration())) {
com.google.appengine.api.channel.ChannelService channelService = ChannelServiceFactory
.getChannelService();
calendar.add(Calendar.MINUTE, CHANNEL_UPTIME);
String token = channelService.createChannel(player.toString(), CHANNEL_UPTIME);
channel = new Channel(clientId, token, calendar.getTime());
channelDAO.persist(channel);
}
return channel;
}
I fixed the problem by further investigations on the source of the exception. The Channel API works with polling requests that are executed every 500ms. I used Firefox's console to track these. Here is an example poll:
[20:40:15.978] GET http://localhost:8080/_ah/channel/dev?command=poll&channel=920a60f9b27ece1a1ba43d251fdacf2e-channel-eqt3xi-1385927324758-{clientId}&client=connection-2 [HTTP/1.1 200 OK 0ms]
In my question I stated, that the exception occurs on page reload, so the problem with this was: When the page is reloaded, something (I don't know what exactly, but i assume it has something to do with sockets getting closed and reopened on page refresh) happens which causes the client (last parameter of the GET request) to no longer be available. However, a new client is available: the client "connection-{i+1}". So when you enter the page initially, the client is "connection-0". After page refresh it is "connection-1". But as the old page used a delayed execution for the poll, a false request (still connection-0) is sent to the server, that, as a result, throws the Exception.
I fixed the problem by manually cancelling the delayed execution, when leaving the page with jQuery.
var channel = new goog.appengine.Channel('${channel.token}');
var socket = channel.open(handler);
$(window).on('beforeunload', function() {
clearTimeout(socket.pollingTimer_);
});
Your token re-use scheme should be carefully checked for bugs as that exception shouldn't occur each page reload.
There is a known issue after local server restarts but as stated it should only be only if the development server restarted.
I had the same issue using GWT and gwt-gae-channel. The solution would be something like:
Socket socket = channel.open(new SocketListener() {...});
Window.addWindowClosingHandler(new ClosingHandler() {
#Override
public void onWindowClosing(ClosingEvent event) {
socket.close();
}
});
I have silver light application.in the client side i have list of Guid using that Guid i can retrieve all data of customer when i am selecting 26 clients it works fine but when i tried to accessed data more then 26 clients it gives me error like
Load Opration failed for query GetAtclientsERCWithAllInformation the remote server not found
my client side look like
var query = formscontext.GetATClientsERCWithAllInformationQuery(guid);
try
{
LoadOperation<ATClient> _loadReturnTypeOperation = formscontext.Load(query);
_loadReturnTypeOperation.Completed += (s, e) =>
{
some code goes here
}
my server side look like
[Query(HasSideEffects=true)]
public IQueryable<ATClient> GetATClientsERCWithAllInformation(List<Guid> clientsGuids)
{
return this.ObjectContext.ATClients.Include("ClientEfileInfo").Include("ATClientImages").Include("ATPreparer").Include("ATPreparer.ATFirm").Include("ATClientReturns")
.Include("ATClientReturns.ATForms").Include("ATClientReturns.ATForms.FormsMaster").Where(p => clientsGuids.Contains(p.ClientGUID));
}
It seems that EF can't handle the query you requested. The error that silverlight is reporting is useless, you should look into the http response (remember that from an http perspective you're just doing a POST).
Override the
protected override void OnError(DomainServiceErrorInfo errorInfo)
on your domainservice class in order to get the real error. Another quick way to get the error is to setup an http proxy and inspect the response (you can use fiddler + wcf binary inspector from msdn)
UPDATE: The plot thickens. I changed my channel name and it is suddenly working (which means it wasn't a problem with my push service, since I'm getting the same HTTP response from the Microsoft Push Notification server).
To me, however, this is not a solution. How will I be able to test this and KNOW my users are getting their push notifications if I'm getting the same response when it's not working as I do when it is?
[ORIGINAL POST]
I've been trying to get push notifications sent to my Windows Phone 7 device, but I'm having very big problems that I can't find any answers for. I'll start with the c# code.
I set up push notifications using the following C# code.
private HttpNotificationChannel channel;
private static string PUSH_CHANNEL = "MySpecialPushChannel";
private Uri PushUri = null;
private bool IsPushRegistered = false;
public void StartPushSubscription()
{
try
{
channel = HttpNotificationChannel.Find(PUSH_CHANNEL);
}
catch
{}
if (channel != null)
{
PushUri = channel.ChannelUri;
if (!channel.IsShellTileBound)
channel.BindToShellTile();
}
else
{
channel = new HttpNotificationChannel(PUSH_CHANNEL);
channel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(channel_ChannelUriUpdated);
channel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(channel_HttpNotificationReceived);
channel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(channel_ErrorOccurred);
try
{
channel.Open();
channel.BindToShellTile();
}
catch (Exception err)
{
channel = null;
IsPushRegistered = false;
// Code to try again
}
}
}
void channel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
PushUri = e.ChannelUri;
IsPushRegistered = true;
}
I'm following the standard WP7 push structure:
Find the HttpNotificationChannel (or start a new one)
Register event handler to get the push notification uri back
Open the channel
Bind to the tile
Handle the channel Uri (which we send to our service to await the happy day when we send the push notification
OK... so far so good. No errors, I get my Uri, send it to my service just fine. I pin my app to the start screen and my service sends a push request to the Uri (sending just the count so that I get a little push count number in the upper right hand corner). I get back an HTTP 200 status with the following:
DeviceConnectionStatus => Connected
NotificationStatus => Received
SubscriptionStatus => Active
And then... nothing. No push status shows up on my app. I've now tried it on my device, in the emulator, on another device, and with multiple servers and the result is always the same. Everything looks like it is working except for the fact that it doesn't work.
To me, however, this is not a solution. How will I be able to test this and KNOW my users are getting their push notifications if I'm getting the same response when it's not working as I do when it is?
The answer is, you can't. It's a limitation of how WP7 handles notifications.
For structured notifications like Tile and Toast, if you get the Connected/Active/Received/200 response, then you can know that MPNS accepted your notification request. However, this does not mean that you have sent a valid XML payload.
The component that handles parsing XML is the Push Client, the process running on the phone that accepts push notifications and deals them out to appropriate applications, displays the toast, etc.
If you have sent invalid XML, there is absolutely no indication that you've done so. At most, if you try to send the notification again to that same push channel URI, you'll get a 404 in response. Apparently getting an invalid XML payload for a specific application makes that application's push channel close, requiring you to go through the whole procedure again.
I've discovered this while debugging with our server team, and through trying to get the phone to display an alternate live tile. The only advice I can offer you is to quadruple-check your XML.
You will get errors in your error event handler for your push notification channel for Toast notifications that have invalid XML, since you are able to send/receive toast notifications while the application is active.
If anyone from Microsoft is reading this, PLEASE provide more thorough documentation on possible error states in the push notification system. We also need an event handler for Tile notifications, or at least allow us to receive tile notifications while the app is in the foreground and fire the notification channel error event so that we can be aware that our XML payload is invalid.
Especially if your web service isn't built with WCF, .NET, Azure, and whatever, working with Push Notifications on WP7 is like wandering blind.
Documentation for an exception message reading "InvalidOperationException(Failed to open channel)" should not read: "This exception is raised when the notification channel failed to open. Try opening the notification channel again." (reference)
are you getting the URL from each device? you need to get a URL from the push notification sevice for each device everytime your device connects,
when it does you need to find a way of retrieving the url from each client,
once you do that and your still not receiving push notifications then I would write to microsoft to see if they can see anything to do with the push notifications
I'm writing a WPF NHibernate Desktop App using Session Per Presenter. I have a list view showing all the saved SalesOrders and an Edit Sales Order form when you double click on a Sales Order.
Each of these forms has a Session Object which lasts for the lifetime of the form. When a SalesOrder is saved it publishes an Event which tells the list view to re-load. The EditForm is definitely saving to the database and the ListView is definitely selecting from the database. However, the session that belongs to the ListViewPresenter is not updating its entities with those retrieved from the database. It just returns the same values as when the listSession was first loaded before anything was saved.
Below is some code which best replicates the scenario:-
[Test]
public void SessionPerPresenter()
{
//This session is the one that is used to load all salesorders from the database. It's lifetime is the lifetime of the form but as you double click on an entry in the list to edit it will stay alive longer than the session in the edit form
ISession listSession = NHibernateHelper.OpenSession();
SalesOrder order = new SalesOrder("P123435", "ACME");
order.AddLine(new SalesOrderLine("Beans", 15));
order.AddLine(new SalesOrderLine("Coke", 24));
order.AddLine(new SalesOrderLine("Pepsi", 3));
order.AddLine(new SalesOrderLine("Apples", 4));
//this session is the equivalent of the one in the Edit Form as soon as the entity is Saved
//the session is disposed
using (ISession session = NHibernateHelper.OpenSession())
{
session.SaveOrUpdate(order);
ID = order.SalesOrderID;
}
//retrieve all SalesOrders from the database and store them in a list
IList<SalesOrder> salesOrders = listSession.CreateCriteria<SalesOrder>().List<SalesOrder>();
foreach (SalesOrder so in salesOrders)
{
Console.WriteLine(so.ToString());
}
//edit the selected order and update its order code value and resave
using (ISession session = NHibernateHelper.OpenSession())
{
hydratedSalesOrder = session.Get<SalesOrder>(ID);
hydratedSalesOrder.OrderCode = "1234-5678";
session.SaveOrUpdate(hydratedSalesOrder);
session.Flush();
}
//re-retrieve the list of orders from the database. Using SQLServer Profiler / NHibernate profiler
//you can see the query being sent to the database so I don't believe it is in the cache. Indeed, if you run
//the query directly against the database the value 1234-5678 is returned. Can't work out why
//the listSession does not have the values read from the database in it but has the values from the
//original list retrieval.
salesOrders = listSession.CreateCriteria<SalesOrder>().List<SalesOrder>();
foreach (SalesOrder so in salesOrders)
{
Console.WriteLine(so.ToString());
}
listSession.Close()
}
Can someone help me with what is going on here? What am I doing wrong? Am I missing something vital? If it didn't query the database I would think it was something to do with the first level cache but that seems unlikely.
On way to ensure that your entities are not cached is to clear the session with ISession.Clear(). Also you can evict individual entities by calling ISession.Evict(object entity).
If you not sure of what is happening in your application, consider a profiling tool such as nhprof.
Quick note: using a session for the lifetime of a dialog can be handy in small applications with no concurrency problems, but you will get in trouble on the long run. A session should be opened late, and closed early.