I am developing an WPF application and tried to design something event driven using Prism's Event Aggregator.
Currently I am trying to implement something like event queue for Prism events.
To do that I want to subscribe related events and pass them to same method but Event Aggregator wants those methods to have same signature with the event.
Example events:
public class TestEvent1 : PubSubEvent<Class1>
{
}
public class TestEvent2 : PubSubEvent<Class2>
{
}
public class TestEvent3 : PubSubEvent<List<Class3>>
{
}
public class TestEvent3 : PubSubEvent<string>
{
}
Subscriptions:
_eventAggregator.GetEvent<TestEvent1>().Subscribe(OnTestEvent1, true);
_eventAggregator.GetEvent<TestEvent2>().Subscribe(OnTestEvent2, true);
_eventAggregator.GetEvent<TestEvent3>().Subscribe(OnTestEvent3, true);
Example callback method:
private void OnTestEvent1(Class1 object1)
{
// do something
}
Since I only receive data when event published, I tried something like this to use as event payload type, but it doesn't look right:
// Payload
public interface IMessage
{
public object Data { get; set; }
public Type Datatype { get; set; }
public PubSubEvent EventType { get; set; }
}
// Events
public class TestEvent1 : PubSubEvent<IMessage>
{
}
public class TestEvent2 : PubSubEvent<IMessage>
{
}
public class TestEvent3 : PubSubEvent<IMessage>
{
}
// Subscriptions
_eventAggregator.GetEvent<TestEvent1>().Subscribe(EventHandler, true);
_eventAggregator.GetEvent<TestEvent2>().Subscribe(EventHandler, true);
_eventAggregator.GetEvent<TestEvent3>().Subscribe(EventHandler, true);
// Callback
private void EventHandler(IMessage payload)
{
// do something
}
Is this viable and how can I improve or change my "generic" payload?
Extra information:
In my scenario I have multiple UDP servers that periodically receives new data, deserialize it to objects and publish events.
Related "managers" subscribed to those events and get triggered when new data received.
My goal is try to implement an event queue like system in my event receiving classes so it will be easier to deal with multithreading issues.
Here are some diagrams that may help me explain myself better:
My architecture:
My "event queue"
This is more of a code-review question, isn't it?
I would very much prefer strong typing, though, and I wouldn't want to build a queue around the event aggregator, that will always be tedious because you never know when new event types show up. Instead, I'd build my own event aggregator with queueing built in (starting from the existing one).
Also, I'd look into dataflow, for example, because the basic working mode of the event aggregator (fire and forget, one sender, multiple or no receivers) doesn't seem to work well with queueing. If you queue at the sender-side, to you wait for one receiver or all? Do you queue when there are no receivers or do you discard then? If you queue at the receiver-side, why bother at all? The receiver can implement the queue on its own.
When you publish an event with a reference to an object, consider these potential problems:
Any listener will be dependent on the type/assembly of the event parameter.
The referenced object will be held for some time. For how long? You don't know.
The referenced object could have been altered/disposed.
Listeners might handle the referenced object on a different thread.
After dealing with PubSubEvent<T> for several years now, I believe there is only one suitable pattern that successfully handles all cases. Publish your event with a unique identifier, e.g. a Guid.
public class MyItemAddedEvent : PubSubEvent<Guid> {}
Then inject a provider wherever you listen for this event:
public class SomeListener
{
private readonly IMyItemProvider myItemProvider;
[ImportingConstructor]
public SomeListener(IEventAggregator eventAggregator,
IMyItemProvider myItemProvider)
{
this.myItemProvider = myItemProvider;
eventAggregator.GetEvent<MyItemAddedEvent>().Subscribe(OnMyItemAdded, true);
}
private void OnMyItemAdded(Guid id)
{
var myItem = myItemProvider.Get(id);
// Do stuff
}
}
Now it is the responsibility of the provider class to deliver a valid and up-to-date object given a unique id.
Related
I am playing around with using Akka.NET in a new WPF .NET Framework application I am currently working on.
Mostly the process of using actors in your application seems pretty self explanitory, however when it comes to actually utilising the actor output at the application view level I have gotten a bit stuck.
Specifically there appear to be two options on how you might handle receiving and processing events in your actor.
Create an actor with publically exposed event handlers. So maybe something like this:
public class DoActionActor : ReceiveActor
{
public event EventHandler<EventArgs> MessageReceived;
private readonly ActorSelection _doActionRemoteActor;
public DoActionActor(ActorSelection doActionRemoteActor)
{
this._doActionRemoteActor = doActionRemoteActor ?? throw new ArgumentNullException("doActionRemoteActor must be provided.");
this.Receive<GetAllStuffRequest>(this.HandleGetAllStuffRequestReceived);
this.Receive<GetAllStuffResponse>(this.HandleGetAllStuffResponseReceived);
}
public static Props Props(ActorSystem actorSystem, string doActionRemoteActorPath)
{
ActorSelection doActionRemoteActor = actorSystem.ActorSelection(doActionRemoteActorPath);
return Akka.Actor.Props.Create(() => new DoActionActor(doActionRemoteActor));
}
private void HandleGetAllStuffResponseReceived(GetAllTablesResponse obj)
{
this.MessageReceived?.Invoke(this, new EventArgs());
}
private void HandleGetAllStuffRequestReceived(GetAllTablesRequest obj)
{
this._doActionRemoteActor.Tell(obj, this.Sender);
}
}
So basically you can then create your view and invoke any calls by doing something like this _doActionActor.Tell(new GetStuffRequest()); and then handle the output through the event handler. This works well but seems to break the 'Actors 'everywhere' model' that Akka.NET encourages and I am not sure about the concurrency implications from such an approach.
The alternative appears to be to actually make it such that my ViewModels are actors themselves. So basically I have something that looks like this.
public abstract class BaseViewModel : ReceiveActor, IViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
public abstract Props GetProps();
protected void RaisePropertyChanged(PropertyChangedEventArgs eventArgs)
{
this.PropertyChanged?.Invoke(this, eventArgs);
}
}
public class MainWindowViewModel : BaseViewModel
{
public MainWindowViewModel()
{
this.Receive<GetAllTablesResponse>(this.HandleGetAllTablesResponseReceived);
ActorManager.Instance.Table.Tell(new GetAllTablesRequest(1), this.Self);
}
public override Props GetProps()
{
return Akka.Actor.Props.Create(() => new MainWindowViewModel());
}
private void HandleGetAllTablesResponseReceived(GetAllTablesResponse obj)
{
}
}
This way I can handle actor events directly in actors themselves (which are actually my view models).
The problem I run into when trying to do this is correctly configuring my Ioc (Castle Windsor) to correctly build Akka.NET instances.
So I have some code to create the Akka.NET object that looks like this
Classes.FromThisAssembly()
.BasedOn<BaseViewModel>()
.Configure(config => config.UsingFactoryMethod((kernel, componentModel, context) =>
{
var props = Props.Create(context.RequestedType);
var result = ActorManager.Instance.System.ActorOf(props, context.RequestedType.Name);
return result;
}))
This works great at actually creating an instance of IActorRef BUT unfortunately I cannot cast the actor reference back to the actual object I need (in this case BaseViewModel).
So if I try to do this return (BaseViewModel)result; I get an invalid cast exception. Which obviously makes sense because I am getting an IActorRef object not a BaseViewModel.
So in conclusion I am hoping to get two questions answered.
What is the best way to deal with Akka.NET actors in MVVM applications, specifically when it comes to handling messages received and handling displaying the output.
Is there a way to correctly configure my Ioc system to both create an IActorRef instance and add it to the system BUT return an instance of the actual parent actor object concrete implementation of BaseViewModel?
Below is the current solution that I am using in the hope someone might propose something a bit better.
Basically I have abandoned my attempt at making my views actors and currently settled on using an interface to communicate between the ViewModel and Actor.
The current solution looks like this:
public class MainWindowViewModel : BaseViewModel, ITableResponseHandler
{
public void HandleResponse(IEnumerable<Entity> allEntities) { }
}
public interface ITableResponseHandler
{
void HandleResponse(IEnumerable<Entity> allEntities);
}
public class MyActor : ReceiveActor
{
public MyActor(ITableResponseHandler viewModel)
{
this.Receive<GetAllEntitiesResponse>(this.HandleGetAllEntitiesResponseReceived);
}
private void HandleGetAllEntitiesResponseReceived(GetAllTablesResponse obj)
{
this._ViewModel.HandleTablesResponse(obj.Result);
}
}
While I don't feel this is ideal it basically lets me avoid all the extra complexity of trying to make my view models themselves actors while sufficently decoupling the actor from the view.
I hope someone else has faced this problem and might be able to provide some insight at a better solution for handling Akka.NET output in a MVVM application.
I am working on a project with over 30 properties that are constantly being updated using data binding. The way i'm fetching the new values sent by the server is with event aggregator, which updates the properties. So far, here's how I got it working
Event Class
public class Zone1TempEvent : PubSubEvent<int>
{
}
Property
private int _zone1Temp;
public int Zone1Temp
{
get { return _zone1Temp; }
set { SetProperty(ref _zone1Temp, value); }
}
Subscribe
eventAggregator.GetEvent<Zone1TempEvent>().Subscribe(tempZone1Update);
Method
private void tempZone1Update(int value) { Zone1Temp = value; }
Publish
private void checkResponsability(ItemValueCallback itemValue)
{
switch ((string)itemValue.ClientHandle)
{
case "Zone1_Temp":
int Zone1Temp = Int32.Parse((string)itemValue.Value);
_eventAggregator.GetEvent<Zone1TempEvent>().Publish(Zone1Temp);
break;
}
}
However, I can't imagine doing this 30 times. I am looking for an alternative. I would like to avoid having to create a class for each event, as well as a method for each property. Is it possible to have one generic class called UpdatePropertyEvent, and use this to do so.
Or maybe do something inspired by this thread with enums?
Mixing enums with event classes in an event aggregator
Thanks
Instead of using the EventAggregator, your service can implement IPropertyChanged (and the models returned from the service can, depending on your scenario ). This way you have to react to just one event.
Also, you could just publish a single event that carries the two string values, like class ServerUpdatedEvent : PubSubEvent<PropertyValuePair> and do the parsing and distributing
to properties in the view model.
Example:
// ...service...
private void checkResponsability(ItemValueCallback itemValue)
{
_eventAggregator.GetEvent<ServerUpdatedEvent>().Publish(new PropertyValuePair((string)itemValue.ClientHandle,(string)itemValue.Value);
}
// ...view model or intermediate service...
private void OnServerUpdate(PropertyValuePair data)
{
switch (data.Property)
{
case "Zone1_Temp": Zone1Temp = int.Parse(data.Value); break;
}
}
If your properties can be named like the events or you put attributes on them, you can use reflection to find the property for an incoming event. Reflection is slow, though, so that if you have lots of events, you might need some type of caching.
Something like this could work for you, too.
I am building a list based on items/objets found. When these objects are found I want them to subscribe to an event using prism/IEventAggregator. They will all be of the same type of object but I do not want them to subscribe to the same event. Even though they are of the same type I want them to have unique subscriptions. So when I publish an event I can direct it to each unique/specific object. Does anyone have any thoughts? Is this feasible at all?
Having unique event types for each object you create would be very cumbersome and probably scale very poorly. However you can have a property on your event object that would be the specific instance you want to direct the event to.
The subscribers (your objects) would all receive the event notification but would check the 'recipient property' to see if the event is for them. If not they would ignore it, otherwise act.
Pseudo code:
public class MyEvent {
public MyClass Recipient { get; set; }
}
public class MyClass {
public MyClass(IEventAggregator evt) {
evt.Register<MyEvent>(TakeAction);
}
private void TakeAction(MyEvent event) {
if (!event.Recipient == this) {
return;
}
// Perform the action
}
}
Use a single event and pass a payload that contains information about the event. Then the subscribers can filter the event based on the payload you sent with the event.
https://github.com/PrismLibrary/Prism/blob/master/Documentation/WPF/70-CommunicatingBetweenLooselyCoupledComponents.md#subscription-filtering
I have two a Caliburn.Micro ViewModels communicating each other by EventAggregator:
public class LeftViewModel
{
// ...
public void DoInRight()
{
messanger.PublishOnUIThread(new DoInRightMessage());
}
}
public class RightViewModel : IHandle<DoInRightMessage>
{
public void Handle(DoInRightMessage e)
{
// ...
}
}
It has worked fine until I had just one instance of LeftViewModel and RighViewModel on the screen. But now I would like to allow arbitrary number of instances of both of them - always in couples. So LeftViewModel.DoInRight() should send the message only in one RighViewModel (the one in the couple), not all RighViewModel instances.
Update 8.7.2015:
How to solve such pair messaging:
Some kind of message channels/groups by adding a parameter target or id to the messages (thanks #jophy job for point me to the idea).
Have multiple EventAggregators.
Abandon the EventAggregator and call methods directly.
Or any other solution?
You can try using some sort of ID for every View.
there is a similar question Caliburn.Micro and event aggregator -unwanted call handle method
I'm currently implementing a SignalR application to send a message to several clients when an external library I've written fire an event.
I want to attach a unique handler to my event for every instance of my hub, so I could send the messages only once when it is fired; but as SignalR instanciate many hubs on every request, I end up with more than one event handler attached every time. So my question is: what can I do to attach only once on my event ?
Here is my code:
public class MyHub : Hub
{
private static ExternalClass staticObject = new ExternalClass();
public MyHub()
{
staticObject.MyEvent += staticObject_MyEvent;
}
private void staticObject_MyEvent(object sender, EventArgs e)
{
//Some irrelevant code which send messages to clients
}
}
I know my question is pretty the same than this one and a lot of others, but I never found a satisfying anwser for multi-threaded or multi-instance environment - which is definitly my case.
Here is the code replacing my old event declaration in my library :
private bool isEventAlreadyRegistered = false;
private static readonly object verrou = new object();
private System.EventHandler myEvent = delegate { };
public event System.EventHandler MyEvent
{
add
{
if(!isEventAlreadyRegistered)
{
lock(verrou)
{
//Double check as multiple add can be made simultaneously
if(!isEventAlreadyRegistered)
{
isEventAlreadyRegistered = true;
myEvent += value;
}
}
}
}
remove
{
myEvent -= value;
}
}
It works magnificently. My only doubt was about the performances with the lock; but as it is used only on the first call, it's negligible.
Of course, this solution only works when you have access to the source code of your event.
Hubs live in transient life. Meaning it will be created for each type of transaction (connected, on client message, dissconnected etc).
Never, ever have anything else then SignalR logic in the hub. Move your code somewhere else and invoke the client method when you want to upate. Or use a library that already have abstracted that for your. For example this library (I'm the author)
https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/wiki
Live demo:
http://malmgrens.org/signalR/