[en] Filter events serverside (solved)

Topics: Technical Support
Sep 15, 2015 at 9:15 PM
Hi,


I would like to use this library to replace WCF with bi-directional communication.
I very much like the fact that I don't need to have client and server interfaces and generate proxies for them. The client can just attach to events, which is very clean.

However, sometimes I would like to exclude a certain client from an event (e.g. when that client was the cause of it) which I might sometimes have the server send to all clients.
Because of this, registering filters from the client-side does not work in my case.

Is there a way to filter the recipients for an event, when it is called on the server? (I did see the solution in the whisper chat example, but would prefer to use events..)

Thanks.
Coordinator
Sep 16, 2015 at 7:16 PM
Hi @vverschuren,
 
I would like to use this library to replace WCF with bi-directional communication.
 
That's great! Glad to hear that! :)
 
Because of this, registering filters from the client-side does not work in my case.
 
Why not? Filtered event handlers should fit just fine for your use case.
You'll need something like this (pseudocode):
// interfaces assembly:

[Serializable]
public class SampleEventArgs : EventArgs
{
    public SampleEventArgs(Guid clientId, string payload)
    {
        ClientID = clientId;
        Payload = payload;
    }
    
    public Guid ClientID { get; private set; }

    public string Payload { get; private set; }
}

[Serializable]
public class SampleEventFilter : EventFilterBase<SampleEventArgs>
{
    public SampleEventFilter(Guid clientId)
    {
        ClientID = clientId;
    }

    private Guid ClientID { get; set; }

    protected override bool AllowInvocation(object sender, SampleEventArgs args)
    {
        return args.ClientID != CientID;
    }
}

// client assembly:

private void SampleEventHandler(object sender, SampleEventArgs e)
{
    Console.WriteLine("Payload: {0} from the client: {1}", e.Payload, e.ClientID);
}

// generate a random Guid for every client, create filtered event handler
var clientId = Guid.NewGuid();
var handler = new EventHandler<SampleEventArgs>(SampleEventHandler);
handler = handler.AddFilter(new SampleEventFilter(clientId));

// create a proxy and attach a filtered event handler
var proxy = connection.CreateProxy<ISampleService>();
proxy.SampleEvent += handler;

// fire an event, supply clientId for filtering
// the event will be routed to everyone except for the current client
proxy.TriggerSampleEvent(clientId, "Sample payload");
Let me know if it helps!

Regards, Alex
Marked as answer by yallie on 9/23/2015 at 11:48 PM
Sep 21, 2015 at 10:28 PM
Hi Alex,


I did some experimentation and this will work!
I am going forward implementing it in this way.

It is a shame that the client has to be Implemented correctly, in order to have event-routing, which is server-controlled.
Well, this isn't a show stopper in v1...

Thanks for your help!
Coordinator
Sep 22, 2015 at 8:22 AM
It is a shame that the client has to be Implemented correctly, in order to have event-routing, which is server-controlled.
 
Well, what do you propose instead?
I don't see any conceptual problem with this approach, even a slight one.
Furthermore, all parts of an application has to be implemented correctly, hasn't they?

The client subscribes to an event and decides how to handle it.
What would you do in a non-distributed application, when all events are local?
You'd probably just add if (sender != this) to your event handler.

It's basically the same code, and it belongs to the client.
Using an event filter is just an optimization.

Regards.
Sep 22, 2015 at 6:20 PM
In a non-distributed environment, there wouldn't be multiple "clients" for the server class.

In WCF I keep a collection of callback-endpoints.
Which is a pain, because I have to manually keep track of lost connections and have to generate proxy classes...
Zyan handles that nicely.

However in a distributed environment the server has to route messages to different clients.
This is inherently different that local events.
Coordinator
Sep 22, 2015 at 9:22 PM
In a non-distributed environment, there wouldn't be multiple "clients" for the server class.
 
Well, not exactly :)

Single client is just a typical degenerate case of the observer pattern (say, a button with a single click handler).
But the pattern is clearly designed to handle multiple recipients — no matter, local or remote.
Something like this (sorry for the dumb example, but you'll get the idea):
db.RecordSaved += (sender, e) => Console.WriteLine("Record saved!"); // client1
db.RecordSaved += (sender, e) => Logger.Debug("T: {TableName}, ID: {ID}", e.TableName, e.ID); // client2
db.RecordSaved += (sender, e) => // client3
{
    if (sender != this)
    {
        this.DataGrid.RefreshData();
    }
}
Observer pattern itself has no routing logic at all: events are simply distributed across all subscribers.
Zyan extends this pattern with event filters, so that a client can enforce custom filtering logic.
Anyway it's always the subscriber who decides which event he wants to receive and which not.
 
This is inherently different that local events.
 
Yes, I'm aware of the differences.

I agree that routing logic you are talking about looks like another typical use case.
It reminds me session-bound events, turned inside out.
Session-bound events are routed to the exact client who initiate it.
Your events should be routed to all clients except the initiator.
I'll think about enhancing session-bound events implementation to handle this use case.

Cheers, Alex.