Tag Archives: wf

Workflow Foundation ile Kopyalanarak Çoğaltılan Aktiviteler

Bir önceki yazımızda sırasıyla geçiş yaptığımız argümanlara göre toplam maliyeti hesaplayan bir workflow oluşturmuştuk. Şimdiye kadar sadece taşıma ücretini hesaplamıştık. Şimdi bu yazımızda her bir ürünün lojik olarak maliyetlerini ekleyeceğiz.

Bu yüzden bir önceki yazımızda oluşturduğumuz Siparisİslemi projeminiz üzerinde çalışacağız. Visual Studio’yu başlatıp yeni bir proje oluşturalım. Other Project Types –> Visual Studio Solutions seçeneği ile Kopyalanarak Çoğaltılan Aktiviteler adında yeni bir boş solution oluşturalım. Şimdi de C:\Users\Soner\Documents\Visual Studio 2010\Projects\Siparisİslemi altındaki Siparisİslemi klasörünü az önce oluşturduğumuz Kopyalanarak Çoğaltılan Aktiviteler klasörünün içine kopyalayalım.

WF-Folder

Solution Explorer kısmından Add->Existing Project diyerek Kopyalanarak Çoğaltılan Aktiviteler/Siparisİslem klasörünün içinde bulunan Siparisİslemi.csproj dosyamızı ekleyelim.

SiparişÜrün İşlemini Ekleyelim

ForEach aktivitesi Collection kısmındaki her bir madde için bir aktivite çalıştırır. Bu proje için bizim tam da istediğimiz budur aslında.

SiparisWF.xaml dosyamızı dizayn modunda açalım.  “İlk Toplam” aktivitemizin altına bir adet ForEach<T> aktivitesi sürükleyip bırakalım. DisplayName özelliğini Sipariş Ürünlerini Biriktirme olarak adlandıralım. Expression özelliğine SiparisBilgisi.Ogeler yazalım. Fakat bunu yazdığımızda bir hata alırız. Aktivitenin <T> olarak tanımlanması bu aktivitenin generic sınıf olduğunu, bu yüzden tip olarak collection içeren bir tip tanımlamamız gerektiğini bize gösterir. Varsayılan tip olarak Int32 seçildiğinden SiparisBilgisi.Ogeler ifadesi de bir Int32 tipinde collection içermediğinden bu hatayı alırız. Bu yüzden ArgumentType seçeneğini Browse Type diyerek Siparisİslemi.SiparisOge olarak belirtmeliyiz.

WF-BrowseType2

Bu tipi seçtiğimizde artık SiparisBilgisi.Ogeler değeri Siparisİslemi.SiparisOge tipinde bir collection içerir. (Siparis.cs içerisinde yazdığımız kod nedeniyle) Hata ortadan kalkmış olur. Şimdi ForEach<T> aktivitesi içerisine bir Assign aktivitesi sürükleyip bırakalım.

NOT: Foreach<T> aktivitesi içerisine sadece bir basit aktivite sürükleyebiliriz. Eğer birden fazla aktiviteye ihtiyacınız olursa, içerisine bir Sequence aktivitesi sürükleyip diğer aktiviteleri bunun içerisine yerleştirebilirsiniz.

Assign aktivitemizin To özelliğini ToplamMaliyet, Value özelliğini de ToplamMaliyet + (item.Miktar * 10.0D) olarak atayalım. Bu işlem sipariş içerisindeki her miktar için toplam maliyete 10 ekler.

Şimdi “Sipariş Ürünlerini Biriktirme” aktivitesinin altına bir Writeline aktivitesi ekleyelim. DisplayName özelliğine “Toplam Maliyeti Göster”, Text özelliğine de “Ürün Toplamı: ” + ToplamMaliyet.ToString() ifadesini atayalım. Bu sipariş edilmiş ürünlerin toplamını, taşıma ücretini eklemeden önce gösterir.

Sipariş Ürünlerini Ekleme

Şimdi bu özelliği test etmeden önce Sipariş sınıfı içerisindeki SiparisOge nesnelerini düzenlememiz lazım. Program.cs dosyamızı açalım ve aşağıdaki kodumuzu siparisim nesnesini oluşturduktan hemen sonra yazmaya başlayalım.


siparisim.Ogeler.Add(new SiparisOge
{
SiparisOgeID = 1,
Miktar = 1,
OgeID = "12345",
Tanım = "Alet"
});

siparisim.Ogeler.Add(new SiparisOge
{
SiparisOgeID = 2,
Miktar = 3,
OgeID = "12346",
Tanım = "Küçük Alet"
});

siparisim.Ogeler.Add(new SiparisOge
{
SiparisOgeID = 3,
Miktar = 2,
OgeID = "12347",
Tanım = "Süper Alet"
});

Şimdi F5 ile projemizi çalıştıralım.

Sipariş Alındı
Ürün Toplamı: 60
Toplam Maliyet: 120,0
Workflow’un döndürdüğü Toplam Maliyet 120,0
Enter to Exit

Peki bu nasıl hesaplandı?

SiparisOge sınıfının miktar olarak sırasıyla 1, 3, 2 ogeler mevcut. Bunların toplamı 6 yapıyor. Her biri için 10 eklediğimizi hatırlayalım. Bu da toplamı 60 yapar. Bir önceki yazıda gördüğümüz üzere Taşıma Ücreti’de 60 olarak hesaplamıştık. Bu ikisinin toplamı da sonuçta gördüğümüz gibi 120 değerini elde ediyoruz.

Tüm Program.cs kodumuz aşağıdaki şekildedir;


using System;
 using System.Linq;
 using System.Activities;
 using System.Activities.Statements;
 using System.Collections.Generic;

namespace Siparisİslemi
 {

class Program
 {
 static void Main(string[] args)
 {
 Siparis siparisim = new Siparis
 {
 SiparisID = 1,
 Tanım = "Sipariş Tanımı",
 SiparisMetodu = "İkiSonrakiGün",
 ToplamAgirlik = 100,
 };

siparisim.Ogeler.Add(new SiparisOge
 {
 SiparisOgeID = 1,
 Miktar = 1,
 OgeID = "12345",
 Tanım = "Alet"
 });

siparisim.Ogeler.Add(new SiparisOge
 {
 SiparisOgeID = 2,
 Miktar = 3,
 OgeID = "12346",
 Tanım = "Küçük Alet"
 });

siparisim.Ogeler.Add(new SiparisOge
 {
 SiparisOgeID = 3,
 Miktar = 2,
 OgeID = "12347",
 Tanım = "Süper Alet"
 });

//Workflow için input argümanlarını oluşturalım
 IDictionary<string, object> input = new Dictionary<string, object>
 {
 {"SiparisBilgisi", siparisim}
 };

//Workflow'u çalıştıralım
 IDictionary<string, object> output = WorkflowInvoker.Invoke(new SiparisWF(), input);

//Workflow'dan geri dönen ToplamMaliyet'i alalım
 decimal toplam = (decimal)output["ToplamMaliyet"];
 Console.WriteLine("Workflow'un döndürdüğü Toplam Maliyet {0}", toplam);
 Console.WriteLine("Enter to Exit");
 Console.ReadLine();
 }
 }
 }

ParallelForEach Aktivitesi

ForEach aktivitesi yerine ParallelForEach aktiviteisi de kullanabilirdik. İki aktivite de aynı şekilde yapılandırılmıştır. Tek fark aktivitelerin nasıl çalıştırıldığıdır.

ParallelForEach’in adından da anlaşılacağı gibi alt aktiviteler eşzamanlı olarak çalışırlar. ForEach aktivitesinde ise alt aktiviteler sıralı olarak çalışırlar.

Workflow Foundation ile Argüman Geçişleri

Daha önceki yazılarımızda workflow içerisinde argüman ve değişkenlerin nasıl tanımlandığını göz attık. Benzer olarak, değişkenler tıpkı sınıf üyeleri gibi, argümanlar ise tıpkı method parametrelerine benzerler. Önceki yazılarımızda değişkenleri kullanmıştık. Bu yazımızda input ve output argümanlarını ve bunların kullanımlarını göreceğiz.

Hemen Siparisİslemi adında bir Workflow Console Application oluşturalım.

WF-Siparisİslemi

Bu projemizde bazı ürünler için bir sipariş oluşturacağız ve bu siparişi workflow’umuzun içerisine geçireceğiz. Daha sonra workflow’umuz toplam maaliyeti hesaplayacak ve uygulamaya döndürecektir.

