I have a simple SignalR proxy with a single client method on it. The javascript looks like the following:
var proxy = $.connection.orderStatusUpdateEmitter;
proxy.client.onOrderUpdated = function(order){
try {
//This is only hit sometimes
getCustomerOrders(userId)
} catch (e) {
}
}
proxy.connection.start().done(function(c){
$log.info('Connected to order status update emitter');
});
proxy.connection.disconnected = function(data){
$log.info('disconnected');
setTimeout(function() {
proxy.connection.start();
},20000);
}
It seems like there is some type of race condition or I am doing this incorrectly because the handler for onOrderUpdated is not consistently hit. Anything glaringly wrong with the implementation above?
There is nothing special happening in my hub on the server, it looks like the following:
[AuthorizeClaims]
public class OrderStatusUpdateEmitter : Hub
{
private static string _groupIdentifier = "OrderStatusUpdate";
public override Task OnConnected()
{
var identity = Context.Request.Environment["user.identity"] as AuthenticatedUserIdentity;
Groups.Add(Context.ConnectionId, string.Format("{0}-{1}", _groupIdentifier, identity.UserId));
return base.OnConnected();
}
public override Task OnReconnected()
{
var identity = Context.Request.Environment["user.identity"] as AuthenticatedUserIdentity;
Groups.Add(Context.ConnectionId, string.Format("{0}-{1}", _groupIdentifier, identity.UserId));
return base.OnReconnected();
}
public static string GetGroupIdentifier()
{
return _groupIdentifier;
}
public Order OnOrderUpdate(Order order)
{
Clients.Group(String.Format("{0}-{1}",GetGroupIdentifier(),
order.CustomerId)).onOrderUpdated(obj);
}
}
Try the following instead of Group
list is consisting of users with ',' separator.
string[] group = list.Split(',');
for(int i=0; i < group.length; i++)
Clients.User(group[i]).onOrderUpdated(obj);
Related
I'm trying to call a method from my c# hub class but when I try to return a string from the javascript code I get a System.Threading.Tasks.Task instead of a string. I'm wondering how I can edit the javascript method to return an actual string.
Here's the hub class:
public String getMessage(int id)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
var s = hubContext.Clients.All.getMessage(id);
return s;
}
JavaScript:
chat.client.getMessage = function (id) {
for (i = 0; i < messageArr.length; i++) {
if (messageArr[i].ID == id) {
var s = messageArr[i].ID + messageArr[i].Name + messageArr[i].Content;
window.alert(s);
return s;
}
}
}
SignalR does not support return values from clients to hubs. The task returned by Clients.All will complete when all clients have been called.
If you want return values from clients, you will have to manually call the hub with the correct value and match the caller using your own message id.
See the documentation
You can't get a return value from a client method; syntax such as int
x = Clients.All.add(1,1) does not work.
I am new to Parse and Cloud Code, but I have managed to write a few AfterSave Cloud Code functions that work fine. However, I am having a lot of trouble with this one, and I cannot figure out why. Please help...
I have
Two PFObject classes: Message and MessageThread
Message contains chat messages that are associated with a MessageThread
MessageThread contains an array of members (which are all PFUsers)
Upon insert to Message, I want to look up all the members of the related MessageThread and Push notifications to them
class MessageThread: PFObject {
#NSManaged var members: [PFUser]
#NSManaged var lastMessageDate: NSDate?
#NSManaged var messageCount: NSNumber?
override class func query() -> PFQuery? {
let query = PFQuery(className: MessageThread.parseClassName())
query.includeKey("members")
return query
}
init(members: [PFUser], lastMessageDate: NSDate?, messageCount: NSNumber?) {
super.init()
self.members = members
self.lastMessageDate = lastMessageDate
self.messageCount = messageCount
}
override init() {
super.init()
}
}
extension MessageThread: PFSubclassing {
class func parseClassName() -> String {
return "MessageThread"
}
override class func initialize() {
var onceToken: dispatch_once_t = 0
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
}
class Message: PFObject {
#NSManaged var messageThreadParent: MessageThread
#NSManaged var from: PFUser
#NSManaged var message: String
#NSManaged var image: PFFile?
override class func query() -> PFQuery? {
let query = PFQuery(className: Message.parseClassName())
query.includeKey("messageThreadParent")
return query
}
init( messageThreadParent: MessageThread, from: PFUser, message: String, image: PFFile?) {
super.init()
self.messageThreadParent = messageThreadParent
self.from = from
self.message = message
self.image = image
}
override init() {
super.init()
}
}
extension Message: PFSubclassing {
class func parseClassName() -> String {
return "Message"
}
override class func initialize() {
var onceToken: dispatch_once_t = 0
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
}
Approach
From the request object (a Message), get its messageThreadParent
Lookup the members of the parent MessageThread, loop through them, etc.
The problem
When I try to retrieve the MessageThread object, I attempt to query on Id == threadParent.objectId. However, this query always returns all 8 of my current MessageThreads, rather than the single one I need.
Parse.Cloud.afterSave(Parse.Object.extend("Message"), function(request) {
Parse.Cloud.useMasterKey();
var theMsg = request.object;
var threadParent;
var currUsername = request.user.get("username");
var threadUsers;
var usernameArray;
threadParent = request.object.get("messageThreadParent");
// promise
queryM = new Parse.Query(Parse.Object.extend("MessageThread"));
queryM.include("members");
queryM.equalTo("id", threadParent.objectId);
queryM.find().then(function (threadParam) {
console.log(" threads: ");
console.log(threadParam.length); //this returns 8, which is the number of threads I have. I would expect just 1, matching threadParent.objectId...
console.log("thread is: ");
//... additional code follows, which seems to work...
After grappling with a separate problem all day I finally figured out that in Parse's Javascript SDK there is a difference between "id" and "objectId".
Changing this
queryM.equalTo("id", threadParent.objectId); // doesn't work
to
queryM.equalTo("objectId", threadParent.id); // works!
fixed my problem.
I have a problem when using SQLDependency and SignalR Hub. When a connection with the hub is started, the SQLDependency's OnChange event is always firing even though there are no changes in the database.
Here is my code that contains SQLDependency
public List<NotifCenterModel> countNewTransaksi()
{
List<NotifCenterModel> ncms = new List<NotifCenterModel>();
command = new SqlCommand(#"SELECT Edolpuz_DB.dbo.TABEL_NOTIF_CENTER.NAMA_TABEL,Edolpuz_DB.dbo.TABEL_NOTIF_CENTER.JUMLAH_NOTIF FROM Edolpuz_DB.dbo.TABEL_NOTIF_CENTER",connect);
try
{
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if(connect.State == ConnectionState.Open)
connect.Close();
connect.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
ncms.Add(new NotifCenterModel(reader[0].ToString(), int.Parse(reader[1].ToString())));
}
return ncms;
}
catch { return null; }
finally { connect.Close(); }
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
TransHub.Show();
}
and in my Hub the code is like this
public class TransHub : Hub
{
public static void Show()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<TransHub>();
context.Clients.All.displayStatus();
}
}
and here is my javascript
$(function () {
// Proxy created on the fly
var job = $.connection.transHub;
// Declare a function on the job hub so the server can invoke it
job.client.displayStatus = function () {
// alert("test");
getData();
};
// Start the connection
$.connection.hub.start().done(function () {
getData();
}).fail(function (e) {
alert(e);
});
});
function getData() {
$.ajax({
url: server + '/Admin/GetNotifikasi/',
type: 'GET',
dataType: 'json',
success: function (data) {
for (var i = 0; i < data.length ; i++)
{
if (data[i].nama_tabel == "TABEL_TRANSAKSI")
{
$('#notifTrans').text(data[i].jumlah_notif);
}
else if (data[i].nama_tabel == "TABEL_KONF_BAYAR")
{
$('#notifBayar').text(data[i].jumlah_notif);
}
else if (data[i].nama_tabel == "TABEL_TESTI")
{
$('#notifTesti').text(data[i].jumlah_notif);
}
else if (data[i].nama_tabel == "TABEL_KUSTOM_ORDER")
{
$('#notifKustom').text(data[i].jumlah_notif);
}
}
}
});
}
When in connection.hub.start().done I call getData(), it will constantly fire and produce an infinite loop, but when I don't call getData() it doesn't fire the event when data in table changes. How can this be fixed?
In dependency_OnChange, you need to check e.Type. If it's != SqlNotificationType.Change, then the handler was called for some reason other than a data change. The subscription itself likely failed.
According to this site (https://learn.microsoft.com/en-us/previous-versions/aewzkxxh(v=vs.90)), the table name in query must be two-part name, like [dbo].[TABEL_NOTIF_CENTER].
The projected columns in the SELECT statement must be explicitly stated, and table names must be qualified with two-part names. Notice that this means that all tables referenced in the statement must be in the same database.
[]'s
i have two Models, first:
class Tutorial extends Eloquent {
protected $table = 'tutorials';
public function rating()
{
return $this->hasMany('Rating');
}
}
and:
class Rating extends Eloquent {
protected $table = 'ratings';
public $timestamps = false;
public function tutorial()
{
return $this->belongsTo('Tutorial');
}
}
now in my controller i have this:
public function get_index() {
$tutorials = tutorial::orderBy('created_at', 'desc')
->with('rating')
->paginate(25);
return View::make('site/home/index')->with('tutorials', $tutorials);
}
So how do i get all ratings from one tutorial in my View?!
EDIT:
Now i have this:
public function ratings()
{
return $this->hasMany('Rating');
}
public function getRating()
{
// Grab the ratings from this tutorial
$ratings = $this->ratings;
$summedRatings = 0;
// Loop through them all and add them together
foreach($ratings as $rating)
{
console.log($rating->value);
$summedRatings += $rating->value;
}
// Return the calculated average
return $summedRatings / count($ratings);
}
public function get_index() {
$tutorials = Tutorial::with('ratings')
->with('user')
->orderBy('created_at', 'desc')
->paginate(25);
return View::make('site/home/index')->with('tutorials', $tutorials);
}
and in my View:
#foreach($tutorials as $tutorial)
<span>{{$tutorial->rating}}</span>
#endforeach
But all my < span >´s are empty!
UPDATE: if i do this:
#foreach($tutorials as $tutorial)
#foreach($tutorial->ratings as $rate)
<span>{{$rate->value}}</span>
#endforeach
everything is good....So what´s wrong?
Depending on the platform you're site is on you should always use the correct case.
$tutorials = tutorial::orderBy(...) // Wrong
$tutorials = Tutorial::orderBy(...) // Correct
To eager load the ratings you should always declare your 'with' method before anything else.
$tutorials = Tutorial::with('rating')
->orderBy('created_at', 'DESC')
->paginate(25);
This has, for some reason, been left out of the L4 docs.
In your view you can now access the rating with this
foreach($tutorials as $tutorial)
{
echo $tutorial->rating->{rating table column name};
}
First, as far as naming conventions go, to make things easier to understand: The rating() method within your tutorial method should be called ratings(), so when you grab your ratings, it will look better ($tutorial->ratings)
After renaming this, in your view, while looping through the array of $tutorials, you could access the ratings of each one like this:
foreach($tutorials as $tutorial)
{
$ratings = $tutorial->ratings;
}
Which would retrieve the ratings object of each.
What you should know is that you can create properties for your model if you need to return the calculation of the ratings, instead of the ORM objects
For example, if each rating is a number from 1-5 in the ratings table stored in an amount column, you can do this to set the average of each rating as a property:
class Tutorial extends Eloquent {
protected $table = 'tutorials';
public function ratings()
{
return $this->hasMany('Rating');
}
public function getRating()
{
// Grab the ratings from this tutorial
$ratings = $this->ratings;
$summedRatings = 0;
// Loop through them all and add them together
foreach($ratings as $rating)
{
$summedRatings += $rating->amount;
}
// Return the calculated average
return $summedRatings / count($ratings);
}
}
Then in your view, you can echo out the property as if it were part of the database
foreach($tutorials as $tutorial)
{
echo $tutorial->rating;
}
In my experience I call simple web methods, like this:
[WebMethod]
public List<string> GetUserListByLetters(string strLetters){ ... }
And here is my OnComplete JS-function:
function OnComplete(args) {
...
if (args != "") {
for (var i = 0; i < args.length; i++) {
// Do what I need with string in args[i]
}
}
...
}
For now I would like to have such method:
[WebMethod]
public string GetUserListByCountry(int countryId, out List<User> users)
{
users=null;
if ( Validate(countryId)==false )
return "wrong country Id";
users = GetUsers(countryId); // returns list of User objects.
return "";
}
Question1: should "out" parameter work in WS? I saw few article (, for example) where said it is impossible.
Question2: if it doesn't work, how should I change method signature to get that workable?
Question3: if it works, how could I access data from 'out' parameter?
Thanks.
I will use this approach:
[WebMethod]
public object GetUserListByCountry(int countryId)
{
users=null;
if ( Validate(countryId)==false )
return "wrong country Id";
users = GetUsers(countryId); // returns list of User objects.
return new {Error="", Users=users};
}
I mean that I will return complex object, its one property will contain usual value for return and another - required data.