C# WCF Windows服务-对调用模块的长时间操作/回调
我有一个Windows服务,它使用一堆文件的名称并对它们进行操作(zip/unzip、更新数据库等)。操作可能需要时间,具体取决于发送到服务的文件的大小和数量 (1) 向该服务发送请求的模块将等待文件处理完毕。我想知道是否有一种方法可以在服务中提供回调,当调用模块处理完文件时,回调将通知调用模块。请注意,多个模块可以一次调用该服务来处理文件,因此该服务需要提供某种类型的TaskIdC# WCF Windows服务-对调用模块的长时间操作/回调,c#,.net,wcf,windows-services,callback,C#,.net,Wcf,Windows Services,Callback,我有一个Windows服务,它使用一堆文件的名称并对它们进行操作(zip/unzip、更新数据库等)。操作可能需要时间,具体取决于发送到服务的文件的大小和数量 (1) 向该服务发送请求的模块将等待文件处理完毕。我想知道是否有一种方法可以在服务中提供回调,当调用模块处理完文件时,回调将通知调用模块。请注意,多个模块可以一次调用该服务来处理文件,因此该服务需要提供某种类型的TaskId (2) 如果一个服务方法被调用并且正在运行,并且对同一个服务进行了另一个调用,那么将如何处理该调用(我认为只有一个
(2) 如果一个服务方法被调用并且正在运行,并且对同一个服务进行了另一个调用,那么将如何处理该调用(我认为只有一个线程与该服务相关)。我已经看到,当服务处理一个方法时,与服务相关联的线程开始增加。WCF确实提供了双工绑定,允许您指定回调约定,以便服务可以回调调用客户端进行通知 然而,在我看来,这种机制相当脆弱,不值得推荐 在这种情况下,当调用导致相当长的运行操作发生时,我会这样做: 如果您想坚持HTTP/NetTcp绑定,我会:
- 通过服务放弃请求,然后“放手”——这将是一个单向呼叫,你只需放弃你想做的事情,然后你的客户就完成了
- 拥有一个状态调用,客户端可以在给定时间后调用该状态调用,以确定请求的结果现在是否准备就绪
- 如果是,则应该有第三个服务调用来检索结果
- 您的客户机将请求放到请求队列中
- 该服务侦听该请求队列,一个接一个地获取请求,并运行正常
- 然后,该服务可以将结果发布到结果队列中,您的调用者依次在该队列中侦听
编辑:默认的服务行为是每次调用启动新线程。可配置属性是,并且可以设置为PerCall、PerSession或Shareable。这个问题有一个解决方案,但我使用WCF双工服务来获得长时间操作的结果,尽管我发现了一个花了我几个小时才解决的问题(这就是我之前搜索这个问题的原因),但现在它工作得很好,我相信这是WCF双工服务框架内的一个简单解决方案 长期手术有什么问题?主要问题是在服务器执行操作时阻塞客户机接口,通过WCF双工服务,我们可以使用对客户机的回调来避免阻塞(这是一种避免阻塞的旧方法,但可以使用TaskCompletionSource轻松地将其转换为异步/等待框架) 简而言之,该解决方案使用一种方法在服务器上异步启动操作并立即返回。当结果准备就绪时,服务器通过客户端回调返回结果 首先,您必须遵循任何标准指南来创建WCF双工服务和客户机,我发现以下两个很有用: 然后按照以下步骤添加您自己的代码:
public interface ILongOperationCallBack
{
[OperationContract(IsOneWay = true)]
void OnResultsSend(....);
}
public class LongOperationService:ILongOperationService
{
ILongOperationCallBack clientCallBackProxy;
public ILongOperationCallBack ClientCallBackProxy
{
get
{
return OperationContext.Current.GetCallbackChannel<ITrialServiceCallBack>());
}
}
public bool StartLongOperation(....)
{
if(!server.IsBusy)
{
//set server busy state
//**Important get the client call back proxy here and save it in a class field.**
this.clientCallBackProxy=ClientCallBackProxy;
//start long operation in any asynchronous way
......LongOperationWorkAsync(....)
return true; //return inmediately
}
else return false;
}
private void LongOperationWorkAsync(.....)
{
.... do work...
//send results when finished using the cached client call back proxy
this.clientCallBackProxy.SendResults(....);
//clear server busy state
}
....
}
public class LongOperationService:ILongOperationService
{
ILongOperationCallBack clientCallBackProxy;
public ILongOperationCallBack ClientCallBackProxy
{
get
{
return OperationContext.Current.GetCallbackChannel<ITrialServiceCallBack>());
}
}
public bool StartLongOperation(....)
{
if(!server.IsBusy)
{
//set server busy state
//**Important get the client call back proxy here and save it in a class field.**
this.clientCallBackProxy=ClientCallBackProxy;
//start long operation in any asynchronous way
......LongOperationWorkAsync(....)
return true; //return inmediately
}
else return false;
}
private void LongOperationWorkAsync(.....)
{
.... do work...
//send results when finished using the cached client call back proxy
this.clientCallBackProxy.SendResults(....);
//clear server busy state
}
....
}
public class LongOperationManager: ILongOperationCallBack
{
public busy StartLongOperation(ILongOperationService server, ....)
{
//here you can make the method async using a TaskCompletionSource
if(server.StartLongOperation(...)) Console.WriteLine("long oper started");
else Console.Writeline("Long Operation Server is busy")
}
public void OnResultsSend(.....)
{
... use long operation results..
//Complete the TaskCompletionSource if you used one
}
}