Şimdi Solution Explorer kullanarak projemize sağ tıklayıp Add –> Class seçeneğinden projemize Siparis.cs adındaki sınıfımızı ekleyelim.

WF-SiparisSolution

Siparis adındaki sınıfımıza siparis detaylarını içeren tanımlamaları yapmaya başlayalım.


namespace Siparisİslemi
{
public class SiparisMaddeleri
{
public int SiparisOgeID { get; set; }
public int Miktar { get; set; }
public int OgeID { get; set; }
public string Tanım { get; set; }
}

public class Siparis
{
public Siparis()
{
Ogeler = new List<SiparisMaddeleri>();
}
public int SiparisID { get; set; }
public string Tanım { get; set; }
public decimal ToplamAgirlik { get; set; }
public string SiparisMetodu { get; set; }

public List<SiparisMaddeleri> Ogeler { get; set; }
}
}

 

Siparis sınıfı birkaç public üyeye sahiptir (SiparisID, Tanım, ToplamAgirlik, SiparisMetodu) ve bir adet SiparisMaddeleri sınıfı listesine sahiptir. Bunlar sipariş maaliyetlerini ölçeceğimiz workflow’umuzun detaylarıdır.

Ardından projemizde bulunan Workflow1.xaml dosyasının ismini SiparisWF.xaml olarak değiştirelim. Ayrıca bu xaml dosyasını “View Code” ile açarak ilk satırdaki x:Class=”Siparisİslemi.Workflow1″ sınıf niteliğini x:Class=”Siparisİslemi.SiparisWF” olarak değiştirelim.

SiparisWF.xaml dosyamızı dizayn modunda açalım. Ardından sol alt taraftaki Arguments kısmına tıklayıp argümanlarımızı ekleyeceğimiz pencereyi açalım.

NOT: Argümanlarda, değişkenlerden farklı olarak Scope kısmı bulunmaz. Bunun nedeni, değişkenlerin workflow’un tümünde ya da belirli aktivitelerde tanımlanmasına karşın, argümanların tanımlanması tüm workflow içindir çünkü bu yapılar workflow’a ya da workflow’dan veri geçişini tanımlarlar.

Açtığımız pencereden Create Argument’e tıkayıp argümanımızın ismini SiparisBilgisi olarak değiştirelim. Direction kısmı In olarak kalsın. Argument Type seçeneğinde ise Browse Type seçeneğinden Siparisİslemi.Siparis tipini seçelim. Eğer bu tip Argument Type kısmında gözükmüyorsa projemizi bir kere Build Solution ile build etmeliyiz.

WF-BrowseType

İkinci bir argümanımızı ToplamMaliyet adında tanımlayalım. Direction kısmını Out, ArgumentType kısmını Decimal olarak taımlayalım. İlk kez kullandığınız için Decimal tipi açtığınız drop-down’da yer almaz. Bu tip mscorlib assembly içerisinde System namespace’i altındadır. (System.Decimal)

Şimdi Workflow’umuzu tasarlayalım. Bir adet Sequence aktivitesi sürükleyip bırakalım. İçine de bir Writeline aktivitesi sürükleyip Text özelliğine “Sipariş Alındı” yazalım. Bu aktivitenin altına bir Assign aktivitesi sürükleyelim. DisplayName özelliğini İlk Toplam, To özelliğini ToplamMaliyet ve Value özelliğini de 0 (sıfır) olarak atayalım. Bu aktivite kısaca toplam maliyeti sıfır olarak ilk kullanıma hazırlar.

Switch Aktivitesi

Switch aktivitesi tıpkı C#’taki switch ifadesine benzer şekilde çalışır. İçerisinde çalıştırılan ifadeye göre belirli aktiviteleri çalıştırılmasına olanak verir. Bu aktiviteyi SiparisMetodu hesaplamak için kullanacağız. Switch aktivitesi Toolbox içerisinde Switch<T> olarak bulunur. Assign aktivitesi altına bir adet Switch<T> aktivitesi sürükleyip bırakalım. Sürükleyip bıraktığımızda bu aktivite bizden içerisinde çalıştıracağımız SiparisMetodu string olduğu için bu aşamada tipimizi String olarak seçelim. DisplayName özelliğini Kullanım Ücreti olarak değiştirelim.

WF-Switch

Switch aktivitesi bir Expression özelliğine sahiptir. Bu özelliği SiparisBilgisi.Siparis olarak tanımlayalım. Daha sonra Add new case seçeneğinden BirSonrakiGün ve İkiSonrakiGün adında iki adet durum ekleyelim.

Expression Aktiviteleri

Şimdiye kadar projemizde BirSonrakiGün ve İkiSonrakiGün adında SiparisMetodu sonucuna göre iki adet case oluşturduk. Bir de bunun yanında diagramda görüleceği gibi default durumumuz mevcut. Şimdi de bu spesifik durumlara göre aktiviteler tanımlayalım. Bu durumumuzda Add aktivitesini kullanacağız.

NOT: System.Activities.Expressions namespace’i workflow’unuzda kullanabileceğiniz birçok aktivite içerir. (Add, Subtract, Multiply ve Divide gibi) Ayrıca expression’ları değerlendirmek için Equal, GreaterThan, And ve Or gibi lojik aktiviteler de içerir.

Ne yazık ki, Add aktivitesi Toolbox’ta mevcut değil. Bu aktiviteyi .xaml dosyamızın kod kısmından elle eklememiz gerekecektir. XAML dosyamızı “View Code” seçeneği ile açalım. Switch aktivitesi aşağıdaki şekilde görülecektir;


<Switch x:TypeArguments="x:String" DisplayName="Kullanım Ücreti" Expression="[SiparisBilgisi.SiparisMetodu]" sap:VirtualizedContainerService.HintSize="473,151">
<x:Null x:Key="BirSonrakiGün" />
<x:Null x:Key="İkiSonrakiGün" />
</Switch>

Görüldüğü üzere, BirSonrakiGün ve İkiSonrakiGün case’leri için x:Null durumu görülür. Bunun anlamı, bu durumlar için herhangi bir aktivite henüz tanımlanmamıştır. Şimdi kod ile Add aktivitemizi tanımlayalım. Yukarıdaki <x:Null ile başlayan iki satırı silip aşağıdaki Add aktivitemizi içeren kodlarımızı yazalım.


<Add x:TypeArguments="s:Decimal, s:Decimal, s:Decimal" x:Key="BirSonrakiGün"
DisplayName="15 Ekle" Left="[ToplamMaliyet]" Result="[ToplamMaliyet]"
Right="[15.0D]" />
<Add x:TypeArguments="s:Decimal, s:Decimal, s:Decimal" x:Key="İkiSonrakiGün"
DisplayName="10 Ekle" Left="[ToplamMaliyet]" Result="[ToplamMaliyet]"
Right="[10.0D]" />

Bu adımdan sonra da default durumu için aşağıdaki kodumuzu tanımlayalım;


<Switch.Default>
<Add x:TypeArguments="s:Decimal, s:Decimal, s:Decimal" DisplayName="5 Ekle"
Left="[ToplamMaliyet]" Result="[ToplamMaliyet]" Right="[5.0D]" />
</Switch.Default>

Add aktivitemizin Left, Right ve Result adında 3 adet özelliği vardır. Özet olarak, Left özelliğine Right özelliği eklenir ve toplam Result özelliğinde saklanır. Left ve Result özellikleri ToplamMaliyet argümanına ayarlanır. Right özelliği statik bir değere sahiptir ve her durum için farklıdır. Switch aktivitemizin son hali şu şekildedir;


<Switch x:TypeArguments="x:String" DisplayName="Kullanım Ücreti" Expression="[SiparisBilgisi.SiparisMetodu]" sap:VirtualizedContainerService.HintSize="473,151">
<Switch.Default>
<Add x:TypeArguments="s:Decimal, s:Decimal, s:Decimal" DisplayName="5 Ekle"
Left="[ToplamMaliyet]" Result="[ToplamMaliyet]" Right="[5.0D]" />
</Switch.Default>
<Add x:TypeArguments="s:Decimal, s:Decimal, s:Decimal" x:Key="BirSonrakiGün"
DisplayName="15 Ekle" Left="[ToplamMaliyet]" Result="[ToplamMaliyet]"
Right="[15.0D]" />
<Add x:TypeArguments="s:Decimal, s:Decimal, s:Decimal" x:Key="İkiSonrakiGün"
DisplayName="10 Ekle" Left="[ToplamMaliyet]" Result="[ToplamMaliyet]"
Right="[10.0D]" />
</Switch>

