Makale Özeti

Daha önceki Yerel İletişim Servisleri yazısında WorkflowInstace’ları ile Workflow “Sunucu(Host) Uygulamaları” arasındaki haberleşmeyi sağlayan “Yerel İletişim Servisleri(Local Communication Services)”ni nasıl tanımlanacağını ve nasıl kullanılacağını öğrenmiştik. Bu bölümde ise “Yerel İletişim Serivisleri” tarafından tetiklenen olayları yakalamaya yarayan “Olaya Dayalı(Event-Driven) Aktiviteler” konusunu öğreniyor olacağız. Bu yazı içerisinde “Olaya Dayalı Aktiviteler”i pekiştirmek için bir “Anket Uygulaması” yapacağız.

Makale

Olaya Dayalı Aktiviteler(Event-Driven Activities)

Daha önceki Yerel İletişim Servisleri yazısında WorkflowInstace’ları ile Workflow “Sunucu(Host) Uygulamaları” arasındaki haberleşmeyi sağlayan “Yerel İletişim Servisleri(Local Communication Services)”ni nasıl tanımlanacağını ve nasıl kullanılacağını öğrenmiştik. Bu bölümde ise “Yerel İletişim Serivisleri” tarafından tetiklenen olayları yakalamaya yarayan “Olaya Dayalı(Event-Driven) Aktiviteler” konusunu öğreniyor olacağız. Bu yazı içerisinde “Olaya Dayalı Aktiviteler”i pekiştirmek için bir “Anket Uygulaması” yapacağız.

 

Olaya Dayalı(Event-Driven) Aktiviteleri Anlamak

“Olaya Dayalı Aktiviteler” Workflow’u  “Yerel İletişim Servisleri”nden bir olay fırlatılana kadar bekletmeye yarayan aktivite türleridir. Daha önceki yazıda CallExternalMethodActivity ile “Yerel İletişim Servisleri” içerisindeki bir metodun nasıl çağrılacağını öğrenmiştik. “Olaya Dayalı Aktiviteler”, WorkflowInstance ile “Sunucu uygulaması” arasındaki iletişimi iki şekilde sağlayabileceğimiz üzeride durmuştuk. Bunlardan birincisi metotlar aracılığıyla, ikincisi ise olaylar aracılığıyla. Metotlar aracılığıyla haberleşirken CallExternalMetotActivity’den faydalanırken, iletişimi olaylar aracılığıyla sağlamak için HandleExternalEvent aktivitesinden faydalanacağız. Yapı olarak .Net Framework 3.0 ile sağlanmış IEventActivty ara yüzünü(interface) işlemiş(implement) olan herhangi bir aktivite, iş akışı içerisindeki olayları yakalayabilir. Diğer yandan EventDriven aktivitesini kullanarak olay yakalama işlemini alt aktivite serisi içerisinden yapmak mümkün. Bu işi yapabilmesi için EventDriven aktivitesi, içerisinde bulundurduğu alt aktivitelerden ilkinin IEventActivity ara yüzünü işlemiş olması yeterlidir. ListenActivity içerisinde alt aktivite olarak birden fazla EventDriven aktivitesi barındırabilen Composite olarak adlandırılan aktivitelerden birisidir. Bazı durumlarda belirli bir süre “Yerel İletişim Serivisi”nden olay fırlatılmaz ise işlemi iptal etmemiz gerekebilir. Bu durumda Listen aktivitesi kullanımı bizim için en uygun çözüm olacaktır. Event-Driven aktiviteler Sequence Workflow’lar içerisinde Listen aktivitesi ile kullanılabildiği gibi State Machine Workflow’lar içerisinde de kullanılabilirler. Vurgulamak gerekirse Event-Driven aktiviteler State Machine Workflow’lar için vazgeçilmezlerdir.

 

Olaya Dayalı(Event-Driven) Aktivitelerin Implementasyonu

1.     “Yerel İletişim Servisleri” içerisinde olayları tetiklerken EventArgs tipinde parametrelere ihtiyaç duyarız. Tetiklediğimiz olayların HandleExternalEvent aktivitesi tarafından yakalanabilmesi için parametrelerin EventArgs tipinde değil de ExternalDataEventArgs tipinde olması gerekmektedir. Bunun için bir sınıf tanımlayıp, bu sınıfı ExternalDataEventArgs sınıfından miras aldırmamız gerekmektedir.

