2016-06-10 1 views
4

Мне нужен мой WCF-сервис, чтобы поднять события для клиентов. Я читал, что это произойдет через обратный вызов канал, и я реализовал это следующим образом: службы интерфейса: реализацияКак настроить канал обратного вызова в службе WCF, созданной через ChannelFactory?

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; 
} 

Услуги:

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 
} 
} 

реализацию обратного вызова клиент:

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); 
    } 
} 

Но вот моя проблема: на стороне клиента, я должен передать его на службу, как это:

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

согласно этому StackOverflow ответ: https://stackoverflow.com/a/1143777/2018696

Но я не подключиться к службе, как это, потому что мой клиент не знает об осуществлении сервиса, он знает только два интерфейса! Так я подключаю так:

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; 
    } 

Так как я могу получить обратные вызовы для работы?

UPDATE:

Хорошо, таким образом, предложенный комментарий, я заменил ChannelFactory с DuplexChannelFactory и BasicHttpBinding с WSDualHttpBinding, и я не получаю ответа от сервера. Я получаю ответ с помощью BasicHTTPBinding, если я поцарапаю обработчик обратного вызова. Так, по существу:

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

^это работает

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

^это не так.

Он работает на локальном хосте, но не в локальной сети или в Интернете. Брандмауэры отключены как на сервере, так и на клиенте. Я получаю 60-секундный тайм-аут, когда я пытаюсь связаться с сервером.

+3

Взгляните на [класс DuplexChannelFactory] (https://msdn.microsoft.com/en-us/library/ms576164 (v = vs.110) .aspx), который должен указывать вам направление , – carlosfigueira

+0

@ carlosfigueira Спасибо, я внесла некоторые изменения, но теперь я не получаю ответа от службы, посмотрите на «Обновить» в op, пожалуйста. Благодаря! – Daniel

ответ

0

Я обнаружил, что проблемы с подключением связаны с тем, что на пути к клиенту не установлено перенаправление портов. Поскольку я не могу обеспечить надлежащий доступ к портам для клиентов, я вернулся к модели без обратного вызова и будет использовать обычный запрос от клиентов для получения накопленных данных о событиях из службы. Вероятно, это не эффективный, но надежный метод, который, похоже, работает до сих пор. Благодарю всех вас за внимание.