Kısacası, SiparisMetodu, BirSonrakiGün olduğunda ToplamMaliyet değerine 15 eklenir, İkiSonrakiGün olduğunda 10 eklenir, diğer durumlarda ise default kısım çalıştırılacağından 5 eklenir. Switch aktivitemizi genişlettiğimizde aşağıdaki şekilde görülür.

WF-AddinSwitch

NOT: Add aktivitemizin Right seçeneğinin 15.0D olarak tanımlanmasının nedeni Visual Basic syntax’ında Decimal değerlerinin bu şekilde gösterimi olduğundandır.

Şimdi Switch aktivitemizin altına bir Assign aktivitesi ekleyelim ve DisplayName özelliğini Taşıma Ücreti, To özelliğini ToplamMaliyet, Value özelliğini de ToplamMaliyet + (SiparisBilgisi.ToplamAgirlik * 0.5D) olarak ayarlayalım. Bu formül kısaca Taşıma Ücretine, her toplam ağırlık için 0.5 değer katar.

Ardından Assign aktivitesinin altına bir Writeline aktivitesi sürükleyip bırakalım. Text özelliğine “Toplam Maliyet: ” + ToplamMaliyet.ToString() ifadesini ekleyelim.

Tasarım kısmında herşeyimiz tamam. Şimdi Program.cs içerisinde workflow’umuzu çağıracağımız kodlarımızı yazalım.


using System;
using System.Linq;
using System.Activities;
using System.Activities.Statements;
using System.Collections.Generic;

namespace Siparisİslemi
{

class Program
{
static void Main(string[] args)
{
Siparis siparisim = new Siparis
{
SiparisID = 1,
Tanım = "Sipariş Tanımı",
SiparisMetodu = "İkiSonrakiGün",
ToplamAgirlik = 200,
};

//Workflow için input argümanlarını oluşturalım
IDictionary<string, object> input = new Dictionary<string, object>
{
{"SiparisBilgisi", siparisim}
};

//Workflow'u çalıştıralım
IDictionary<string, object> output = WorkflowInvoker.Invoke(new SiparisWF(), input);

//Workflow'dan geri dönen ToplamMaliyet'i alalım
decimal toplam = (decimal)output["ToplamMaliyet"];
Console.WriteLine("Toplam Maliyet {0}", toplam);
Console.WriteLine("Enter to Exit");
Console.ReadLine();
}
}
}

Bu kod kısaca Siparis sınıfı oluşturur ve bu sınıfı bazı verilerle doldurur. Sonra bir Dictionary nesnesi oluşturur ve Siparis nesnesini bunun içinde muhafaza eder. WorkflowInvoker sınıfının statik Invoke() metodunu çağırır. Invoke() metodu uygulama içerisinde bir workflow örneği oluşturur ve çalıştırır.

Bunu bir Dictionary nesnesi olarak geçirmemiz bize birden fazla parametre geçişine olanak verir. Invoke() metodu geriye bir Dictionary nesnesi (In/Out Direction ile tüm workflow argümanlarını içeren) döndürür. Toplam Maliyet argümanı Dictionary içerisinden çıkartılır ve ekrana yazdırılır. Şimdi uygulamamızı F5 ile çalıştıralım.

Sipariş Alındı
Toplam Maliyet: 110,0
Workflow’un döndürdüğü Toplam Maliyet 110,0
Enter to Exit

 

Peki doğru mu hesapladı?

Toplam ağırlığımızı 200 olarak verdiğimizden bu değeri 0.5 ile çarpıp 100 değerini elde ettik. Buna da “İkiSonrakiGün”’ seçeneğinin Right özelliğindeki değer 10.0 olduğu için bu iki değerin toplamını yani 110 değerini elde ettik.

Workflow Foundation ile FlowSwitch ve Parallel Aktivitesi Kullanımı

FlowSwitch aktivitesi tıpkı FlowDecision aktivitesine benzer şekilde çalışır fakat farklı olarak FlowDecision aktivitesinde True ya da False şeklinde 2 adet dal tanımlanmasına karşın, bu yapıda sınırsız sayıda dal tanımlayabilirsiniz. C#’ta ve bir çok programlama dilinde kullandığımız switch yapısına benzer.

Bu yazıda WF ile FlowChart Aktivitesi Oluşturmak makalemizde kullandığımız projemiz üzerinden devam edeceğiz. Projemizin alt tarafına bir FlowSwitch aktivitesi sürükleyip bırakalım. Toolbox kısmında FlowSwitch<T> şeklinde görünmektedir bu aktivite. Sürüklediğinizde sizden içereceği daha tipini gerektiren bir mesaj kutusu görünür. Burada default olarak Int32 görünür ki bizim de bu projemizde kullancağımız data tipi budur. Ardından daha önceden projemize eklediğimiz “Sabah”, “Öğleden Sonra” ve “Akşam” isimli Writeline aktivitelerini FlowSwitch aktivitemize bağlayalım.

WF-FlowSwitch

FlowSwitch aktivitesinin bir adet Expression adından bir özelliği vardır. Burada yazılan koşul, bu aktivitenin birden fazla dalındaki değerleri çözmek için kullanılır. (Tıpkı programlama’da olduğu gibi) Şimdi bu FlowSwitch aktivitemizin Expression özelliğine koşulumuzu yazalım;


CInt(((DateTime.Now.Month Mod 12) + 1) / 4)

NOT: Burada yazdığımız ifade sizi biraz şaşırtabilir. WF 4.0’da yazılan tüm ifadeler Visual Basic syntax’ı ile yazılırlar. Bu ifadeler derlenmezler, sadece workflow aktiviteleri tarafından hesaplanırlar. Böylece bu ifadelerin syntax’ı, uygulamalarda kullandığınız programlama dilinden bağımsızdır. İfade yazacağınız kutunun üzerine geldiğinizde “Enter a VB expression” yazısını görürüz.

Yukarıda yazdığımız ifade, yaklaşık olarak şu andaki zamanın mevsim değerini ifade eder. Aralık, Ocak ve Şubat ayları için 0 değerini üretir, Mart, Nisan, Mayıs için 1 değerini üretir. Şimdi bu aktivitemizde her bir mevsim için bir adet dal (branch) üretiriz.

FlowSwitch aktivitemizin her bir dalına FlowStep adı verilir. Tabi Toolbox’ta FlowStep aktivitesi bulunmaz. Bu dalları açıkça workflow’a eklemezsiniz. Bu dallar FlowSwitch aktivitesinden bağlantılar çizdiğinizde oluşturulurlar.

Şimdi FlowSwitch aktivitemizin etrafına 5 adet Writeline aktivitesi sürükleyip bırakalım. Bunlardan dördünü mevsimler için birini de default değer için kullanacağız. FlowSwitch aktivitesi ile bu 5 Writeline aktivitesini birbirlerine bağlayalım. Daha sonra da bu bağlantı dallarının üzerlerine tıklayıp Properties penceresinden Case özelliğine, ifade sonucunun hangi bağlantıyı seçeceğine dair Int32 değerlerimizi girelim. Default Writeline aktivitesini boş bırakalım ve bu bağlantının IsDefaultCase özelliğini tikleyelim.

WF-FlowStep

Tabi bu diagramımızda Default aktivitesi hiçbir zaman çalıştırılmaz çünkü olası her değer için bir Writeline aktivitesi tanımlamış durumdayız. Projemizi F5 ile çalıştırdığımızda aşağıdakine benzer sonuç elde ederiz;

Merhaba Dünya
İyi Öğle Sonraları
Selam Yaz
ENTER to exit

 

Parallel Aktivitesini Tanıyalım

Parallel aktivitesi, birçok aktivitenin paralel olarak çalıştırılmasına imkan veren bir aktivitedir. Bu projemizde her bir branch belirli bilgiler gösterirler. Gösterdikleri bilgilerin sıraları önemli değildir, bu yüzden bunları bir Parallel aktivite içerisine koyarız.

Şimdi bir Parallel aktivitesini Toolbox’tan workflow’umuzun altına sürükleyip bırakalım. Daha önce eklediğimiz 5 Writeline aktivitesini bu Parallel aktivitesine bağlayalım.

