MVVM Light Messeging issue - c#

Im using MVVM Light Messenger in my WPF application, and something is not working as expected.
my view-model registered with a token. im using 'long' objects as tokens.
my code registered for example with the token 5, then sends request to a service.
when the service replies it handles in my second view-model, which will then send the message with the same token.
When i debug and print the registration and sending of the messages it seems OK, but for some reason not all the messenger are received by the registered.
My registration and handling looks as follows:
private void registerTest()
{
long tokenId = getNextToken();
ExtraData data = new ExtraData();
Messenger.Default.Register<MyMsg>(this, tokenId, (m) => recieve(m,data));
}
private void receive(MyMsg m,ExtraData data)
{
Messenger.Default.Unregister<MyMsg>(this, m.tokenId);
}
My sending looks as follows:
private void sendTest(long tokenId)
{
Messenger.Default.Send(new MyMsg(tokenId), tokenId);
}
I always register with token X before its received in my sendTest,
but for some reason, sometimes when sendTest(X) is called, its not received.
Anyone has any idea whats going on?

You should have your ExtraData as a class property on your message to be able to interact with it from different sources.
public class MyMsg {
public int TokenId {get;set;}
public ExtraData Data {get;set;}
}
public void registerTest()
{
Messenger.Default.Register<MyMsg>(this, recieve);
}
public void recieve(MyMsg myMsg)
{
Messenger.Default.Unregister<MyMsg>(this, myMsg.TokenId);
//Note that you can also access m.Data here if you need to
}
public void sendTest()
{
var myMsg = new MyMsg {TokenId = GetNextToken(), Data = new ExtraData()};
Messenger.Default.Send(myMsg);
}

Related

Google Play Developer Notification

I'm having difficulty setting up an end-point to receive Google Play Developer Notifications via Pub/Sub in a c# controller. I've set everything up against the app to publish to a topic and I have successfully setup a subscription in Google Pub/Sub...and the test notification is coming through on Google Pub/Sub...the problem is I am having trouble receiving the push notification on my server side c# controller end-point...I'm not sure how to set that up and if I have the correct json signature. I can't find any examples or documentation around this. Any help would be appreciated!
This is my first "test" of Pub/Sub and this sample worked for me.
See all samples here: https://github.com/GoogleCloudPlatform/dotnet-docs-samples/tree/main/pubsub/api/Pubsub.Samples
These steps needs to be done:
Create a topic here: https://console.cloud.google.com/cloudpubsub/topic/ , in the example we call it "iap"
Under permission for "iap", add google-play-developer-notifications#system.gserviceaccount.com as Pub/Sub publisher. This will allow Google Play to publish on this topic.
Under subscriptions https://console.cloud.google.com/cloudpubsub/subscription add your service account/personal gmail or what ever that is linked to your c# server later on. I tested firebase-adminsdk#yourproject.iam.gserviceaccount.com and it worked fine. Check your environment variable "GOOGLE_APPLICATION_CREDENTIALS" and extract this user as Pub/Sub subscriber in permissions for "iap-sub".
Play store needs to be configured under "Monetization setup". String are for instance: projects/yourproject/topics/iap
Press a test message (you can also see it in the Cloud console)
Test message could look something like this:
20:16:07: Received message 6108129433484522 20:16:07:
{"version":"1.0","packageName":"com.yourproject","eventTimeMillis":"1666642564858","testNotification":{"version":"1.0"}}
Class below runs the client in the background without waiting.
If you just wanna try in a console, use the Console.ReadLine()
public class GCloudPubSub : IDisposable
{
public String projectId { get; set; }
public String subscriptionId { get; set; }
private SubscriberClient _client;
public FirebasePubSub() {
projectId = "yourproject";
subscriptionId = "iap-sub";
}
public async void Start()
{
SubscriptionName subscriptionName = SubscriptionName.FromProjectSubscription(projectId, subscriptionId);
_client = await SubscriberClient.CreateAsync(subscriptionName);
await _client.StartAsync(HandleMessage);
}
public async void Stop()
{
await _client.StopAsync(CancellationToken.None);
}
public void Dispose()
{
Stop();
}
static Task<SubscriberClient.Reply> HandleMessage(PubsubMessage message, CancellationToken token)
{
Log($"Received message {message.MessageId}");
string text = System.Text.Encoding.UTF8.GetString(message.Data.ToArray());
Log(text);
return Task.FromResult(SubscriberClient.Reply.Ack);
}
static void Log(string text) => Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss}: {text}");
}
Hopefully this will lead you on the right way :)

