Bu makalede basit bir örnek uygulama yaparak Sequence türündeki workflowları anlatmaya çalışacağım.
Örnek uygulamamız basit bir bilgisayar toplama flow’u içeriyor. Bu workflowu inceleyerek şunları öğrenmiş olacaksınız :
- Bir sequence workflow oluşturmak
- Mevcut bir workflow’u çalıştırmak
- Çalıştıracağımız workflow’a parametre geçmek
- if-else-activity, codeActivity ve terminate aktiviteleri(bileşenleri)
Öncelikle uygulamayı anlatarak başlayayım. Uygulama belirli parça stoklarına sahip olacak ve kullanıcı istediği ve stoklar elverdiği müddetçe yeni bilgisayar toplayacak ve son olarak topladığı bilgisayarda kullandığı parçaları stoktan düşecek.
İlk önce yeni bir proje açıyoruz. Soldaki listeden workflow grubunu, sağdaki listeden ise “sequential workflow console application” seçiyoruz.

Proje adını da “wfCompTechSample” olarak belirttikten sonra “Ok” düğmesine bastığımız anda visual studio bize gerekli solution ve dosyaları yaratıyor. Default yaratılan “workflow1.cs” dosyasını siliyoruz ve yerine kendi “Builder” worklowmuzu yaratıyoruz.

Yeni workflow yaratmak için projenin üzerinde sağ tuşa tıklayıp “Add->Sequential Workflow” seçiyoruz. Dosya adı olarak “Builder.cs” belirttikten sonra “Ok” diyoruz.
Sonrasında “Stock” adında yeni bir sınıf ekliyoruz. Bu sınıf bizim stok bilgilerimizi saklayacak.
public class Stock
{
public int Mainboard { get; set; }
public int Ram { get; set; }
public int GraphicCard { get; set; }
public int Chasis { get; set; }
public int CDRom { get; set; }
public int CPU { get; set; }
public int Fan { get; set; }
public int HDD { get; set; }
public void LoadDefaultStock()
{
Mainboard = 5;
Ram = 7;
GraphicCard = 3;
Chasis = 3;
CDRom = 3;
CPU = 3;
Fan = 2;
HDD = 3;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append(String.Format(”Mainboards : {0}\n”, Mainboard));
sb.Append(String.Format(”Ram : {0}\n”, Ram));
sb.Append(String.Format(”GraphicCard : {0}\n”, GraphicCard));
sb.Append(String.Format(”Chasis : {0}\n”, Chasis));
sb.Append(String.Format(”CDRom : {0}\n”, CDRom));
sb.Append(String.Format(”CPU : {0}\n”, CPU));
sb.Append(String.Format(”Fan : {0}\n”, Fan));
sb.Append(String.Format(”HDD : {0}\n”, HDD));
return sb.ToString();
}
}
Sonrasında Builder workflowumuza dönüyoruz. Workflowun designer penceresinde bir yeşil ok ve bir kırmızı kare sembollü iki şekil göreceğiz. Bunlar akışın başlangıç ve bitişini gösteriyor. bunların arasına yerleştireceğimiz her aktivite, iş akışındaki bir görev veya adım anlamına gelmektedir.
Kullanacağımız aktiviteleri toolbar üzerinden görebilirsiniz. Öncelikle bir Code Activity bileşenini sürükleyip workflow’a bırakıyoruz. Bu bizim başlangıç aktivitemiz olacak, bunu koymaktaki amacaım amaç akışın başladığını kullanıcıya haber vermek. Code Activity eklediğimizde üzerinde bir kırmızı ünlem işareti belirecek. Bu henüz “ExecuteCode” özelliğinin belirtilmediği anlamına gelmektedir. Benzer şekilde, diğer bileşenlerin de temel gereksinim özelliklerini belirtmediğiniz sürece üzerlerinde bir “!” işareti göreceksiniz.
ExecuteCode özelliği bir metoda delege etmekte, dolayısı ile aynı ButtonClick olayı gibi buraya bir metod ismi belirtiyoruz. Ben “caInitActivity_ExecuteCode” yazdım. Sonra da kod arkasından aktivitenin başladığına dair bir mesaj yazdırdım.

