如何在通过 ChannelFactory 创建的 WCF 服务上设置回调通道?
How to set up callback channel on WCF service created via ChannelFactory?
我需要我的 WCF 服务来向客户端引发事件。我已经读到这是通过回调通道发生的,并且我已经通过以下方式实现了它:
服务接口:
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));
根据这个 Whosebug 答案:
但是我没有这样连接到我的服务,因为我的客户端不知道服务的实现,它只知道这两个接口!
所以我这样连接:
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;
}
那么如何让回调起作用?
更新:
好的,按照评论的建议,我用 DuplexChannelFactory 替换了 ChannelFactory,用 WsDualHTTPBinding 替换了 BasicHTTPBinding,但我没有得到服务器的响应。如果我抓取回调处理程序,我确实会收到 BasicHTTPBinding 的响应。所以基本上:
[ServiceContract]
BasicHttpBinding();
ChannelFactory<IService>.CreateChannel(binding, endpoint);
^ 这有效
[ServiceContract(CallbackContract = typeof(IServiceCallback))]
WSDualHttpBinding(WSDualHttpSecurityMode.None);
DuplexChannelFactory<IService>.CreateChannel(new InstanceContext(handler), binding, endpoint);
^ 这不是。
它适用于本地主机,但不适用于 LAN 或 Internet。服务器和客户端的防火墙都关闭了。当我尝试联系服务器时出现 60 秒超时。
我发现我的连接问题是由于没有在客户端路径上设置端口转发。由于我无法确保对客户端的正确端口访问,我已经恢复到非回调模型,并将使用来自客户端的常规请求来接收来自服务的累积事件数据。可能效率不高,但到目前为止似乎是一种万无一失的方法。谢谢大家的关注。
我需要我的 WCF 服务来向客户端引发事件。我已经读到这是通过回调通道发生的,并且我已经通过以下方式实现了它: 服务接口:
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));
根据这个 Whosebug 答案:
但是我没有这样连接到我的服务,因为我的客户端不知道服务的实现,它只知道这两个接口! 所以我这样连接:
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;
}
那么如何让回调起作用?
更新:
好的,按照评论的建议,我用 DuplexChannelFactory 替换了 ChannelFactory,用 WsDualHTTPBinding 替换了 BasicHTTPBinding,但我没有得到服务器的响应。如果我抓取回调处理程序,我确实会收到 BasicHTTPBinding 的响应。所以基本上:
[ServiceContract]
BasicHttpBinding();
ChannelFactory<IService>.CreateChannel(binding, endpoint);
^ 这有效
[ServiceContract(CallbackContract = typeof(IServiceCallback))]
WSDualHttpBinding(WSDualHttpSecurityMode.None);
DuplexChannelFactory<IService>.CreateChannel(new InstanceContext(handler), binding, endpoint);
^ 这不是。
它适用于本地主机,但不适用于 LAN 或 Internet。服务器和客户端的防火墙都关闭了。当我尝试联系服务器时出现 60 秒超时。
我发现我的连接问题是由于没有在客户端路径上设置端口转发。由于我无法确保对客户端的正确端口访问,我已经恢复到非回调模型,并将使用来自客户端的常规请求来接收来自服务的累积事件数据。可能效率不高,但到目前为止似乎是一种万无一失的方法。谢谢大家的关注。