Monday, June 20, 2016

How to set up callback channel on WCF service crated via ChannelFactory?

Leave a Comment

I need my WCF service to raise events to the clients. I've read that that happens through callback channel, and I've implemented it in the following manner: Service interfaces:

public interface IServiceCallback {     [OperationContract(IsOneWay = true)]     void OnNewAlert(Alert a);     [OperationContract(IsOneWay = true)]     void OnProductEdited(Product p);     [OperationContract(IsOneWay = true)]     void OnHighlightChanged(Dictionary<User, List<Product>> highlighted);     [OperationContract(IsOneWay = true)]     void OnCatalogUpdated();       event EventHandler NewAlert;     event EventHandler ProductEdited;     event EventHandler HighlightChanged;     event EventHandler CatalogUpdated; } [ServiceContract(CallbackContract = typeof(IServiceCallback))] public interface IService : IDisposable {     [OperationContract]     List<Product> GetProducts(Predicate<Product> match = null, int limit = 0, string username = null);     [OperationContract]     Product GetProduct(Predicate<Product> match, string username = null);     [OperationContract]     Product GetRandomProduct(Predicate<Product> match = null, string username = null);     [OperationContract]     int GetFlagIndex(string flagName);     [OperationContract]     void SetFlag(string pid, string flagName, bool value);     [OperationContract]     List<Alert> GetAlerts(string username);     [OperationContract]     void DismissAlert(Alert alert, String username);     [OperationContract]     void HighlightProduct(List<string> pids, string user);     [OperationContract]     void EditProduct(string pid, Dictionary<string, object> fieldValues, string username = null);     [OperationContract]     void AttachModule(IModule m);     [OperationContract]     void Ping();      event EventHandler NewAlert;     event EventHandler ProductEdited;     event EventHandler HighlightChanged;     event EventHandler CatalogUpdated; } 

Service implementation:

namespace Service { [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Reentrant)] public class ServiceInstance : IService {     List<IServiceCallback> callbackChannels = new List<IServiceCallback>();     //other vars      public ServiceInstance()     {             //lots of stuff here     }      private User SignalUser(string username)     {         if (username == null)             return null;          IServiceCallback channel = OperationContext.Current.GetCallbackChannel<IServiceCallback>();         if (!callbackChannels.Contains(channel)) //if CallbackChannels not contain current one.         {             callbackChannels.Add(channel);         }          User user = knownUsers.Find(p => p.username == username);         if (user == null)         {             user = new User();             user.username = username;             user.highlighColor = Color.FromArgb(r.Next(0, 128), r.Next(0, 128), r.Next(0, 128));             knownUsers.Add(user);             foreach (KeyValuePair<Alert, List<User>> kvp in alerts)             {                 kvp.Value.Add(user);             }         }         user.lastOnline = DateTime.Now;         if(!onlineUsers.Contains(user))             onlineUsers.Add(user);          return user;     }      //lots of other things here } } 

Callback implementation on Client:

class ServiceEventHandler : IServiceCallback {     public event EventHandler NewAlert;     public event EventHandler ProductEdited;     public event EventHandler HighlightChanged;     public event EventHandler CatalogUpdated;      public void OnCatalogUpdated()     {         CatalogUpdated?.BeginInvoke(null, null, null, null);     }      public void OnHighlightChanged(Dictionary<User, List<Product>> highlighted)     {         HighlightChanged?.BeginInvoke(highlighted, EventArgs.Empty, null, null);     }      public void OnNewAlert(Alert a)     {         NewAlert?.BeginInvoke(a, EventArgs.Empty, null, null);     }      public void OnProductEdited(Product p)     {         ProductEdited?.BeginInvoke(p, EventArgs.Empty, null, null);     } } 

But here is my problem: On the client side, I'm supposed to pass it to the service like this:

EventHandler eventHandler = new EventHandler(); MyServiceClient client = new MyServiceClient(new InstanceContext(eventHandler)); 

according to this StackOverflow answer: http://stackoverflow.com/a/1143777/2018696

But I don't connect to my service like this, because my client does not know about the implementation of the service, it only knows the two interfaces! So I connect like this:

    public static IService GetService(string serviceAddress)     {         Uri service_uri = new Uri(serviceAddress);         var endpoint = new EndpointAddress(service_uri, new[] { AddressHeader.CreateAddressHeader(settings["username"], "", "") });         IService service = ChannelFactory<IService>.CreateChannel(new BasicHttpBinding(), endpoint);         return service;     } 

So how do I get the callbacks to work? Thanks!

UPDATE:

Ok, so as proposed by a comment, I replaced ChannelFactory with DuplexChannelFactory and BasicHTTPBinding with WsDualHTTPBinding, and I don't get response from the server. I do get response with BasicHTTPBinding if I scratch the callback handler. So essentially:

[ServiceContract] BasicHttpBinding(); ChannelFactory<IService>.CreateChannel(binding, endpoint); 

^ this works

[ServiceContract(CallbackContract = typeof(IServiceCallback))] WSDualHttpBinding(WSDualHttpSecurityMode.None); DuplexChannelFactory<IService>.CreateChannel(new InstanceContext(handler), binding, endpoint); 

^ this doesn't.

It works on localhost, but not on LAN or Internet. Firewalls are off on both server and client. I'm getting 60 second timeout when I try to contact the server.

1 Answers

Answers 1

I have discovered that my connection issues are due to no port forwarding being set on the path to the client. Since I can't ensure proper port access to clients, I have reverted back to non-callback model, and will be using regular request from clients to received accumulated event data from the service. Probably not efficient, but a foolproof method that seems to be working so far. Thank you all for your attention.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment