標準工作流服務透過 Receive 活動接收訊息,由 WorkflowServiceHost 承載。但當工作流不包含任何訊息活動(純邏輯流程、批次處理、長時間執行作業),卻仍需透過服務端點建立執行個體、恢復書籤時,WorkflowHostingEndpoint 就是關鍵解決方案。
| 特性 | WorkflowServiceHost + Receive | WorkflowHostingEndpoint |
|---|---|---|
| 適用場景 | 訊息驅動工作流 | 無訊息活動的工作流 |
| 建立執行個體 | 自動 (Receive 啟動) | 透過端點明確呼叫 Create |
| 書籤恢復 | Receive 隱含處理 | 需實作 OnResolveBookmark |
| 服務合約 | 從活動推導 | 開發者自訂介面 |
[ServiceContract(Name = "IWorkflowCreation")]
public interface IWorkflowCreation
{
[OperationContract(Name = "Create")]
Guid Create(IDictionary<string, object> inputs);
[OperationContract(Name = "CreateWithInstanceId", IsOneWay = true)]
void CreateWithInstanceId(Guid instanceId, IDictionary<string, object> inputs);
[OperationContract(Name = "ResumeBookmark", IsOneWay = true)]
void ResumeBookmark(Guid instanceId, string bookmarkName, string message);
}
public class ResumeBookmarkEndpoint : WorkflowHostingEndpoint
{
public ResumeBookmarkEndpoint(Binding binding, EndpointAddress address)
: base(typeof(IWorkflowCreation), binding, address) { }
// 決定執行個體 ID:Create 回傳新 GUID,其他使用傳入 ID
protected override Guid OnGetInstanceId(object[] inputs, OperationContext operationContext)
{
string action = operationContext.IncomingMessageHeaders.Action;
if (action.EndsWith("Create")) return Guid.Empty;
if (action.EndsWith("CreateWithInstanceId") || action.EndsWith("ResumeBookmark"))
return (Guid)inputs[0];
throw new InvalidOperationException("Invalid Action: " + action);
}
// 建立工作流建立內容:傳遞輸入參數
protected override WorkflowCreationContext OnGetCreationContext(
object[] inputs, OperationContext operationContext,
Guid instanceId, WorkflowHostingResponseContext responseContext)
{
var context = new WorkflowCreationContext();
string action = operationContext.IncomingMessageHeaders.Action;
if (action.EndsWith("Create") || action.EndsWith("CreateWithInstanceId"))
{
var args = (Dictionary<string, object>)inputs[0];
if (args != null)
foreach (var kvp in args) context.WorkflowArguments.Add(kvp.Key, kvp.Value);
if (action.EndsWith("Create"))
responseContext.SendResponse(instanceId, null); // 同步回傳 InstanceId
}
else throw new InvalidOperationException("Invalid Action: " + action);
return context;
}
// 解析書籤:ResumeBookmark 對應書籤名稱與載荷
protected override Bookmark OnResolveBookmark(
object[] inputs, OperationContext operationContext,
WorkflowHostingResponseContext responseContext, out object value)
{
if (operationContext.IncomingMessageHeaders.Action.EndsWith("ResumeBookmark"))
{
value = inputs[2]; // message 參數
return new Bookmark((string)inputs[1]); // bookmarkName 參數
}
throw new NotImplementedException();
}
}
public sealed class CaryActivity<T> : NativeActivity<T>
{
public string BookmarkName { get; set; }
protected override bool CanInduceIdle { get { return true; } }
protected override void Execute(NativeActivityContext context)
{
// 建立書籤等待外部恢復
context.CreateBookmark(BookmarkName, OnBookmarkCallback);
}
void OnBookmarkCallback(NativeActivityContext context, Bookmark bookmark, object state)
{
Result.Set(context, (T)state); // 書籤恢復時設定活動結果
}
}
序列:
CaryActivity<string> (BookmarkName="cary") - 等待外部訊息WriteLine - 輸出書籤恢復的訊息內容static void Main(string[] args)
{
WorkflowServiceHost host = null;
try
{
host = new WorkflowServiceHost(new Workflow1(), new Uri("https://localhost"));
var endpoint = new ResumeBookmarkEndpoint(
new BasicHttpBinding(),
new EndpointAddress(new Uri("https://localhost/CaryService1.xaml")));
host.AddServiceEndpoint(endpoint);
host.Open();
Console.WriteLine("Host Started: " + endpoint.Address);
Console.ReadLine();
}
finally { host?.Close(); }
}
static void Main(string[] args)
{
var endpoint = new ResumeBookmarkEndpoint(
new BasicHttpBinding(),
new EndpointAddress(new Uri("https://localhost/CaryService1.xaml")));
var client = new ChannelFactory<IWorkflowCreation>(endpoint.Binding, endpoint.Address).CreateChannel();
Guid id = client.Create(null);
Console.WriteLine("WorkflowApplication {0} Created", id);
client.ResumeBookmark(id, "cary", "Hello World!");
}
Host Started: https://localhost/CaryService1.xaml
WorkflowApplication {GUID} Created
// 服務端主控台輸出: Hello World!
Create 回傳 ID (查詢),CreateWithInstanceId/ResumeBookmark 為單向命令OnGetInstanceId 根據 Action 決定新建或關聯既有執行個體CreateBookmark 閒置,外部透過 ResumeBookmark 注入資料繼續執行responseContext.SendResponse 僅在同步 Create 時使用此模式適用於:長時間執行的業務流程、人工審核等待、批次作業外部觸發、混合人機協作流程等無訊息活動工作流承載場景。