工作流中的活動就像用戶自定義的控件,將許多的功能封裝起來用。WF4.0中提供了四種可繼承的活動 類:CodeActivity 、AsyncCodeActivity、Activity、NativeActivity。這幾種活動都有自己使用的適合 場合,正確的使用這些活動將非常有利。
1、CodeActivity
WF4.0中的活動是樹形結構的,創建葉子活動最簡單是方式就是使用CodeActivity ,它的邏輯都放在 一個方法:Execute 裡面,這個也是四種活動中最簡單的一種。這裡用一個簡單的自定活動HttpGet來說 明怎麼使用CodeActivity。HttpGet的功能是從網絡上抓取數據。
public sealed class HttpGet : CodeActivity<string>
{
public InArgument<string> Uri { get; set; }
protected override string Execute(CodeActivityContext context)
{
WebRequest request = HttpWebRequest.Create(this.Uri.Get (context));
using (WebResponse response = request.GetResponse())
{
//read everything response.GetResponseStream() as one string
using (StreamReader reader = new StreamReader (response.GetResponseStream()))
{
return reader.ReadToEnd();
}
}
}
}
public InArgument<string> Uri { get; set; }是工作流中的一個屬性,相當於類的屬性,不 過取它的值與類有點不同,你需要使用:Uri.Get(context))或者 context.GetValue(Uri),Execute方法 是這個活動的邏輯,特別注意CodeActivityContext context參數,這是WF的上下文,非常有用。
如何使用這個活動:
HttpGet fetchMsn = new HttpGet
{
Uri = "http://www.msn.com"
};
string msnContent = WorkflowInvoker.Invoke<string>(fetchMsn);
Console.WriteLine(msnContent);
2、AsyncCodeActivity
AsyncCodeActivity 類似CodeActivity ,只是它是使用了 Begin/EndExecute 取代了CodeActivity 的Execute 方法。BeginExecute 開始一個異步操作,無需等待它完成,就返回IAsyncResult對象 。當這 個操作完成的時候,就執行EndExecute 方法放回結果。HttpGet 能這樣實現,請注意 CodeActivityContext換成了AsyncCodeActivityContext:
class HttpGet : AsyncCodeActivity<string>
{
public InArgument<string> Uri { get; set; }
protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
{
WebRequest request = HttpWebRequest.Create(this.Uri.Get (context));
context.UserState = request;
return request.BeginGetResponse(callback, state);
}
protected override string EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
{
WebRequest request = (WebRequest)context.UserState;
using (WebResponse response = request.EndGetResponse (result))
{
using (StreamReader reader = new StreamReader (response.GetResponseStream()))
{
return reader.ReadToEnd();
}
}
}
}
3、Activity
Activity可以以組合的方式定義活動。開發人員可以使用已有活動構建一個活動樹來實現這個活動。 如果你需要在工作流中完成一些復雜的邏輯,可以使用這種方式。這裡用一個flowchart為例:
public sealed class FlowChartActivity : Activity
{
public string promoCode { get; set; }
public int numKids { get; set; }
public FlowChartActivity(string promoCode, int numKids)
{
this.promoCode = promoCode;
this.numKids = numKids;
base.Implementation = new Func<Activity> (CreateBody);
}
Activity CreateBody()
{
Variable<string> promo = new Variable<string> { Default = promoCode};
Variable<int> numberOfKids = new Variable<int> { Default = numKids };
Variable<double> discount = new Variable<double> ();
DelegateInArgument<DivideByZeroException> ex = new DelegateInArgument<DivideByZeroException>();
FlowStep discountNotApplied = new FlowStep
{
Action = new WriteLine
{
DisplayName = "WriteLine: Discount not applied",
Text = "Discount not applied"
},
Next = null
};
FlowStep discountApplied = new FlowStep
{
Action = new WriteLine
{
DisplayName = "WriteLine: Discount applied",
Text = "Discount applied "
},
Next = null
};
FlowDecision flowDecision = new FlowDecision
{
Condition = ExpressionServices.Convert<bool> ((ctx) => discount.Get(ctx) > 0),
True = discountApplied,
False = discountNotApplied
};
FlowStep singleStep = new FlowStep
{
Action = new Assign
{
DisplayName = "discount = 10.0",
To = new OutArgument<double> (discount),
Value = new InArgument<double>(10.0)
},
Next = flowDecision
};
FlowStep mnkStep = new FlowStep
{
Action = new Assign
{
DisplayName = "discount = 15.0",
To = new OutArgument<double> (discount),
Value = new InArgument<double>(15.0)
},
Next = flowDecision
};
FlowStep mwkStep = new FlowStep
{
Action = new TryCatch
{
DisplayName = "Try/Catch for Divide By Zero Exception",
Try = new Assign
{
DisplayName = "discount = 15 + (1 - 1/numberOfKids)*10",
To = new OutArgument<double> (discount),
Value = new InArgument<double> ((ctx) => (15 + (1 - 1 / numberOfKids.Get(ctx)) * 10))
},
Catches =
{
new Catch<System.DivideByZeroException>
{
Action = new ActivityAction<System.DivideByZeroException>
{
Argument = ex,
DisplayName = "ActivityAction - DivideByZeroException",
Handler =
new Sequence
{
DisplayName = "Divide by Zero Exception Workflow",
Activities =
{
new WriteLine()
{
DisplayName = "WriteLine: DivideByZeroException",
Text = "DivideByZeroException: Promo code is MWK - but number of kids = 0"
},
new Assign<double>
{
DisplayName = "Exception - discount = 0",
To = discount,
Value = new InArgument<double>(0)
}
}
}
}
}
}
},
Next = flowDecision
};
FlowStep discountDefault = new FlowStep
{
Action = new Assign<double>
{
DisplayName = "Default discount assignment: discount = 0",
To = discount,
Value = new InArgument<double>(0)
},
Next = flowDecision
};
FlowSwitch<string> promoCodeSwitch = new FlowSwitch<string>
{
Expression = promo,
Cases =
{
{ "Single", singleStep },
{ "MNK", mnkStep },
{ "MWK", mwkStep }
},
Default = discountDefault
};
Flowchart flowChart = new Flowchart
{
DisplayName = "Promotional Discount Calculation",
Variables = { discount, promo, numberOfKids },
StartNode = promoCodeSwitch,
Nodes =
{
promoCodeSwitch,
singleStep,
mnkStep,
mwkStep,
discountDefault,
flowDecision,
discountApplied,
discountNotApplied
}
};
return flowChart;
}
}
調用:
WorkflowInvoker.Invoke(new FlowChartActivity("MWK", 2));
注意這個自定義活動實現與前面兩個的區別,它在構造函數中指定實現活動的方法,而這個方法返回 Activity類型。
4、NativeActivity
這個活動是四種活動中最強大的一個,實現起來非常的靈活。WF4.0中內置的Sequence 、While 、If 、Parallel 等活動都繼承此類。如果前面三種都實現不了,這個活動可能能實現你需要的功能。例如自 定一個While:
public sealed class MyWhile : NativeActivity
{
Collection<Variable> variables;
public MyWhile()
{
this.variables = new Collection<Variable>();
}
public Collection<Variable> Variables
{
get
{
return this.variables;
}
}
public Activity<bool> Condition { get; set; }
public Activity Body { get; set; }
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
//call base.CacheMetadata to add the Variables, Condition, and Body to the activity's metadata
base.CacheMetadata(metadata);
if (this.Condition == null)
{
//MyWhile requires a Condition expression so - add a validation error if one isn't present
metadata.AddValidationError(string.Format("While {0} requires a Condition", this.DisplayName));
return;
}
}
protected override void Execute(NativeActivityContext context)
{
InternalExecute(context, null);
}
void InternalExecute(NativeActivityContext context, ActivityInstance instance)
{
//schedule the Condition for evaluation
context.ScheduleActivity(this.Condition, new CompletionCallback<bool>(OnEvaluateConditionCompleted));
}
void OnEvaluateConditionCompleted(NativeActivityContext context, ActivityInstance instance, bool result)
{
if (result)
{
if (this.Body != null)
{
//if the Condition evaluates to true and the Body is not null
//schedule the Body with the InternalExecute as the CompletionCallback
context.ScheduleActivity(this.Body, InternalExecute);
}
}
}
}
簡單的看下這個MyWhile,很簡單分兩個部分看:屬性和方法。屬性有variables、Condition、和Body 。方法有構造函數、 CacheMetadata、Execute、OnEvaluateConditionCompleted。CacheMetadata、 Execute 覆蓋父類的方法。ScheduleActivity完成之後執行OnEvaluateConditionCompleted。
總結:WF4.0的資料不多,但是相對WF3.0和3.5簡單很多,自定義活動是非常重要學習內容。