Ardından da if-else-activity ekliyoruz. Bu aktivite, belirli bir durumu kontrol edip buna bağlı olarak branch larındaki uygun akışı devam ettirir. Kod yazarken kullandığımız if-else yapısının görsel halidir diyebiliriz. İlk sürükleyip bıraktığımızda iki adet brunch otomatik eklenir, istersek daha fazla brunch ekleyebiliriz.
Sonrasında her bir branchın çalışma şartını belirliyoruz. Dalların çalışma şartı iki şekilde olabilir : “Code Condition” ve “Declarative Rule Condition”.
Code Condition, karşılaştırma işlemini kod arkasından yapmamızı sağlar. Belirteceğimiz metod karşılaştırma işlemini yapar ve result olarak true veya false değeri döner. false döner ise, bir sonraki brunch test edilir.
Declarative Rule Condition ise direkt belirtebileceğimiz bir script expression içerir. Örneğin workflow’a ait bir özellik veya bir başka aktivitenin sonucunu karşılaştırabiliriz.
If-else-activity, soldan sağa doğru her bir dalı kontrol eder. İlk bulduğu doğru değerdeki aktivite akışını tamamlar ve diğerlerini çalıştırmaz. Biz de ilk olarak soldaki aktivitede stok kontrolü yapıyoruz. Stok kontrolü geçilmediği durumda sağ taraftaki dal çalışacak (bunun için declarative rule contition a sadece “true” değerini yazıyoruz)
Ardından kod aktivitelerini aşağıdaki gibi ekleyip gerekli tanımlamaları yapıyoruz.

Burada kullandığımız “terminate-activity” (kırmızı “x” simgeli), hata oluştuğu için workflow u sonlandırmamızı sağlayacak. Fakat workflow sonlandırılırken bir hata mesajı verilmesini istiyoruz.
Bunun için yeni bir property yaratıp “Error Message” adını vereceğiz. .NET bizim için gerekli kodları otomatik oluşturacaktır. Bu property’i oluştumuşken bizim parametre olarak göndereceğimiz “Stock” türündeki property’i de tanımlıyoruz.
Bundan sonra aktivitelerimizin arkasındaki kodları geliştiriyoruz. Böylece şartları ve süreci tamamlıyoruz. Builder sınıfının kodları aşağıdaki gibidir.
public sealed partial class Builder: SequentialWorkflowActivity
{
#region Property definitions
public Stock CurrentStock { get; set; }
public static DependencyProperty taNotEnoughStock_ErrorMessageProperty = DependencyProperty.Register(”taNotEnoughStock_ErrorMessage”, typeof(System.String), typeof(wfCompTechSample.Builder));
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[BrowsableAttribute(true)]
[CategoryAttribute("Activity")]
public String taNotEnoughStock_ErrorMessage
{
get
{
return ((string)(base.GetValue(wfCompTechSample.Builder.taNotEnoughStock_ErrorMessageProperty)));
}
set
{
base.SetValue(wfCompTechSample.Builder.taNotEnoughStock_ErrorMessageProperty, value);
}
}
#endregion
public Builder()
{
InitializeComponent();
}
private void caInitActivity_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine(”builder started. with following stock.”);
Console.WriteLine(CurrentStock.ToString());
}
private void ifbStockEnough_CheckStock(object sender, ConditionalEventArgs e)
{
e.Result = (existEnough(CurrentStock.Mainboard) &&
existEnough(CurrentStock.Ram) &&
existEnough(CurrentStock.HDD) &&
existEnough(CurrentStock.GraphicCard) &&
existEnough(CurrentStock.Fan) &&
existEnough(CurrentStock.CPU) &&
existEnough(CurrentStock.Chasis) &&
existEnough(CurrentStock.CDRom));
}
private void caBuildComputer_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine(”building computer..”);
CurrentStock.Mainboard–;
CurrentStock.Ram–;
CurrentStock.HDD–;
CurrentStock.GraphicCard–;
CurrentStock.Fan–;
CurrentStock.CPU–;
CurrentStock.Chasis–;
CurrentStock.CDRom–;
}
private void caSetStockErrorMessage_ExecuteCode(object sender, EventArgs e)
{
taNotEnoughStock_ErrorMessage = “not enough stock to build a computer”;
}
private void caFinalActivity_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine(”successfully built..”);
}
private bool existEnough(int device)
{
return device – 1 >= 0;
}
}
Bütün bunları tamamladıktan sonra iş ana programdan workflow’u çağırmaya kaldı.
.NET zaten default olarak ana programa workflow’u çağırmak için gerekli temel kodları yerleştiriyor. Biz bu kodlara kullanıcı ile kuracağımız iletişim kodlarını ve parametrik workflow çağırımı için gerekli değişiklikleri ekleyeceğiz. Bu eklemeler sonrası tam kod aşağıdaki gibidir.
static void Main(string[] args)
{
string answer = “y”;
Stock currentStock = new Stock();
currentStock.LoadDefaultStock();
while (true)
{
Console.Write(”Do you want to build a computer [y/n]?”);
answer = Console.ReadLine();
if (answer.Equals(”n”, StringComparison.InvariantCultureIgnoreCase)) break;
using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) { waitHandle.Set(); };
workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};
Dictionary<string, object> arguments = new Dictionary<string, object>() { { “CurrentStock”, currentStock } };
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(wfCompTechSample.Builder), arguments);
instance.Start();
waitHandle.WaitOne();
}
}
}
Son olarak uygulamamızı çalıştırıyoruz. Çıktısı aşağıdaki gibi olacaktır.

Örnek uygulamayı aşağıdaki linkten indirebilirsiniz
wfCompTechSample.zip