Tag Archives: workflow

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’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.