2.    “Yerel İletişim Servisleri” tarafından işlenecek olan ve ExternalDataExchangeAttribute ile işaretlenmiş bir ara yüz(interface) tanımlamamız gerekmektedir. Bu ara yüz içerisinde bir olay tanımlayarak iletişim sağlama yolunu oluşturmalıyız.

3.    Bir önceki adımda tanımladığımız ara yüzü işleyen “Yerel Servis” işlemlerini gerçekleştirecek bir sınıf tanımlamalıyız.

4.    “Yerel Servis” içerisinde olayı tetikleyecek bir mekanizma oluşturmalıyız.

5.    Workflow içerisinde Listen aktivitesi eklemeliyiz. Listen aktivitesini ekledikten sonra, Listen aktivitesi altında iki tane EventDriven aktivitesi oluşacaktır. EventDriven aktivitesinin ilk bölümünün IEventActivity ara yüzünü işlemiş olma zorunluluğunu da unutmamamız gerekir.

6.    Listen aktivitesinin içerisinde bulunan birinci EventDriven aktivitesi içerisine HandleExternalEvent aktivitesi eklemeliyiz. Bu aktiviteyi tetiklenen olayı yakalamak için kullanacağız. HandleExternalEvent aktivitesinin “Interface” özelliğine tanımladığımız “Yerel Servis” ara yüzünü değer olarak vermeliyiz ve “Event” özelliğine de “Yerel Servis” ara yüzü içerisinde tanımladığımız olaylardan birini atamalıyız.

7.    Eğer diğer EventDriven aktivitesini kullanmak istemiyorsanız, Delay aktivitesini kullanabilirsiniz.

 

Proje Açıklaması

Bu konuyu bir “Anket Uygulaması” yaparak öğreneceğiz. “Sunucu Uygulaması” “Yerel Servis” aracılığıyla “İş akışı” ile haberleşecek.

Bu uygulamada tanımlayacağımız “Yerel Servis” içerisinde “Soru Alındı”, “Sonuç Alındı”, “Soru Talep Edildi” ve “Soru Cevaplandı” olayları bulunacaktır. Bunun yanı sıra “Soru iste”, “Soru Cevabı Gönder”, “Soru Yolla”, “Sonuç Yolla” gibi metotları içermelidir. Buradaki akış şöyle gerçekleşecektir:

  • “Sunucu uygulaması” “Yerel Servis” içerisindeki “Soru İste” metodunu çağırır.
  • “Yerel Servis” üzerinde çağrılan metot aracılığı ile “İş akışı”na bildirim “Soru Talep Edildi” olayı üzerinden yapılır.
  • “İş akışı” HandleExternalEvent aktivitesi ile gelen olayı yakalar ve CallExternalMethod aktivitesi ile “Yerel Servis” üzerindeki “Soru Yolla” metodunu çağırır.
  • “Yere Servis”in “Soru Yolla” metodunun çağrılması sonucu “Sunucu Uygulaması”na bildirim “Soru Alındı” olayı ile yapılır.
  • Soruyu alan “Sunucu Uygulaması” içerisinde soru cevaplanır ve “Yerel Servis” üzerindeki “Soru Cevabı Gönder” metodu çalıştırılır.
  • “Yerel Servis” üzerindeki “Soru Cevabı Gönder” metodu çalıştırıldığında, “İş akışı” tarafında “Soru Cevaplandı” olayı yakalanacaktır.
  • Bu işlemler cevaplanacak soru kalmayana kadar devam edecek ve soruların cevaplanması bittiğinde “İş Akışı” tarafından “Yerel Servis” içerisindeki “Sonuç Yolla” metodu çağrılacaktır.
  • “Yerel Servis” içerisindeki “Sonuç Yolla” metodu çağrıldığında ise  “Sunucu Uygulaması” içerisinde “Sonuç Alındı” olayı tetiklenecek. Böylelikle “Sunucu Uygulaması” tetiklenen olayı yakalayarak kullanıcıya kaç puan aldığını söyleyecek.

Aşağıdaki bölümde olayların ve metotların hangi uygulamaya ait olduğunu belirtiyor.

SoruTalepEdildi: İş akışı

SoruCevaplandı: İş akışı

SoruYolla: İş akışı

SonucYolla: İş akışı

SoruAlindi: Sunucu uygulaması

SonucAlindi: Sunucu uygulaması

SoruIste: Sunucu uygulaması

SonucCevabıGonder: Sunucu uygulaması

 

Yerel Servis Tanımlaması

“Yerel Servis” tanımlamasını yapmak için boş bir “Solution” başlatın. “Solution” içerisine “Class Library” tipinde, adı “AnketServiceLib” olan bir proje ekleyin. Projenize “System.Workflow.Runtime” ve “System.Workflow.Activities” referanslarını ekleyin.

“Yerel İletişim Servisleri” bölümünde öğrendiğimiz gibi ilk olarak “ExternalDataExchangeAttribute” ile imzalanmış bir ara yüz oluşturmamız gerekiyor. Bu ara yüz içerisine sadece Workflow tarafında ihtiyacımız olan metotlar ve olayları tanımlamamız gerekiyor. Projenize IAnketService adında bir ara yüz ekleyin.

“Sunucu Uygulaması”na Soru sınıfı göndererek Cevaplara, Soru Metnine erişimi sağlayacağız. Soru sınıfının Serializable olarak işaretlenmesi gerekiyor. Projenize Soru adında bir sınıf ekleyin.

“IAnketService” tanımamasını yaptıktan sonra, bu ara yüzü işleyen bir sınıfa ihtiyacımız olacak. IAnketService içerisinde “İş Akışı” içerisinde ihtiyacımız olan olayları tanımlamıştık. “AnketService” sınıfı içerisinde hem “IAnketService” ara yüzü ile sağlanan üyeler, hem de “SoruCevabıGonder”, “SoruIste” metotlar ve “SoruAlindi”, “SorucAlindi” gibi olaylar yer alacak. Projeniz içerisine “AnketService” adında bir sınıf ekleyin.

“AnketService” sınıfı içerisinde tetiklenecek olayların ihtiyaç duyduğu olay argümanlarını da tanımlamamız gerekmektedir. Projenize “SonucAlindiEventArgs”, “SoruTalepEdildiEventArgs”, “SonucTalepEdildiEventArgs”, “SoruAlindiEventArgs” ve “SoruCevaplandiEventArgs” adlarında sınıflar ekleyin.

“Yerel Servis”i kodlamaya Soru sınıfından başlayalım. Soru sınıfını Serializable olarak işaretleyin. Soru sınıfı içerisinde “SoruMetni”, “Cevaplar” ve “DogruCevapIndex” adında 3 özellik ekleyin.

“Sunucu Uygualması” ile “İş Akışı” uygulaması arasındaki kontrat ilişkisini sağlayacak “IAnketService” ara yüzünü tanımlayalım.

“SonucAlindiEventArgs” ismiyle tanımladığınız sınıfı kodlayalım. Bu sınıfı ilk önce Serializable olarak işraretlememiz gerekmektedir. “Yerel Servis” içerisindeki olay mekanizmasında kullanabilmemiz için Sytem.Workflow.Activities isimalanı altında buluanan ExternalDataEventArgs sınıfından miras aldırmamız gerekiyor. “Sunucu Uygulaması” “SonucAlindi” olayını yakaladığında, fırlatılan olay ile birlikte gelen “SonucAlindiEventArgs” ile “sonuc” nesnesini de almış olacak. Ayrıca ExternalDataEventArgs sınıfını miras aldığımızda, Guid tipindeki bir parametre ile ExternalDataEventArgs sınıfının yapıcı metodunu kullanmalıyız. Bu zorunludur.

“SoruTalepEdildiEventArgs” sınıfını kodlayalım.

“SonucTalepEdildiEventArgs” sınıfını kodlayalım.

“SoruAlindiEventArgs” sınıfını kodlayalım. Burada “Sunucu Uygulaması” “SoruAlindi” olayını yakaladığında hangi sorunun “İş Akışı” tarafından gönderildiğini de öğrenmiş olacak.

“SoruCevaplandiEventArgs” sınıfını kodlayalım.

Buraya kadar olan aşamada “IAnketService” adındaki ara yüzümüzü tanımladık ve IAnketService ara yüzünü işleyecek olan “Yerel Servis” içerisinde tetiklenecek olayların olay argümanlarını tanımladık. Artık “Yerel Servis”imizi de tanımlayacak aşamaya geldik. Tanımadığınız “AnketService” isimli sınıfı açın ve “IAnketService” ara yüzünü işleyin.

