<a href="http://www.cnblogs.com/carysun/archive/2008/05/29/WF.html">[置頂]堅持學習WF文章索引</a>
我們除了使用WF提供的SqlWorkflowPersistenceService外,還可以自定義持久化服務。因為有的時候你可能不想使用Sql Server資料庫,我們就可以通過自定義持久化服務來使用其他的資料庫,檔案等來進行持久化存儲。
一:1.1 我們先看一個MSDN中的例子,當從記憶體中解除安裝工作流時,工作流運作時可使用該服務将工作流執行個體狀态儲存到檔案。該持久服務類代碼如下FilePersistence.cs:
public class FilePersistenceService : WorkflowPersistenceService
{
public readonly static TimeSpan MaxInterval = new TimeSpan(30, 0, 0, 0);
private bool unloadOnIdle = false;
private Dictionary<Guid,Timer> instanceTimers;
public FilePersistenceService(bool unloadOnIdle)
{
this.unloadOnIdle = unloadOnIdle;
this.instanceTimers = new Dictionary<Guid, Timer>();
}
protected override void SaveWorkflowInstanceState(Activity rootActivity, bool unlock)
// Save the workflow
Guid contextGuid = (Guid)rootActivity.GetValue(Activity.ActivityContextGuidProperty);
Console.WriteLine("Saving instance: {0}\n", contextGuid);
SerializeToFile( WorkflowPersistenceService.GetDefaultSerializedForm(rootActivity), contextGuid);
// See when the next timer (Delay activity) for this workflow will expire
TimerEventSubscriptionCollection timers = (TimerEventSubscriptionCollection)rootActivity.GetValue(TimerEventSubscriptionCollection.TimerCollectionProperty);
TimerEventSubscription subscription = timers.Peek();
if (subscription != null)
{
// Set a system timer to automatically reload this workflow when its next timer expires
TimerCallback callback = new TimerCallback(ReloadWorkflow);
TimeSpan timeDifference = subscription.ExpiresAt - DateTime.UtcNow;
// check to make sure timeDifference is in legal range
if (timeDifference > FilePersistenceService.MaxInterval)
{
timeDifference = FilePersistenceService.MaxInterval;
}
else if (timeDifference < TimeSpan.Zero)
timeDifference = TimeSpan.Zero;
this.instanceTimers.Add(contextGuid, new System.Threading.Timer(
callback,
subscription.WorkflowInstanceId,
timeDifference,
new TimeSpan(-1)));
}
private void ReloadWorkflow(object id)
// Reload the workflow so that it will continue processing
Timer toDispose;
if (this.instanceTimers.TryGetValue((Guid)id, out toDispose))
this.instanceTimers.Remove((Guid)id);
toDispose.Dispose();
this.Runtime.GetWorkflow((Guid)id);
// Load workflow instance state.
protected override Activity LoadWorkflowInstanceState(Guid instanceId)
Console.WriteLine("Loading instance: {0}\n", instanceId);
byte[] workflowBytes = DeserializeFromFile(instanceId);
return WorkflowPersistenceService.RestoreFromDefaultSerializedForm(workflowBytes, null);
// Unlock the workflow instance state.
// Instance state locking is necessary when multiple runtimes share instance persistence store
protected override void UnlockWorkflowInstanceState(Activity state)
//File locking is not supported in this sample
// Save the completed activity state.
protected override void SaveCompletedContextActivity(Activity activity)
Guid contextGuid = (Guid)activity.GetValue(Activity.ActivityContextGuidProperty);
Console.WriteLine("Saving completed activity context: {0}", contextGuid);
SerializeToFile(
WorkflowPersistenceService.GetDefaultSerializedForm(activity), contextGuid);
// Load the completed activity state.
protected override Activity LoadCompletedContextActivity(Guid activityId, Activity outerActivity)
Console.WriteLine("Loading completed activity context: {0}", activityId);
byte[] workflowBytes = DeserializeFromFile(activityId);
Activity deserializedActivities = WorkflowPersistenceService.RestoreFromDefaultSerializedForm(workflowBytes, outerActivity);
return deserializedActivities;
protected override bool UnloadOnIdle(Activity activity)
return unloadOnIdle;
// Serialize the activity instance state to file
private void SerializeToFile(byte[] workflowBytes, Guid id)
String filename = id.ToString();
FileStream fileStream = null;
try
if (File.Exists(filename))
File.Delete(filename);
fileStream = new FileStream(filename, FileMode.CreateNew, FileAccess.Write, FileShare.None);
// Get the serialized form
fileStream.Write(workflowBytes, 0, workflowBytes.Length);
finally
if (fileStream != null)
fileStream.Close();
// Deserialize the instance state from the file given the instance id
private byte[] DeserializeFromFile(Guid id)
// File opened for shared reads but no writes by anyone
fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
fileStream.Seek(0, SeekOrigin.Begin);
byte[] workflowBytes = new byte[fileStream.Length];
fileStream.Read(workflowBytes, 0, workflowBytes.Length);
return workflowBytes;
fileStream.Close();
}
1.2 看看我們的工作流設計,隻需要放入一個DelayActivity即可并設定他的Timeout時間,如下圖:
<a href="http://www.cnblogs.com/images/cnblogs_com/carysun/WindowsLiveWriter/WF14_64EB/CustomPersistenceService2.jpg"></a>
1.3 宿主程式加載了我們自定義的持久化服務後,執行結果如下:
<a href="http://www.cnblogs.com/images/cnblogs_com/carysun/WindowsLiveWriter/WF14_64EB/CustomPersistenceService1.jpg"></a>
二:上面的例子其實很簡單,我們隻是做了一些基本的操作,還有很多工作沒有做。我們就來說說如何自定義持久化服務。自定義持久化服務大概有以下幾步:
1.定義自己的持久化類FilePersistenceService ,必須繼承自WorkflowPersistenceService.
2.實作WorkflowPersistenceService中所有的抽象方法,下面會具體介紹。
3.把自定義的持久化服務裝載到工作流引擎中(和裝載WF提供的标準服務的方式一樣)。
下面我們來介紹下WorkflowPersistenceService類中相關的抽象方法:
2.1 SaveWorkflowInstanceState:将工作流執行個體狀态儲存到資料存儲區。
可以看出主要是調用了activity.Save(stream); ,将rootActivity 序列化到 Stream 中,如果我們自定義這個方法我們要調用 Activity的save方法将Activity序列化到stream中去,我們在實作LoadWorkflowInstanceState方法時會調用Activity的Load方法來讀取,另外我們把工作流儲存到持久化存儲裡我們一般都使用WorkflowInstanceId來做為唯一性辨別
當工作流執行個體完成或終止時,工作流運作時引擎最後一次調用 SaveWorkflowInstanceState。是以,如果 GetWorkflowStatus等于 Completed或 Terminated,則可以從資料存儲區中安全地删除工作流執行個體及其所有關聯的已完成作用域。此外,可以訂閱 WorkflowCompleted或 WorkflowTerminated事件,确定何時可以安全地删除與工作流執行個體關聯的記錄。是否确實從資料存儲區中删除記錄取決于您的實作。
如果無法将工作流執行個體狀态儲存到資料存儲區,則應引發帶有适當錯誤消息的 PersistenceException。
2.2 LoadWorkflowInstanceState :将SaveWorkflowInstanceState中儲存的工作流執行個體的指定狀态加載回記憶體
<dl></dl>
<dt>instanceId:工作流執行個體的根活動的 Guid。</dt>
<dt></dt>
必須還原活動的相同副本。為此,必須從資料存儲區中工作流執行個體的表示形式中還原有效的 Stream;然後,必須将此 Stream 傳遞到重載的 Load 方法之一,用于反序列化工作流執行個體。如果持久性服務無法從其資料存儲區加載工作流執行個體狀态,則它應引發帶有适當消息的 PersistenceException。
在我們上面例子中實作的該方法中,
我們調用了WorkflowPersistenceService.RestoreFromDefaultSerializedForm(workflowBytes, null);方法
我們Reflector出WorkflowPersistenceService類的代碼後可以看到,如下代碼:
protected static Activity RestoreFromDefaultSerializedForm(byte[] activityBytes, Activity outerActivity)
Activity activity;
DateTime now = DateTime.Now;
MemoryStream stream = new MemoryStream(activityBytes);
stream.Position = 0L;
using (GZipStream stream2 = new GZipStream(stream, CompressionMode.Decompress, true))
activity = Activity.Load(stream2, outerActivity);
TimeSpan span = (TimeSpan) (DateTime.Now - now);
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Deserialized a {0}
to length {1}. Took {2}.", new object[] { activity, stream.Length, span });
return activity;
将指定的已完成作用域儲存到資料存儲區。儲存完成活動的AEC環境以便實作補償,比如WhileActivity他每次循環的
都會建立新的AEC環境,這個時候完成的活動的AEC就會被儲存,但是前提是這個活動要支援補償才可以,所有如果你的WhileActivity裡包含SequenceActivity這樣該方法是不會被調用的,如果你換成CompensatableSequenceActivity就可以了
工作流運作時引擎儲存已完成作用域活動的狀态,以便實作補償。必須調用重載的 Save 方法之一,将 activity 序列化到 Stream 中;然後可以選擇在将 Stream 寫入到資料存儲區之前,對其執行其他處理。但是,在工作流運作時引擎調用 LoadCompletedContextActivity時,必須還原活動的相同副本。
本例子的程式中不會涉及到這部分
也同樣使用了WorkflowPersistenceService的GetDefaultSerializedForm方法
2.4 LoadCompletedContextActivity
和SaveCompletedContextActivity是對應的,加載SaveCompletedContextActivity中儲存的已完成活動的AEC,就不多說了
2.5 UnlockWorkflowInstanceState:解除對工作流執行個體狀态的鎖定。
此方法是抽象的,是以它不包含對鎖定和解鎖的預設實作。
實作自定義持久性服務時,如果要實作鎖定方案,則需要重寫此方法,并在 SaveWorkflowInstanceState方法中提供根據解鎖參數的值進行鎖定-解鎖的機制。
比如我們的工作流在取消的時候,這個方法被調用來解除工作流執行個體狀态的鎖定。
2.6 UnloadOnIdle
傳回一個布爾值,确定在工作流空閑時是否将其解除安裝。
這些都隻是自定義持久化中最基本的,就先說這些吧。
本文轉自生魚片部落格園部落格,原文連結:http://www.cnblogs.com/carysun/archive/2008/07/06/WFCustomPersistenceService.html,如需轉載請自行聯系原作者