WF-Parallel

Ardından Parallel aktivitemize çift tıklayıp içerisine 3 adet Writeline aktivitesi ekleyelim. Bu aktiviteleri sırayla zamanı, tarihi ve günü göstermek için kullanacağız. Bu yüzden bu aktivitelerin Text özelliklerine sırasıyla;


"Zaman: " + DateTime.Now.TimeOfDay.ToString()

"Tarih: " + DateTime.Now.Date.ToShortDateString()

"Bugün: " + DateTime.Now.ToString("dddd")

ifadelerini yazalım.

WF-Parallel2

NOT: Parallel aktivitesi kendisine bağlanan her bir branch için basit bir adet aktivite destekler. Bizim projemiz için şu anda herhangi bir sorun yok. Fakat, her dal için birden fazla aktiviteye ihtiyacınız varsa,  Sequence aktivitesi kullanmanız gerekecektir. Sonra da içine herhangi bir sayıda aktivite ekleyebilirsiniz.

Şimdi de projemizi F5 ile çalıştıralım. Aşağıdakine benzer bir sonuç almalıyız;

Merhaba Dünya
İyi Öğle Sonraları
Selam Yaz
Zaman: 17:18:07.3144532
Tarih: 05.08.2012
Bugün: Pazar
ENTER to exit

Bir sonraki yazımızda görüşmek üzere..

Workflow Foundation ile Flowchart Aktivitesi Oluşturmak

Bu yazımızda Flowchart aktivitesi içeren workflow’ların nasıl oluşturulduğunu göreceğiz. Adından da anlaşılacağı gibi bir Flowchart aktivitesi aynı bir flowchart gibi çalışır. Aktiviteler birbirlerine karar yapıları ile bağlıdırlar (decision trees). Daha önceki yazılarımızda gördüğümüz gibi Sequence aktivitesi içerisinde aktiviteler sıralı olarak yukarıdan aşağıya çalışırlar. Fakat bir Flowchart aktivitesi içerisinde aktiviteler karar dallanmasına bağlı olarak herhangi bir sırada çalışabilirler.

Hemen bununla ilgili bir proje oluşturalım. Visual Studio 2010 –> File –> New –> Project –> Visual C# –> Workflow –> Workflow Console Application adımlarını takip edelim.

Oluşturduğumuz aktiviteye bir Flowchart aktivitesi sürükleyip bırakalım.

WF-Flowchart-Activity

Ortadaki yeşil daire flowchart aktivitemizin başlangıç düğümünü gösterir. Aşağıdaki boşluk alan da flowchart’ımızı oluşturmak için ekleyeceğimiz aktiviteleri içerme görevini üstlenir.

Bir Flowchart aktivitesi ile Sequence aktivitesi arasındaki temel fark alt aktivitelerin birbirlerine nasıl bağlandıklarıdır. Önceki yazılarımıza baktığımızda Sequence aktivitelerinin içindeki aktiviteler daima yukarıdan aşağıya doğru çalışırlar. Aktivitelerin çalışma sıralarını yerlerini değiştirerek kontrol edebiliriz. Flowchart aktivitesinde ise aktiviteleri diagram içerisinde istediğiniz yere koyabilirsiniz. Daha da önemlisi, bu aktivitelerin aralarına oklar çizmek zorundayız. Tabi Flowchart aktivitesi içerisinde, bir önceki aktiviteye de bağlantı kurabilirsiniz.

Hemen Flowchart aktivitemizin içerisine yeşil dairenin alt tarafına bir adet Writeline aktivitesi ekleyelim. DisplayName özelliği “Merhaba”, Text özelliği de “Merhaba Dünya” olsun.

Başlangıç düğümümüzün üzerine fare ile geldiğimizde dört kenarında gri kutucuklar belirecektir. Bu kutucuklardan birini tıklayıp Merhaba adındaki Writeline aktivitemizin kenarlarına bağlantı görününceye kadar sürükleyip bırakalım.

WF-Start-Node

Bu bize iki aktivitenin nasıl birbirlerine bağlandığı konusunda fikir verir.

Şimdi de diagramımızdaki Writeline aktivitemizin altına bir FlowDecision aktivitesi sürükleyip bırakalım. Bu aktivite sarı bir elmas şeklindedir tıpkı normal bir flowchart diagramındaki karar yapıları gibi. Bu aktivitenin üzerine tıklayıp Properties penceresinden Condition kısmına DateTime.Now.Hour > 12 ifadesini yerleştirelim. Bu ifadeyi yazdıktan sonra FlowDecision aktivitesinin üzerine gelip bu koşul ifadesini görebiliriz.

WF-FlowDecision

Ayrıca yukarıdaki sarı elmas şeklinin sağ üst köşesindeki sarı ok ile koşul ifadesini diagrama sabitleyebiliriz. Burada gördüğümüz gibi aktivitenin sol tarafında True sağ tarafında ise False dalları bulunur. Properties penceresinden TrueLabel özelliğine “Öğleden Sonra”, FalseLabel özelliğine de “Sabah” yazalım. Bu aktivitenin üzerine geldiğimizde bu özelliklerin isimlerini sağ ve sol tarafta görebiliriz.

Bu aşamalardan sonra Merhaba Writeline aktivitemizi FlowDecision aktivitemize bağlayalım. Sonra da FlowDecision aktivitesinin sağ tarafına yeni bir Writeline aktivitesi sürükleyip bırakalım. DisplayName özelliğini “Sabah”, Text özelliğini de “İyi Sabahlar” olarak ayarlayalım. Ardından FlowDecision aktivitemizin Sabah bağlantısını yeni eklediğimiz Sabah adındaki Writeline aktivitemize bağlayalım.

WF-Connection

NOT: Göreceğiniz gibi FlowDecision aktivitesinde DisplayName özelliği bulunmuyor.

Başka bir FlowDecision aktivitesini ilk eklediğimiz olanın soluna sürükleyip bırakalım. Bu aktivitenin Condition kısmına DateTime.Now.Hour >= 18 ifadesini yazalım. İlk FlowDecision aktivitemizin “Öğleden Sonra” dalını bu yeni eklediğimiz aktiviteye bağlayalım. Yeni aktivitemizin FalseLabel özelliğini “Öğleden Sonra”, TrueLabel özelliğini de “Akşam” olarak ayarlayalım. Ardından 2 adet Writeline aktivitesi ekleyelim. Sırayısla DisplayName özelliklerine “Akşam”, “Öğleden Sonra”, Text özelliklerine de “İyi Akşamlar” ve “İyi Öğle Sonraları” yazalım. Daha sonra ikinci eklediğimiz FlowDecision aktivitesini bu Writeline aktivitelerine bağlayalım.

WF-FlowDecision2

Herşey tamam. Şimdi projemizi çalıştırmadan önce Program.cs dosyamızı açalım. WorkflowInvoker sınıfı içerisinden çağırdığımız Invoke() methodu ardından aşağıdaki kodları yazalım;


Console.WriteLine("ENTER to exit");
Console.ReadLine();

Projemizi F5 ile çalıştıralım. Günün hangi saatinde olduğunuza göre aşağıdakine benzer bir sonuç alacaksınız;

Merhaba Dünya
İyi Akşamlar
ENTER to exit

Bu yazımızda FlowDecision aktivitesinin nasıl kullanıldığına dair bilgiler vermeye çalıştım.

Workflow Foundation 4.0 ile Workflow Kodlamak

Daha önceki yazılarda workflow’ları designer kısmından sürükle bırak yöntemi ile diagramımıza ekliyorduk. Bu workflow’ları kod yardımı ile de diagramımıza uygulayabiliriz. Her workflow kod ile ya da tasarım ile diagramlara eklenebilir, bu tamamen seçim meselesi. Fakat kod ile workflow’ları uygulamak size bu yapıların nasıl çalıştığına dair daha fazla bilgiler verecektir.

Başlangıç için bir Visual C# konsol uygulaması oluşturalım (WF konsol uygulaması değil!!). Daha sonra Solution Explorer kısmından Reference’a sağ tıklayıp Add Reference seçeneğini seçelim. Buradan System.Activities referansını seçip projemize ekleyelim. Bu durum bize workflow aktivitelerini kodumuzun içerisinde kullanma imkanı verir. Daha sonra kod kısmından aşağıdaki namespace’leri de ekleyelim;