“IAnketService” ara yüzünü işlediğinizde “SoruYolla”, “SonucYolla” adında iki metot ve “SoruTalepEdildi”, “SoruCevaplandi” adında iki olay tanımlandığını göreceksiniz. Şimdi bu metotların içlerini aşağıdaki gibi kodlayalım. Yukarıdaki grafiği incelediğinizde “SoruYolla” metodu çalıştırıldığında, “Sunucu Uygulaması” tarafında “SoruAlindi” olayının yakalandığını göreceksiniz. “SonucYolla” metodu çalıştırıldığında ise “Sunucu Uygulaması” tarafında “SonucAlindi” olayının yakalandığını göreceksiniz.

“AnketService” sınıfı içerisinde ara yüz ile ilgili üyelerin kodlamasını yaptıktan sonra “Sunucu Uygulaması” tarafını da sınıf içerisinde kodlamamız gerekiyor. Bunun için “SoruAlindi” ve “SonucAlindi” adında iki olay tanımlamalıyız.

Olayları tanımladıktan sonra “SoruIste” ve “SoruCevabiGonder” metotlarını tanımlayın.

Böylelikle “AnketService” sınıfını da tanımlayarak, AnketServiceLib adlı projenin kodlama işlemini bitirmiş oluyoruz.

 

İş Akışı Tanımlaması

Şimdiki aşamada “Solution”ımıza “WorkflowLibrary1” adlı bir “Seqential Workflow Library” ekleyerek iş akışımızı kodlayabileceğiz. Projemiz içerisine “AnketSericeLib” adlı referansı ekleyelim. Eklediğimiz “Sequential Workflow Library” ile gelen “Workflow1” adlı iş akışını kullanacağız. Bu iş akışını kullanırken bazı sınıf seviyesindeki değişkenlere ihtiyaç duyacağımızdan ilk önce “Workflow1”in kod bölümüne geçelim ve aşağıdaki sınıf seviyesindeki değişkenleri tanımlayalım.

Soru koleksiyonu olan “Sorular” özelliği iş akışı içerisinden “Sunucu Uygulaması”na yollanacak sorular olacaktır. “Cevaplar” ise “Sunucu Uygulaması” tarafından iş akışımıza gelecek olan cevaplarıdır. “CurrentIndex” özelliği kullanıcıya “Sorular” koleksiyonu içerisindeki kaçıncı sıradaki sorunun gönderileceğine karar verirken kullanacağımız özelliktir. “CurrentSoru” özelliği kullanıcıya gönderilecek sorudur. “Sonuc” özelliği ise kullanıcı soruları cevaplandırmayı bitirdiğinde, kullanıcıya kaç puan aldığını bildirmek için kullanacağımız özelliktir.

“Workflow1” adlı iş akışının örneği oluşturulduğunda yukarıda tanımladığımız sınıf seviyesindeki değişkenleri ilklendirmemiz gerekiyor. Bu yüzden “Workflow1” içerisinde “InitializeObjects” adında bir metot tanımlayalım ve bu metodu “Workflow1” sınıfının “Initialized” olayında çağıralım.

Burada 3 tane soru oluşturuyorum. Oluşturduğum soruları “Sorular” adlı “Soru” koleksiyonuna ekliyoruz. Bunun haricinde “Cevaplar” koleksiyonunun örneğini oluşturuyoruz, “CurrentIndex” özelliğinin değerini “0” olarak ve “Sonuc” özelliğinin değerini de “0” olarak belirleyip, ilklendirme işlemini sonlandırıyoruz.

Yukarıdaki işlemleri yaptıktan sonra “Workflow1” içerisine bir tane “While” aktivitesi ekliyoruz. “While” aktivitesinin “Condition” özelliğini “DeclarativeRuleCondition” olarak belirliyoruz. Daha sonra “Expression” özelliğinden “this.CurrentIndex < this.Sorular.Count” kuralını ekliyoruz.