SignalR Core how to send message to clients every n seconds

AKA "Send messages to clients from a background service"
I would like my SignalR server to update a dashboard every n seconds. I'm using this right now:
public class MyHub : Hub
{
public override Task OnConnectedAsync()
{
Observable.Interval(TimeSpan.FromSeconds(0.5)).Subscribe(l =>
{
var alt = CalcAltitude(l);
SendMessage(alt);
});
return Task.CompletedTask;
}
private void SendMessage(double alt)
{
Clients.All.SendAsync("SendAction", new Status() {Altitude = alt});
}
private double CalcAltitude(long l)
{
return 100 * Math.Sin((double) l / 100) + 200;
}
}
public class Status
{
public double Altitude { get; set; }
}
When my code is executed, it throws an exception saying that
cannot access a disposed object
I suspect I'm doing something wrong here.
So, what's the correct way to make send messages to all the clients on a timely manner?
OK, this time the answer didn't come from Stack Overflow, but from another source.
This can be done with this code in ASP.NET Core + SignalR Core.
You'll need 2 parts.
the Background Service itself (updates the clients): https://github.com/davidfowl/UT3/blob/fb12e182d42d2a5a902c1979ea0e91b66fe60607/UTT/Scavenger.cs
the Setup (wiring) part: https://github.com/davidfowl/UT3/blob/fb12e182d42d2a5a902c1979ea0e91b66fe60607/UTT/Startup.cs#L46
Big thanks to David Fowler for the reply!
var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
hubContext.Clients.All.SendnewData(Data);
Put this code where your data is updated or calculated in every second.
Like: Your DB process is done then Broadcast new data
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Infrastructure;
using Microsoft.AspNet.Mvc;
public class TestController : Controller
{
private IHubContext testHub;
public TestController(IConnectionManager connectionManager)
{
testHub = connectionManager.GetHubContext<TestHub>();
testHub.Clients.All.SendnewData(Data);
}
}

SignalR registering a user to a group outside of hub?

Maybe I am just not understanding Groups in SignalR correctly, but I am confused on the registering a user to a group part. I am using version 1.2.2.
Here: Groups demonstrates how to use groups in older SignalR
I am using a singleton to maintain the context of the hub. I am also using asp.net mvc 4. Basically, I want to do something with a menu item (make it flash, add a count of new tasks, etc..) during an update, but only to the users that are assigned tasks within that option.
So I figured, server side when checking the user's roles, I can conditionally assign them to the SignalR Group for broadcasts.
Here is my hub, and singleton classes:
public class TransactHub : Hub
{
public Task RegisterForTransactionPartUpdates()
{
return Groups.Add(Context.ConnectionId, "Transact");
}
public void UpdateDailyTransactionTable(string r)
{
Clients.All.broadcastUpdate(r);
}
}
And Singleton:
public class TransactSingleton
{
private readonly static Lazy<TransactSingleton> _instance = new Lazy<TransactSingleton>(() => new TransactSingleton(GlobalHost.ConnectionManager.GetHubContext<TransactHub>().Clients));
private TransactSingleton(IHubConnectionContext clients)
{
Clients = clients;
}
private IHubConnectionContext Clients
{
get;
set;
}
public static Transact Instance
{
get
{
return _instance.Value;
}
}
public void RegisterForTransactionUpdates()
{
//I want to register user here..
}
public void BroadcastUpdate(List<string> orders)
{
}
}
So where would I actually register the user? Also, upon a new connection using:
$.connection.hub.disconnected(function () {
setTimeout(function () {
$.connection.hub.start();
}, 5000); // Restart connection after 5 seconds.
});
Will the user stay registered in the group?
Hmm, so it appears from this article: Hubs
public class ContosoChatHub : Hub
{
public override Task OnConnected()
{
// Add your own code here.
// For example: in a chat application, record the association between
// the current connection ID and user name, and mark the user as online.
// After the code in this method completes, the client is informed that
// the connection is established; for example, in a JavaScript client,
// the start().done callback is executed.
return base.OnConnected();
}
public override Task OnDisconnected()
{
// Add your own code here.
// For example: in a chat application, mark the user as offline,
// delete the association between the current connection id and user name.
return base.OnDisconnected();
}
public override Task OnReconnected()
{
// Add your own code here.
// For example: in a chat application, you might have marked the
// user as offline after a period of inactivity; in that case
// mark the user as online again.
return base.OnReconnected();
}
}
I should be using the OnConnected, and make a database call to handle the conditional assigning. I was hoping I would be able to handle the database call outside of the hub, but doesn't look possible. Correct me if I am wrong.