using System.Activities.Statements;
using System.Activities.Expressions;

Daha sonra Main() methodumuzun içerisine aşağıdaki kodumuzu yazalım;


WorkflowInvoker.Invoke(WorkflowOlustur());
Console.WriteLine("Press ENTER to exit");
Console.ReadLine();

Burada WorkflowOlustur() methodumuzu biz kendimiz yazacağız. Bir önceki yazımda bahsettiğim gibi bir workflow sadece iç içe geçmiş özellikler topluluğudur. Daha doğrusu, iç içe geçmiş sınıflar ve bunları özellikleri topluluğudur. Şimdi WorkflowOlustur() methodumuzu yazmaya başlayalım.


static Activity WorkflowOlustur()
{
Variable<int> NumberOfBells = new Variable<int>()
{
Name = "NumberOfBells",
Default= DateTime.Now.Hour
};

Variable<int> Counter = new Variable<int>()
{
Name = "Counter",
Default = 1
};
return new Sequence()
{

};
}

WorkflowOlustur() methodumuz öncelikle 2 adet int tipinde Variable<T> template sınıfı oluşturduk. (NumberOfBells ve Counter) Bunlar çeşitli aktiviteler tarafından kullanılacak olan değişkenlerdir.

WorkflowInvoker sınıfının beklediği gibi, WorkflowOlustur() methodumuz geriye Activity döndürecek şekilde tanımlandı. Aslında geriye anonim bir Sequence sınıf örneği döndürür. Activity sınıfı, tüm workflow aktivitelerinin türetildiği ana sınıftır, buna Sequence dahil. Şimdi var olan kodumuzu adım adı geliştirmeye başlayalım;

1. Adım: Şimdiye kadar kodumuzda boş bir Sequence aktivitesi oluşturmuştuk. Bu kabaca Sequence’i olup hiçbir aktivitesi olmayan yeni bir workflow yaratmaya benzer. Şimdi yukarıda tanımladığımız Sequence() sınıfımızı yazalım;


return new Sequence()
{
DisplayName = "Main Sequence",
Variables = { NumberOfBells, Counter },
Activities =
{
new WriteLine()
{
DisplayName = "Merhaba",
Text = "Merhaba Dünya"
},
new If()
{
DisplayName = "PM için ayarla"
},
new While()
{
DisplayName = "Zil Sesi"
},
new WriteLine()
{
DisplayName = "Zamanı Göster",
Text = "Şu anki zaman" + DateTime.Now.ToString()
},
new If()
{
DisplayName = "Selam"
},
}
};

Yukarıdaki kodumuz önce aktiviteler için DisplayName ve içerisinde kullanılacak Variable nesnelerini tanımlar. Daha sonra da aşağıdaki aktiviteleri belirtilen DisplayName özelliği ile tanımlar.

Type DisplayName
Writeline Merhaba
If PM için ayarla
While Zil Sesi
Writeline Zamanı Göster
If Selam

2. Adım: Şimdi ilk If aktivitemizin içerisine koşul ifadelerini yazalım.


new If()
{
DisplayName = "PM için ayarla",
Condition = ExpressionServices.Convert<bool>
(env => NumberOfBells.Get(env) > 12),
Then = new Assign<int>()
{
DisplayName = "Zili Ayarla"
}
},

Bu kod if aktivitesinin koşul (condition) ve then kısımlarını tanımlar. Şimdi biraz Condition kısmına biraz daha yakından bakalım.

Deyimler

ExpressionServices sınıfının statik Convert<T>() methodu bir InArgument<T> sınıfı oluşturmak için kullanılır ki bu da tam olarak Condition özelliğinin istediği şeydir. Bu sınıf ve methodlar generic tipinde (<T>) olduklarından, her veri tipi için kullanılabilirler. Bu durumda bool tipini kullanmamız gerekir çünkü if aktivitesinin Condition özelliği sadece true ya da false destekler.

Bu deyim lambda expression kullanarak veriyi workflow’un çevresinden dışarı çıkartır. Burada gösterdiğimiz lambda expression deyiminde => lambda operatörü olarak gösterilir. Bu operatörün solundakiler giriş parametrelerini ifade ettiği gibi, gerçek koşul ifadesi bu operatörün sağında tanımlanır. env değeri runtime zamanında Condition kısmının çalıştırılmasını sağlar.

Aslında workflow herhangi bir veri elemanı depolamaz. Variable sınıfı basitçe veri tanımlar. Get() methodu ile Variable sınıfından veriler alınır. Get(env) ile geri dönen değeri 12’den büyük olup olmadığını karşılaştırır. Şimdi aşağıdaki kodumuzu While aktivitemize ekleyelim;


new While()
{
DisplayName = "Zil Sesi" ,
Condition = ExpressionServices.Convert<bool>
(env => Counter.Get(env) <= NumberOfBells.Get(env)),
Body = new Sequence()
{
DisplayName = "Zil Sesi"
}
},

Burada kullandığımız Condition kısmı If aktivitesi içerisinde kullandığımız ile özdeş bir kullanım. Tabi ki yine ExpressionServices sınıfınının (bool tipi ile) InArgument<T> sınıfı oluşturmak için kullanıyoruz. Bu durumda Counter <= NumberOfBells koşulunu hesaplarız. Bu iki değer için de, gerçek değerlerini hesaplamak için Get(env) methodunu kullanırız.

Şimdi de Selam adındaki ikinci If ifademize kodumuzu yazmaya başlayalım;


new If()
{
DisplayName = "Selam",
Condition = ExpressionServices.Convert<bool>
(env => DateTime.Now.Hour >= 18),
Then = new WriteLine() { Text = "İyi Akşamlar"},
Else = new WriteLine() { Text = "İyi Günler"}
},

Condition kısmında env giriş parametresi kullanılmaz fakat yine de deyim içerisinde tanımlanmak zorundadır. Mantıklı olarak saatin 6.00 PM’i geçip geçmediğine göre Then ve Else kısımlarında Writeline aktivitelerinde “İyi Akşamlar” ve “İyi Günler” yazdırılır.

Şimdi ilk If aktivitemiz “PM için ayarla” içerisine Assign aktivitesi (Then kısmı olan) oluşturmuştuk. Bu Assign aktivitemizin içerisine aşağıdaki kodlarımızı ekleyelim;


Then = new Assign<int>()
{
DisplayName = "Zili Ayarla",
To = new OutArgument<int>(NumberOfBells),
Value = new InArgument<int>(env => NumberOfBells.Get(env) - 12)
}

Assign Aktivitesi

Assign sınıfı generic tipindedir, yani herhangi bir veri tipini destekler. Bu durumda, integer tipinde bir değeri atamak için Assign<int> şeklinde kullanmamız gerekir. To ve Value özellikleri template sınıf olarak kullanılır ve aynı tipte (<int>) olmak zorundadırlar. To özelliği bir OutArgument sınıfıdır ve yapıcısının içerisinde bir Variable sınıfı olur. Value özelliği bir InArgument sınıfıdır. Yapıcısı için, Condition kısmında yaptığımız gibi lambda expression kullanımına ihtiyaç duyarız.

Sequence

While aktivitesi içerisinde boş bir Sequence aktivitesi oluşturmuştuk. Bunun anlamı while döngüsü her tekrarlandığında belirli olan aktivite çalıştırılır. Şimdi bu Sequence aktivitesine kodlarımızı yazalım;


Body = new Sequence()
{
DisplayName = "Zil Sesi",
Activities =
{
new WriteLine()
{
Text = new InArgument<string>(env => Counter.Get(env).ToString())
},
new Assign<int>()
{
DisplayName = "Sayacı Arttır",
To = new OutArgument<int>(Counter),
Value = new InArgument<int>(env => Counter.Get(env) + 1)
},
new Delay()
{
Duration = TimeSpan.FromSeconds(1)
}
}
}

Bu kod Sequence içerisine 3 adet aktivite ekler;

  • Counter değişkenini göstermek için bir Writeline aktivitesi
  • Counter değişkenini arttırmak için bir Assign aktivitesi
  • Her öteleme sonrasında küçük bir duraklama için Delay aktivitesi

Burada yazdığımız Writeline aktivitesine bakarsak Text özelliği diğer kullanımlar gibi klasik string değil. Burada değer bir deyim ile gösterilir. Get(env) methodu ile geriye int bir değer döndürür bu deyim. Daha sonra da bu deyimi ToString() methodu ile string haline (Text özelliğinin istediği şekilde) getiririz.

