這個問題有一個解決方案,但我使用WCF雙工服務來獲得長操作的結果,即使我發現一個問題花了我幾個小時來解決(這就是爲什麼我搜索這個問題較早),現在它完美地工作,我相信這是WCF雙工服務框架內的一個簡單解決方案。
長時間操作有什麼問題?主要的問題是在服務器執行操作時阻塞客戶端接口,並且使用WCF雙工服務,我們可以使用回調客戶端來避免阻塞(這是一種避免阻塞的舊方法,但它可以很容易地轉換爲使用TaskCompletionSource的異步/等待框架)。
簡而言之,解決方案使用一種方法在服務器上異步啓動操作並立即返回。結果準備就緒後,服務器通過客戶端回撥返回它們。
首先,你必須遵循任何標準指南來創建WCF雙工服務和客戶端,我發現這兩個有用:
msdn duplex service
Codeproject Article WCF Duplex Service
然後按照以下步驟將自己的代碼:
定義帶有事件管理器方法的回調接口以從t他服務器並在客戶端接收它們。
public interface ILongOperationCallBack
{
[OperationContract(IsOneWay = true)]
void OnResultsSend(....);
}
定義服務接口與方法傳遞由長操作所需的參數(參照在CallBackContractAttribute先前ILongOperationCallBack接口)
[ServiceContract(CallbackContract=typeof(ILongOperationCallBack))]
public interface ILongOperationService
{
[OperationContract]
bool StartLongOperation(...);
}
在實現該服務的服務類接口,首先獲取客戶端回調的代理並將其保存在類字段中,然後異步啓動長時間操作並立即返回bool值。長操作工作完成後,使用客戶端回撥代理字段將結果發送給客戶端。
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
}
....
}
在客戶端創建一個實現ILongOperationCallBack接受結果,並添加了一個方法在服務器(start方法和事件管理器啓動運行時間長的一類並不需要在同一類)
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
}
}
注:
我用的是布爾RET urn在StartLongOperation方法中指示服務器是Busy而不是Down,但只有在長操作不能像我的實際應用程序中那樣併發時才需要,並且可能在WCF中有最佳方式來實現非併發(發現服務器是否關閉,像往常一樣添加一個Try/Catch塊)。
我沒有看到記錄的重要報價是需要在StartLongOperation方法中緩存回調客戶端代理。我的問題是我試圖在工作方法中獲取代理(是的,所有示例都在服務方法中使用回調客戶端代理,但在文檔中沒有明確說明,並且在長時間的操作我們必須延遲迴撥直到操作結束)。
在服務方法返回並且在下一個服務方法之前,不要獲取並緩存兩次回調代理。
聲明:我還沒有添加代碼來控制誤差等
因爲這個Windows服務運行用戶的系統上,我不能使用MSMQ(XP,Vista中,7),是產品的一部分用戶下載。所以GetStatus()是我想要的方式。想知道在多個命令同時執行時會有多複雜。 – A9S6 2010-03-08 10:36:33
@ A9S6:WCF爲每個調用創建一個全新的服務實例 - 因此,多個請求在同一時間確實沒有問題 - 每個都有自己的小型隔離服務類實例。 – 2010-03-08 11:19:06
您是否意味着在每次調用時都會創建Service Class(ServiceBase繼承)的新實例?我以爲這隻發生在Web服務上,而不是Windows服務。那麼,除非每個方法調用都需要,否則不應該在這個類中放置初始化? – A9S6 2010-03-08 13:39:46