“While“ aktivitesi içerisinde bir tane “Listen” aktivitesi ekleyin ve “Listen” aktivitesi ile gelen iki “EventDriven” aktivitesinin içlerine “HandleExternalEvent” aktivitesi sürükleyip bırakıyoruz. “HandleExternalEvent” aktivitesinin ilkini “SoruTalepEdildi” olarak isimlendiriyoruz. Diğerini ise “SoruCevaplandi” olarak adlandırıyoruz.

 

“SoruTalepEdildi” adlı “HandleExternalEvent” aktivitesinin “InterfaceType” özelliğinin yanındaki butona tıklıyoruz. Açılan pencereden “IAnketService” ara yüzünü seçiyoruz.

Daha sonra “SoruTalepEdildi” aktivitesinin “EventName” özelliğini “SoruTalepEdildi” olarak belirliyoruz. Son olarak “Invoked” özelliğine “soruTalepEdildi_Invoked” değerini veriyoruz.

Böylelikle ilk “HandleExternalEvent” aktivitemizi tanımlamış olduk. Şimdi “SoruCevaplandi” olarak adlandırdığımız “HandleExternalEvent” aktivitesini tanımlayacağız. Yine “SoruCevaplandi” aktivitesinin “InterfaceType” bölümüne gelerek “IAnketService” ara yüzünü seçiyoruz. “EventName” özelliğini “SoruCevaplandi” olarak belirliyoruz ve “Invoked” özelliğini “soruCevaplandi_Invoked” olarak belirleyerek, “SoruCevaplandi” “HandleExternalEvent” aktivitesinin tanımlama işlemini bitirmiş oluyoruz.

Bu aşamadan sonra “SoruTalepEdildi” aktivitesinin alt bölümüne “soruYolla” olarak adlandıracağımız “CallExternalMethod” aktivitesi ekliyoruz.

“SoruYolla“ aktivitesinin “InterfaceType” özelliğini “IAnketService” olarak belirliyoruz. “MethodName” özelliğini “SoruYolla” olarak belirliyoruz ve “s” olarak gözüken parametre bölümünün yanındaki butona tıklıyoruz. Açılan pencereden “CurrentSoru” adlı özelliği seçiyoruz.

Seçim işlemini yaptıktan sonra “SoruYolla” “CallExternalMethod” aktivitesini tanımlamış oluyoruz. Aktiviteyi düzgün tanımladığınızdan emin olmak için aktivite üzerine tıklayarak özelliklerine gelin ve özellikler penceresinin aşağıdaki gibi olduğunu kontrol edin.

“While” aktivitesinin dışına geliyoruz ve “caSonucHesapla” adında bir “Code” aktivitesi ekliyoruz. “Code” aktivitesinin “ExecuteCode” özelliğini “SonucHesapla” olarak belirliyoruz. Son olarak “caSonucHesapla” aktivitesinin altına “sonucYolla” adında “CallExternalMethod” aktivitesi ekliyoruz. “sonucYolla” aktivitesinin “InterfaceType” özelliğini “IAnketService” olarak belirliyoruz, “MethodName” özelliğini “SonucYolla” ve “sonuc” özelliğinin yanındaki butona tıklayarak “Sonuc” olarak belirliyoruz.

Böylelikle “sonucYolla” aktivitesinin özellikler penceresine geldiğimizde aşağıdaki ekran görüntüsüne sahip oluyoruz.

Bu işlemlerin hepsini yaptığınızda “Workflow1”in görüntüsü aşağıdaki gibi olacaktır.

“İş Akışı” uygulamamızı kodlamaya devam edelim. Şimdi “SoruTalepEdildi” olayı iş akışı tarafından yakalandığında çalışacak metodu kodlayalım. Burada kullanıcıya gönderilecek soruyu belirliyoruz.

“SoruCevaplandi” olayı yakalandığında çalışacak metodu kodlayalım. Burada “Sunucu Uygulaması” tarafından gelen “SoruCevaplandıEventArgs” parametresi içerisindeki “CevapIndex” adlı değeri “Cevaplar” koleksiyonuna ekliyoruz ve kullanıcıya gönderilecek soru sırasını arttırıyoruz.

“Code” aktivitesi tipinde olan “caSonucHesapla” adlı aktivitenin kodlamasını yapıyoruz. Burada soruların doğru sıraları ile kullanıcının verdiği cevap sıralarını karşılaştırıyoruz. Eğer birbirine eşit olan varsa kullanıcının puanını 10 arttırıyoruz.