Delay aktivitesinde ise Duration özelliğine TimeSpan sınıfının FromSeconds() statik methodu geçirilerek bu aktiviteyi kullanırız.

Şimdi projemizi F5 ile çalıştıralım. Günün hangi saatinde olduğunuza göre değişebilen çıktımız aşağıdaki gibi olacaktır;

Merhaba Dünya
1
2
3
4
5
6
Şu anki zaman 03.08.2012 18:57:11
İyi Akşamlar
Press ENTER to exit

Program.cs dosyamızın tüm kodu aşağıdaki şekildedir;


using System;
using System.Activities;
using System.Activities.Statements;
using System.Activities.Expressions;

namespace WF_Konsol_Uygulama
{
class Program
{
static void Main(string[] args)
{
WorkflowInvoker.Invoke(WorkflowOlustur());
Console.WriteLine("Press ENTER to exit");
Console.ReadLine();
}

static Activity WorkflowOlustur()
{
Variable<int> NumberOfBells = new Variable<int>()
{
Name = "NumberOfBells",
Default= DateTime.Now.Hour
};

Variable<int> Counter = new Variable<int>()
{
Name = "Counter",
Default = 1
};
return new Sequence()
{
DisplayName = "Main Sequence",
Variables = { NumberOfBells, Counter },
Activities =
{
new WriteLine()
{
DisplayName = "Merhaba",
Text = "Merhaba Dünya"
},
new If()
{
DisplayName = "PM için ayarla",
Condition = ExpressionServices.Convert<bool>
(env => NumberOfBells.Get(env) > 12),
Then = new Assign<int>()
{
DisplayName = "Zili Ayarla",
To = new OutArgument<int>(NumberOfBells),
Value = new InArgument<int>(env => NumberOfBells.Get(env) - 12)
}

},
new While()
{
DisplayName = "Zil Sesi" ,
Condition = ExpressionServices.Convert<bool>
(env => Counter.Get(env) <= NumberOfBells.Get(env)),
Body = new Sequence()
{
DisplayName = "Zil Sesi",
Activities =
{
new WriteLine()
{
Text = new InArgument<string>(env => Counter.Get(env).ToString())
},
new Assign<int>()
{
DisplayName = "Sayacı Arttır",
To = new OutArgument<int>(Counter),
Value = new InArgument<int>(env => Counter.Get(env) + 1)
},
new Delay()
{
Duration = TimeSpan.FromSeconds(1)
}
}
}
},
new WriteLine()
{
DisplayName = "Zamanı Göster",
Text = "Şu anki zaman " + DateTime.Now.ToString()
},
new If()
{
DisplayName = "Selam",
Condition = ExpressionServices.Convert<bool>
(env => DateTime.Now.Hour >= 18),
Then = new WriteLine() { Text = "İyi Akşamlar"},
Else = new WriteLine() { Text = "İyi Günler"}
},
}

};
}
}
}

Bu yazımızda Workflow Foundation yazı serimizin ilk makalelerinde tasarım kısmı ile aktivite oluşturulmasına karşın kod ile nasıl aktivite oluşturulur ve bu aktivitelerin özelliklerinin hangi tür değişkenler ve ifadeler aldıklarını gördük.

Workflow Foundation 4.0 ile Temel Elemanları Eklemek

Workflow Foundation 4.0, bize kullanmak için If, While, Assign, Sequence gibi yönetimsel elemanlar sunar. Bu yazıda bu elemanların nasıl kullanılacağına dair bilgiler vermeye çalışacağım.

WF 4.0 kullanıcıları workflow elemanları tarafından kullanılan tüm değişkenleri tanımlamak zorundadırlar. Şimdi projemize iki adet değişken tanımlayalım. Birinci değişkenimiz kaç adet zile ihtiyacımız olduğunu gösteren NumberOfBells, diğerini de şimdiye kadar kaç defa zil duyulduğunu gösteren Counter. Peki bu değişkenleri nasıl tanımlıyoruz?

Öncelikle projemize bir adet Sequence aktivitesi ekleyelim. Daha sonra bu aktivitenin üzerine bir defa tıklayıp tasarım kısmında sol altta bulunan “Variables” sekmesine tıklayalım. Daha sonra “Create Variable” kısmına tıklayıp ismi Counter olan tipi de Int32 olan bir değişken kaydedelim. Scope kısmını Sequence olarak bırakabilirsiniz. Bunun anlamı; o değişken Sequence ve onun soyundan gelen tüm aktiviteler tarafından kullanılabilir olmasıdır. Aynı şekilde tekrar “Create Variable” diyelim. Bu sefer değişkenimizi Properties penceresinden tanımlayalım. İsmi NumberOfBells ve tipi de Int32 olan bir değişken tanımlayalım. Scope kısmını yeniden Sequence olarak bırakalım.

WF-VariableDefinition

WF-Properties2

Daha sonra ifadeler yazabileceğimiz yukarıdaki resimde kırmızı kutu içerisinde gösterdiğim Expression Editor’ü açalım. Ve oraya DateAndTime.Now.Hour ifadesini yazalım.

WF-ExpressionEditor

Bu ifadeyi default propertie olarak kaydedelim. Bu işlem NumberOfBells değişkenini günün o andaki saat değerine atar. Bu işlemden sonra değişkenlerimiz aşağıdaki şekilde görünmelidir.

WF-Variables3

Şimdide IF kullanımına bakalım;

DateAndTime sınıfının Hour üyesi geriye 24 saatlik sistemde saati geri döndürür. Örneğin; saat öğleden sonra 3 ise geriye 15 değerini döndürür. Öncelikle diagramımızda bulunan Sequence yapısının içine Toolbox’ta bulunan Writeline aktivitesini sürükleyip bırakalım. Daha sonra bu aktivitenin DisplayName özelliğini Merhaba olarak değiştirelim. Tekrar Sequence içerisine bir If aktivitesi sürükleyip bırakalım.

NOT: Bu adımda If ve Sequence aktivitelerinin sağ üst köşelerinde kırmızı renkli hata kutuları çıkacaktır. If aktivitesindeki kutucuk If aktivitesi içerisinde henüz herhangi bir koşul (condition) yazılmadığını, Sequence aktivitesindeki kutucuk ise ya bu aktivitede ya da alt aktivitelerinde bir hata olduğunu gösterir. Bu hataları kutucukların üstüne gelerek görebilirsiniz.

WF-If

Şimdi If aktivitesini Properties kısmından DisplayName özelliğini “PM için ayarla” olarak değiştirelim. If aktivitesinin 3 kısmı vardır. Condition kısmı hangi koşulun hesaplanacağını gösterir. Bir Boolean (true ya da false) değer çözmelidir bu koşul. Eğer ifade True is Then aktivitesi, eğer ifade False ise Else aktivitesi çalıştırılır. Hem Then hem de Else aktivitelerinin ikisini birden tanımlamak zorunda değiliz. Eğer hiçbir aktivite tanımlanmasa, hiçbir aktivite çalıştırılmaz. Şimdi koşulumuza NumberOfBells>12 ifadesini girelim.

Şimdi de Assign aktivitesini Then kısmımızın içine sürükleyip bırakalım. Assign aktivitesi size bir değişkene ya da bir argümana bir değer atamanıza imkan verir. Hem To hem de Value özelliklerinin bir ifade içerebileceğine dikkat edin. To özelliğine NumberOfBells, Value özelliğine de NumberOfBells – 12 ifadelerini girelim.

WF-Properties2

WF 4.0’da bir çok aktivite compound aktivitedir yani başka aktiviteleri içerebilirler. If aktivitesi buna güzel bir örnek olarak gösterilebilir.

Sıra geldi While aktivitesini kullanmaya. While aktivitesini, ismi “PM için ayarla” olan If aktivitesinin altına sürükleyip bırakalım. Ardından DisplayName özelliğini “Zil Çalınma Sayısı” olarak değiştirelim.

WF-While

While aktivitesi, içerisinde yazılan ifade true olduğu sürece Body kısmındaki aktiviteleri çalıştırır. İlk önce koşul hesaplanır, eğer true ise, içindeki aktiviteler çalıştırılır. Bu durum, koşul false olana kadar devam eder.