Signalr connects but doesn't push a message

I've been working on implementing signalr as part of a wcf service to talk to a .net client. Apart form a connection message all communication is one way passing a dynamic payload to the client side.
I've managed to set it up so that the client will connect to the service and pass a connection message but I can't get the pushing of a message from the service to the client.
Sorry if I've missed this answered else where but I've been unable to find a reason for this failing as it seems to follow the "how to's"
Any help would be much appreciated and thank you in advance
Server side:
WCF external call
public class MessageService : IMessageService
{
public string PushAlerts()
{
var payLoad = new PayLoad
{
MethodName = "alerts"
};
IHubContext connectionHub = GlobalHost.ConnectionManager.GetHubContext<PushConnection>();
connectionHub.Clients.All.Notify(payLoad);
}
}
My Hub
[HubName("PushHub")]
public class PushHub : Hub
{
public override Task OnConnected()
{
var connectionMessage = Context.QueryString["CONNECTION MESSAGE"];
if (connectionMessage != null)
{
Debug.WriteLine("connectionMessage");
}
return base.OnConnected();
}
}
ClientSide:
var querystringData = new Dictionary<string, string>{};
querystringData.Add("CONNECTION MESSAGE", "foo Connection");
var hubConnection = new HubConnection("http://localhost:60479/", querystringData); //Running local till working
hubConnection.TraceLevel = TraceLevels.All;
hubConnection.TraceWriter = Console.Out;
IHubProxy clientHubProxy = hubConnection.CreateHubProxy("PushHub");
clientHubProxy.On("Notify", payLoad =>
SynchronizationContext.Current.Post(delegate
{
ResponseMethod(payLoad);
}, null)
);
await hubConnection.Start();
I've missed out payload but that only holds a string value at present. I've also setup a pipemodule for logging perposes.
Thanks Again
Ok so I resolved this problem in two ways firstly I moved the call to the client inside the hub its self, which I then called from a method in my wcf service.
[HubName("PushHub")]
public class PushHub : Hub
{
IHubContext connectionHub = GlobalHost.ConnectionManager.GetHubContext<PushConnection>();
public void Send(Payload payload)
{
connectionHub.Clients.All.Notify(payLoad);
}
}
Secondly the client code for the method was all wrong. In the end this worked:
clientHubProxy.On("Notify", (payLoad) => { dostuff };
Took a lot of fiddling but hope my answer helps others.

Async WCF: wait for another call

We have an old Silverlight UserControl + WCF component in our framework and we would like to increase the reusability of this feature. The component should work with basic functionality by default, but we would like to extend it based on the current project (without modifying the original, so more of this control can appear in the full system with different functionality).
So we made a plan, where everything looks great, except one thing. Here is a short summary:
Silverlight UserControl can be extended and manipulated via ContentPresenter at the UI and ViewModel inheritance, events and messaging in the client logic.
Back-end business logic can be manipulated with module loading.
This gonna be okay I think. For example you can disable/remove fields from the UI with overriden ViewModel properties, and at the back-end you can avoid some action with custom modules.
The interesting part is when you add new fields via the ContentPresenter. Ok, you add new properties to the inherited ViewModel, then you can bind to them. You have the additional data. When you save base data, you know it's succeeded, then you can start saving your additional data (additional data can be anything, in a different table at back-end for example). Fine, we extended our UserControl and the back-end logic and the original userControl still doesn't know anything about our extension.
But we lost transaction. For example we can save base data, but additional data saving throws an exception, we have the updated base data but nothing in the additional table. We really doesn't want this possibility, so I came up with this idea:
One WCF call should wait for the other at the back-end, and if both arrived, we can begin cross thread communication between them, and of course, we can handle the base and the additional data in the same transaction, and the base component still doesn't know anything about the other (it just provide a feature to do something with it, but it doesn't know who gonna do it).
I made a very simplified proof of concept solution, this is the output:
1 send begins
Press return to send the second piece
2 send begins
2 send completed, returned: 1
1 send completed, returned: 2
Service
namespace MyService
{
[ServiceContract]
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service1
{
protected bool _sameArrived;
protected Piece _same;
[OperationContract]
public Piece SendPiece(Piece piece)
{
_sameArrived = false;
Mediator.Instance.WaitFor(piece, sameArrived);
while (!_sameArrived)
{
Thread.Sleep(100);
}
return _same;
}
protected void sameArrived(Piece piece)
{
_same = piece;
_sameArrived = true;
}
}
}
Piece (entity)
namespace MyService
{
[DataContract]
public class Piece
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string SameIdentifier { get; set; }
}
}
Mediator
namespace MyService
{
public sealed class Mediator
{
private static Mediator _instance;
private static object syncRoot = new Object();
private List<Tuple<Piece, Action<Piece>>> _waitsFor;
private Mediator()
{
_waitsFor = new List<Tuple<Piece, Action<Piece>>>();
}
public static Mediator Instance
{
get
{
if (_instance == null)
{
lock (syncRoot)
{
_instance = new Mediator();
}
}
return _instance;
}
}
public void WaitFor(Piece piece, Action<Piece> callback)
{
lock (_waitsFor)
{
var waiter = _waitsFor.Where(i => i.Item1.SameIdentifier == piece.SameIdentifier).FirstOrDefault();
if (waiter != null)
{
_waitsFor.Remove(waiter);
waiter.Item2(piece);
callback(waiter.Item1);
}
else
{
_waitsFor.Add(new Tuple<Piece, Action<Piece>>(piece, callback));
}
}
}
}
}
And the client side code
namespace MyClient
{
class Program
{
static void Main(string[] args)
{
Client c1 = new Client(new Piece()
{
ID = 1,
SameIdentifier = "customIdentifier"
});
Client c2 = new Client(new Piece()
{
ID = 2,
SameIdentifier = "customIdentifier"
});
c1.SendPiece();
Console.WriteLine("Press return to send the second piece");
Console.ReadLine();
c2.SendPiece();
Console.ReadLine();
}
}
class Client
{
protected Piece _piece;
protected Service1Client _service;
public Client(Piece piece)
{
_piece = piece;
_service = new Service1Client();
}
public void SendPiece()
{
Console.WriteLine("{0} send begins", _piece.ID);
_service.BeginSendPiece(_piece, new AsyncCallback(sendPieceCallback), null);
}
protected void sendPieceCallback(IAsyncResult result)
{
Piece returnedPiece = _service.EndSendPiece(result);
Console.WriteLine("{0} send completed, returned: {1}", _piece.ID, returnedPiece.ID);
}
}
}
So is it a good idea to wait for another WCF call (which may or may not be invoked, so in a real example it would be more complex), and process them together with cross threading communication? Or not and I should look for another solution?
Thanks in advance,
negra
If you want to extend your application without changing any existing code, you can use MEF that is Microsoft Extensibility Framework.
For using MEF with silverlight see: http://development-guides.silverbaylabs.org/Video/Silverlight-MEF
I would not wait for 2 WCF calls from Silverlight, for the following reasons:
You are making your code more complex and less maintainable
You are storing business knowledge, that two services should be called together, in the client
I would call a single service that aggreagated the two services.
It doesn't feel like a great idea to me, to be honest. I think it would be neater if you could package up both "partial" requests in a single "full" request, and wait for that. Unfortunately I don't know the best way of doing that within WCF. It's possible that there's a generalized mechanism for this, but I don't know about it. Basically you'd need some loosely typed service layer where you could represent a generalized request and a generalized response, routing the requests appropriately in the server. You could then represent a collection of requests and responses easily.
That's the approach I'd look at, personally - but I don't know how neatly it will turn out in WCF.

Categories