Bu işlemlerin hepsini yaptıktan sonra “Anket uygulaması”nın “İş Akışı” bölümünü de kodlamış olduk.

 

Sunucu Uygulaması Tanımlaması

“Solution”ınız içerisine bir tane “Windows Forms Application“ türünde ve adı “WindowsFormsApplication1” olan bir proje ekleyin. Bu projenin referanslarına “System.Workflow.Runtime”, “System.Workflow.Activities”, “System.WorkflowServices”, “System.Workflow.ComponentModel”, “WorkflowLibrary1”, “AnketServiceLib” referanslarını ekliyoruz. Başlattığınız proje içerisinde bulunan “Form1”de aşağıdaki ifadeleri kullanarak isimalanlarını ekliyoruz.

“Form1” içerisindeki sınıf seviyesindeki değişkenleri tanımlayın.

“runtime” değişkenini “Workflow”u sunmak için kullanacağız. “service” değişkenini “Yere Servis” ile haberleşmek için kullanacağız. “instanceId” değişkenini “Yerel Servis”e hangi iş akışını çalıştırdığımızı haber vermek için kullanacağız. “Form1”in “Load” olayında aşağıdaki kodları yazıyoruz.

“WorkflowRuntime” nesnesini oluşturduktan sonra, “ExternalDataExchangeService” tipinde “exServ” adında bir nesne oluşturuyoruz. Oluşturduğumuz “exServ” nesnesini “runtime” nesnemize “AddService” metodunu kullanarak ekliyoruz. Daha sonra “AnketService” tipinde “service” nesnesi tanımlıyoruz ve “service” nesnesini “Yerel Servis”lerden biri olan “exServ” nesnesine “AddService” metodunu kullanarak ekliyoruz. Kendi tanımladığımız “AnketService” sınıfının “SoruAlindi” ve “SonucAlindi” olaylarına abone oluyoruz. Son olarak “Workflow1” sınıfının örneğini oluşturarak “instanceId” değişkenimize değer atıyoruz, “instance” nesnesini başlatıyoruz ve “service” nesnesinin “SoruIste” metodunu kullanarak “Yerek Servis”ler aracılığıyla “İş Akışı” içerisindeki sorulardan birini istiyoruz.

.Net 2.0 ile gelen Threadler arası güvenliği sağlayan yenilik bazı noktalarda bizim işimizi zorlaştırır oldu. Örneğin WorkerThread üzerinden ana thread üzerindeki bir kontrole erişim “Cross Thread Operation” olarak karşımıza çıkmaktaydı. Bu sorunu çözmek için genelde formun “Load” olayında “Control.IllegalCrossThreadCalls = false;” gibi bir ifade kullanmamız gerekiyordu. Ya da bunu kullanmak yerine, bir delege aracılığıyla “Form” sınıfının “Invoke” metodunu kullanarak işi güvenli hale getirebiliyoruz. Biz burada delegate aracılığıyla ana thread üzerindeki bir kontrole erişim yapmış olduk. Yaptığımız iş ise “İş Akışı” tarafından soruyu almak ve ekranda göstermek.

“Button1”e tıkladığımızda kullanıcının seçmiş olduğu cevabı “Yerel Servis” aracılığıyla “İş Akışı”na göndermek ve daha sonra “İş Akışı”ndan yeni soruyu istemek.

“İş Akışı” üzerinde gönderilecek soru kalmadığında bize sonucu yollayacaktır. Yukarıdaki kod bloğu ile sonuç yollandığında kullanıcıya “MessageBox” aracılığıyla sonucu göstermek.

Formumuz kapanırken runtime nesnesini bellekten kaldırılabilir hale getiriyoruz.

 

Test

Uygulamayı F5 tuşuna basarak çalıştırın ve karşınıza çıkan soruları teker teker cevaplayın. Son soruyu cevapladığınızda puanınızı görüyor olacaksınız.

 

Özet

Bu yazı içerisinde “State Machine Workflow” konusuna geçiş niteliğinde olan “Event-Driven” aktivite kullanımını gördük. Bununla birlikte “Anket uygulaması” geliştirerek “CallExternalMethod”, ”Listen”, “HandleExternalEvent” aktivitelerinin kullanımlarını da görmüş olduk.