5
votes

I am designing a 2 WCF services that all my clients will connect to. One of these services will be a notifications service.

I would like each client to connect to the service, subscribe to it and then receive notifications, using a duplex Callback interface (the service will fire the 'Notify' operation in the clients).

This is my Design Idea:

enter image description here

My question is: When each client connects to my service, I will validate it against the 'Users' table in my database (I will be using a UserNamePasswordValidator and implement the 'Validate' function).

Requirement: Each user needs to receive different notifications, based on rules defined in the database, but they all use the same contract.

For example:

John Smith's rules in the DB might be : Notify me on all new products that have a price of over 100 dollars.

Jane Doe's rules in the DB might be : Notify me on all new products that their names begin with 'JA'.

Jim Jabra's rules in the DB might be : Notify me on all new products that are of type 'Food'.

My service will have a worker thread that detects a change in the database (a new product was inserted to the database).

It should then loop over all the clients connected - and for each client send him a notification of the new product, only if it matches the client's notification requests.

Again - all clients receive the same type of update (new products), but each client should receive different products according to the rule in the database.

One approach I thought to implement this would be to use a Singleton service, that holds a list of :

  • Client Enpoint
  • User Object (from database)

This way - each time the worker thread detects a new product, it loops over this list and send notifications to whome ever needs it. The problem with this approach is that in order to have one global list of clients - I need to have the service as a Singlton, right ?

The second approach would be ... well ... I don't have another idea of how I can access a list of clients connected to the service from a worker thread ...

I guess the main problem I have is that each client might want different kinds of product notified to him. Meaning - the pub\sub method is not so good here, because my scenario requires the service to know about the clients.

Any suggestions on how I can solve this headaches ?

2
Have you thought about using a queue. Where each of your client connects to the queue and poll for data as they available. And simple have the WCF service be a service for subscribing for data. Then in your worker thread, push data to the client queue when the data pertains to them.rpgmaker
how would a queue solve my problem exactly ? what do you mean 'the wcf service will be a service for subscribing for data' ? do you have a sample or example I can look at ?John Miner

2 Answers

2
votes

In any way with duplex communication, you need to maintain TCP channel opened from server to client to be able to send notification.

Client is the one who initiate connection to server, and you need to keep this connection open, if this connection is lost you can't (shouldn't) initiate connection from server to client, because client can be behind NAT, have firewall, etc.

So in any way there must be some static (singleton) object on server side that keeps clients connections list, thought this is not necessarily WCF service. You can dependency inject this object into service constructor.

public class ProductRepository
{
    private EventAggregator eventAggregator;

    public void Add(Product product)
    {
        //...
        eventAggregator.Publish(new NewProductEvent(product))
    }
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class EventPublishingService
{
    private IClientCallback client;
    private EventAggregator eventAggregator;
    private Func<Product, bool> predicate;

    public EventPublishingService(...)
    {
        eventAggregator.Subscibe<NewProductEvent>(OnNewProduct);
    }
    private void OnNewProduct(NewProductEvent e)
    {
        if (predicate(e.Product)==true) client.Notify(e.Product);
    }

    public void Subscribe()
    {
        client = OperationContext.Current.GetCallbackChannel<IClientCallback>()
        var user = ServiceSecurityContext.PrimaryIdentity;
        predicate = GetFilterForUser(user);
    }
}
0
votes

What I meant to say is the following.

Create a wcf service that will be called by each client for subscribing to filters once. The wcf service itself will simply add data to the database and include information such as client name and filters information in the data store. Then your worker thread which will be in a window service which will simply poll your db for data and when data is available it will read from the subscription tables. It will then push the data to the queue of each clients which could be a shared queue server like rabbitmq.

On the client side assuming it is a window based app, it will simply poll the rabbitmq queue server for it data by looking for it in the queue with the name of itself(client1, e.t.c).