NOT: İleride göreceğimiz DoWhile aktivitesi ile While aktivitesi arasındaki fark, programlama olduğu gibidir. DoWhile aktivitesinde önce içindeki aktiviteler bir kez çalıştırılır, sonra koşul çalıştırılıp true olup olmadığı kontrol edilir. While aktivitesinde ise, önce koşul çalıştırılır. Koşul true ise içindeki aktiviteler çalıştırılır. Eğer koşul false ise içindeki ifadeler hiçbir zaman çalıştırılmazlar.

While aktivitesinin koşul kısmına Counter <= NumberOfBells ifadesini yazalım. Daha sonra Body kısmına bir Sequence aktivitesini sürükleyip bırakalım. Bunun da DisplayName özelliğini “Zil Çalınma Sayısı” olarak değiştirelim.

WF-Sequence

Şimdi bu “Zil Çalınma Sayısı” isimli Sequence aktivitemizin içine bir Writeline aktivitesi sürükleyip bırakalım. Writeline aktivitesinin Text özelliğine de Counter.ToString() ifadesini yazalım. Bunun amacı kaç kere zil çaldığını konsola yazdırmaktır. Ardından bu Writeline aktivitesinin altına bir Assign aktivitesi yerleştirelim. To kısmına Counter, Value kısmına ise Counter + 1 yazalım. Bu kısaca Counter değişkenini bir arttıracaktır.

WF-Assign2

Ardından bu Assign aktivitesinin altına bir Delay aktivitesi sürükleyip bırakalım. Kısaca Delay aktivitesi, belirli bir zaman periyodu için o aktiviteyi durdurur. Delay aktivitesinin Duration adında tek bir özelliği vardır. Bu özellik aktivitenin ne kadar duracağını gösterir.  Bu özelliğe TimeSpan.FromSeconds(1) ifadesini yerleştirelim.

“Zil Çalınma Sayısı” adındaki While aktivitesini sağ üst köşesinden daraltalım.

WF-Collapse

Sonra da bu While aktivitesinin altına bir Writeline aktivitesi ekleyelim. DisplayName özelliğini “Zaman:” , Text özelliğini de “Şu anki zaman: ” + DateTime.Now.ToString() şeklinde yazalım.

“Zaman” aktivitesinin altına da bir If aktivitesini sürükleyelim ve ismini de “Selamlama” olarak koyalım. Koşul kısmına da DateTime.Now.Hour >= 18 ifadesini yerleştirelim. Şimdi de Selamlama aktivitesinin içindeki Then ve Else kısımlarına da birer Writeline aktivitesi yerleştirelim. Then kısmındaki aktivitenin Text özelliğine “İyi Akşamlar”, Else kısmındaki aktivitenin Text özelliğine de “İyi Günler” yazalım. Şimdi de projemizi kaydedip F5 ile çalıştıralım.

0
1
2
3
4
5
Şu anki zaman: 30.07.2012 17:00:16
İyi Günler
Press any key to continue . . .

 

Görüleceği üzere 0-5 arası her rakamı yazdırırken 1 saniye bekleme yaptı program. Bu durum Delay aktivitesi sayesinde gerçekleşir.

Bu yazıda Workflow Foundation içerisindeki, bizim de programlama dillerinden bildiğimiz ifadelerin aktivitelerini inceledik.

WF 4.0 ile Tasarım Kısmına Daha Derin Bir Bakış

Bir önceki yazımda gördüğünüz üzere, bu kadar basit bir workflow’da bile tüm diagramı görmek ne kadar zor durumlar alabiliyor. İyi ki, tasarım kısmı büyük workflow’larda çalışabilmemiz için bize bir çok özellik sağlıyor. Örneğin; tasarım kısmının sağ üst köşesindeki “Expand All” ve “Collapse All” seçenekleri ile bize görünüm anlamında çok büyük avantajlar sağlar. Collapse All seçeneği ile tüm ana aktiviteleri görmemize olanak verir. Expand All seçeneği ile de tüm aktiviteleri genişletir fakat bu durumda tüm diagramın bir kısmını görebiliriz.

Sağ alt köşedeki “Overview” seçeneğine tıklarsak, tüm diagramı küçük bir pencerede görebiliriz. Bu pencerenin içindeki sarı kutu, bize o anda diagramın içerisinde gösterilmek istenen alanı verir. Bu sarı kutuyu, hangi kısmı görmek istiyorsak o kısma sürükleyebiliriz.

WF-Designer

Şimdi Overview seçeneğini kapatıp hemen yanındaki “Fit to screen” özelliğine bakalım. Bu özellik bize tüm diagramı mümkün olduğunda küçültüp tasarım penceresine sığdırır. Monitör büyüklüğünüze bağlı olarak bu şekilde aktiviteleri okumak zorlaşabilir. Eğer zoom özelliğinin yanındaki büyüteç işaretine tıklarsak bu durumda diagram %100 haline gelip eski haline bu şekilde döndürebiliriz.

Şimdi biraz daha derinlere bakalım;

Solution Explorer kısmından “Workflow1.xaml” dosyamıza sağ tıklayıp “View Code” seçeneğine tıklayalım. Bu adımda bu dosyanın hala açık olduğunu, kapatmak isteyip istemediğinizi soran bir uyarı mesajı alacaksınız. Bu uyarıyı evet diyerek geçelim. Aşağıdaki şekilde .xaml doyamızın kod kısmını göreceğiz;


<Activity mc:Ignorable="sap" x:Class="WF_Temel_Elemanları_Eklemek.Workflow1" sap:VirtualizedContainerService.HintSize="526,1297" mva:VisualBasic.Settings="Assembly references and imported namespaces for internal implementation" xmlns="<a href="http://schemas.microsoft.com/netfx/2009/xaml/activities&quot;">http://schemas.microsoft.com/netfx/2009/xaml/activities"</a> xmlns:mc="<a href="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;">http://schemas.openxmlformats.org/markup-compatibility/2006"</a> xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s1="clr-namespace:System;assembly=System" xmlns:s2="clr-namespace:System;assembly=System.Xml" xmlns:s3="clr-namespace:System;assembly=System.Core" xmlns:s4="clr-namespace:System;assembly=System.ServiceModel" xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:sap="<a href="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation&quot;">http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation"</a> xmlns:scg="clr-namespace:System.Collections.Generic;assembly=System" xmlns:scg1="clr-namespace:System.Collections.Generic;assembly=System.ServiceModel" xmlns:scg2="clr-namespace:System.Collections.Generic;assembly=System.Core" xmlns:scg3="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sd="clr-namespace:System.Data;assembly=System.Data" xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" xmlns:st="clr-namespace:System.Text;assembly=mscorlib" xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml&quot;">http://schemas.microsoft.com/winfx/2006/xaml"</a>>
<Sequence sad:XamlDebuggerXmlReader.FileName="c:\users\soner\documents\visual studio 2010\Projects\WF-Temel Elemanları Eklemek\WF-Temel Elemanları Eklemek\Workflow1.xaml" sap:VirtualizedContainerService.HintSize="486,1257">
<Sequence.Variables>
<strong><Variable x:TypeArguments="x:Int32" Name="Counter" />
</strong>      <strong><Variable x:TypeArguments="x:Int32" Default="[DateAndTime.Now.Hour]" Name="NumberOfBells" /></strong>
</Sequence.Variables>
<sap:WorkflowViewStateService.ViewState>
<scg3:Dictionary x:TypeArguments="x:String, x:Object">
<x:Boolean x:Key="IsExpanded">True</x:Boolean>
</scg3:Dictionary>
</sap:WorkflowViewStateService.ViewState>
<WriteLine DisplayName="Merhaba" sap:VirtualizedContainerService.HintSize="464,59" />
<strong><If Condition="[NumberOfBells &gt; 12]" DisplayName="PM için ayarla" sap:VirtualizedContainerService.HintSize="464,201">
</strong>      <sap:WorkflowViewStateService.ViewState>
<scg3:Dictionary x:TypeArguments="x:String, x:Object">
<x:Boolean x:Key="IsExpanded">True</x:Boolean>
<x:Boolean x:Key="IsPinned">False</x:Boolean>
</scg3:Dictionary>
</sap:WorkflowViewStateService.ViewState>
<If.Then>
<Assign sap:VirtualizedContainerService.HintSize="291,100">
<Assign.To>
<OutArgument x:TypeArguments="x:Int32">[NumberOfBells]</OutArgument>
</Assign.To>
<Assign.Value>
<InArgument x:TypeArguments="x:Int32">[NumberOfBells - 12]</InArgument>
</Assign.Value>
</Assign>
</If.Then>
</If>
<strong><While DisplayName="Zil Çalınma Sayısı" sap:VirtualizedContainerService.HintSize="464,453">
</strong>      <sap:WorkflowViewStateService.ViewState>
<scg3:Dictionary x:TypeArguments="x:String, x:Object">
<x:Boolean x:Key="IsExpanded">True</x:Boolean>
<x:Boolean x:Key="IsPinned">False</x:Boolean>
</scg3:Dictionary>
</sap:WorkflowViewStateService.ViewState>
<strong><While.Condition>[Counter &lt;= NumberOfBells]</While.Condition>
</strong>      <strong><Sequence DisplayName="Zil Çalınma Sayısı" sap:VirtualizedContainerService.HintSize="438,342">
</strong>        <sap:WorkflowViewStateService.ViewState>
<scg3:Dictionary x:TypeArguments="x:String, x:Object">
<x:Boolean x:Key="IsExpanded">True</x:Boolean>
<x:Boolean x:Key="IsPinned">False</x:Boolean>
</scg3:Dictionary>
</sap:WorkflowViewStateService.ViewState>
<strong><WriteLine sap:VirtualizedContainerService.HintSize="242,59" Text="[Counter.ToString()]" /></strong>
<Assign sap:VirtualizedContainerService.HintSize="242,57">
<strong><Assign.To>
<OutArgument x:TypeArguments="x:Int32">[Counter]</OutArgument>
</Assign.To>
<Assign.Value>
<InArgument x:TypeArguments="x:Int32">[Counter + 1]</InArgument>
</Assign.Value>
</strong>        </Assign>
<strong><Delay Duration="[TimeSpan.FromSeconds(1)]" sap:VirtualizedContainerService.HintSize="242,22" />
</strong>      </Sequence>
</While>
<strong><WriteLine DisplayName="Zaman:" sap:VirtualizedContainerService.HintSize="464,59" Text="[&quot;Şu anki zaman: &quot; + DateTime.Now.ToString()]" /></strong>
<strong><If Condition="[DateTime.Now.Hour &gt;= 18]" DisplayName="Selamlama" sap:VirtualizedContainerService.HintSize="464,201">
</strong>      <If.Then>
<strong><WriteLine sap:VirtualizedContainerService.HintSize="219,100" Text="İyi Akşamlar" />
</strong>      </If.Then>
<If.Else>
<strong><WriteLine sap:VirtualizedContainerService.HintSize="220,100" Text="İyi Günler" /></strong>
</If.Else>
</If>
</Sequence>
</Activity>

Burada gördüğünüz üzere aktiviteleri bold olarak işaretledim. Ayrıca burdaki koda baktığımızda hiç çalıştırılabilir bir kod göremeyiz. Bu .xaml dosyası içerisinde sadece iç içe yuvarlanmış özellikler görüyoruz. Normalde Counter değişkeni için Counter = Counter + 1 şeklinde bir kod satırı görmeliydik. Bunun yerine elimizde bir Assign sınıfı ve bu sınıf içerisinde Counter ve Counter + 1 ifadeleri mevcut. Burada aslında Assign sınıfı içerisinde bu ifadeler çalıştırılır. Workflow tanımında herhangi bir ifade belirtmezler.

Workflow Foundation 4.0’a Giriş

.NET 4.0 içerisinden Workflow Foundation (ayrıca WF 4.0 olarak anılır) workflow tabanlı uygulamaların geliştirilmesi için yeni bir örnek ifade eder.

Şimdi basit bir workflow oluşturma ile işe başlayalım. Visual Studio 2010’u çalıştırıp New Project linkine tıklayalım. Installed Templates’in altında, Visual C#’ın içerisinde, 4 adet farklı template’in var olduğunu göreceğiz. Buradan Windows Console Application’ı seçelim ve projemizin ismini HelloWF olarak adlandıralım.

HelloWF

Bu adım bize bir adet konsol uygulaması için Program.cs  ve Workflow1.xaml adında workflow’unuzdaki aktiviteleri tanımlayan dosyalar üretilir. XAML dosyası, iş akışı tanımınızdaki aktivite-türetilen elementleri içerir. Visual Studio 2010 size bu tür elementleri düzenlemeniz için grafiksel olarak bir arayüz imkanı tanır. Burada klasik bir VS 2010 arayüzünü görüyoruz. Sağ tarafta Solution Explorer, Properties, sol tarafta Toolbox, Server Explorer, alt tarafta ise Errors, Warnings ve Messages kısımlarını görmekteyiz (Tabi bu kısımların IDE içerisinde yerlerinin kişiden kişiye değişebileceğini unutmayalım.)

WF-IDE

Orta tarafta ise WF tasarım kısmını görüyoruz. Sağ altta zoom için gerekli olan seçenekleri ve Overview seçeneklerini görüyoruz. Zira Workflow Foundation içerisinde oluşturabileceğiniz aktiviteler çok büyük olabileceği için bu kontroller bize büyük resim içerisinde küçük resmi görebilmek için çok büyük avantaj sağlayacaklardır. Sol alt tarafta ise 3 adet kontrolümüz var. “Variables”, “Arguments” ve assembly dosyalarını içeren “Imports”. Variables kısmına tıkladığımızda var olan değişkenleri içeren bir pencere açılacaktır.

WF-Variables

Eğer oluşturduğunuz workflow’u bir sınıf olarak düşünürsek, variables kısmı sınıf üyeleri olarak düşünebiliriz. Bunlarla aktiviteler arasındaki paylaşılan verileri tutabilirsiniz. Bu değişkenleri isterseniz tüm workflow için, isterseniz belirli bir aktivite için tanımlayabilirsiniz. Arguments kısmı tıpkı değişkenler gibidir, farklı olarak workflow dışına veri geçisini sağlayabilirler. Bunları method parametreleri olarak düşünebiliriz. Not olarak; buradaki Direction kolonu, verinin nereye geçtiğini ya da workflow dışında nereye gönderildiğini gösterir.

WF-Arguments

 

~Workflow Tasarımı~

Workflow’u ilk oluşturduğumuzda tasarım kısmının boş olduğunu görürüz. Aktiviteleri sürükle bırak mantığı ile workflow’umuza ekleriz. Şimdi Workflow Foundation içerisinde küçük bir “Hello World” uygulaması yaratalım. Başlangıç için, Sequence aktivitesini tasarım alanına sürükleyip bunun içerisinde Writeline aktivitesini sürükleyelim. Diagram aşağıdaki şekilde görünecektir;

WF-Sequence-Writeline

Ayrıca Properties kısmı aşağıdaki şekilde görünür.

WF-Properties

Buradaki DisplayName özelliğini Hello şeklinde değiştirelim ki, bir çok Writeline aktivitemizin olduğu diagramlarda bu yapıları hatırlamamız kolaylaşsın. Ayrıca Text özelliğine “Hello, World!” kelimelerini giriş yapalım. Tasarım kısmında da aynı anda Writeline aktivitesinin içerisinde bu stringi görebiliriz. TextWriter özelliğini boş bırakabiliriz. Default olarak, bu text konsola yazılacaktır. Eğer spesifik bir uygulama yapmak isterseniz buraya TextWriter’dan türetilmiş sınıf yapılarını da yazabiliriz. Şimdi de Program.cs dosyamızda değişiklikler yapalım. Bu dosyayı ilk açtığımızda aşağıdaki şekilde görürüz;


using System;
using System.Linq;
using System.Activities;
using System.Activities.Statements;

namespace HelloWF
{

class Program
{
static void Main(string[] args)
{
WorkflowInvoker.Invoke(new Workflow1());
}
}
}

Statik WorkflowInvoker sınıfı Workflow1 sınıfı tarafından tanımlanmış workflow’u başlatır. Şimdi Main() methodumuzun içerisine aşağıdaki kodları ekleyelim;

Console.WriteLine(“ENTER to exit”);
Console.ReadLine();

Daha sonra projemizi F5 ile çalıştırdığımızda, aşağıdaki şekilde bir konsol çıktımız olur.

Hello, World!
ENTER to exit

Bu makalede Windows Workflow Foundation’a giriş kısmını anlatmaya çalıştım. Umarım bundan sonra WF 4.0 ile ilgili açıklayıcı makaleler yazabilirim.