Makale Özeti

Workflow Hosting yazı dizimin 2. bölümünde kendi workflow host kütüphanenizi oluşturmanızı sağlayacağım. Birinci bölümde basit bir uygulama ve bu uygulamayı host edecek bir uygulama yapmıştım. Host uygulamasının çalışma şeklini inceleyerek dezavantajları ortaya koymuştum. İkinci bölümde ise bu dez avantajları tamamen ortadan kaldıran bir yöntemi sizlerle paylaşacağım.

Makale

Workflow Hosting - 2

Workflow Hosting yazı dizimin 2. bölümünde kendi workflow host kütüphanenizi oluşturmanızı sağlayacağım. Birinci bölümde basit bir uygulama ve bu uygulamayı host edecek bir uygulama yapmıştım. Host uygulamasının çalışma şeklini inceleyerek dezavantajları ortaya koymuştum. İkinci bölümde ise bu dez avantajları tamamen ortadan kaldıran bir yöntemi sizlerle paylaşacağım.

 Geliştireceğimiz Kütüphaneye Genel Bakış

 Bu yazımda anlatacağım kütüphane yapısı, Bruce Bukovics’in workflow uygulamalarını host etme yaklaşımıdır. Windows Workflow Foundation ile ilgili kitapları karıştırırken bu yaklaşımı gördüm ve sizlerin de kendi uygulamalarınızı geliştirirken bu kütüphaneyi kullanmanızı tavsiye ederim.

Kütüphane 3 sınıftan oluşuyor.

1-     WorkflowInstanceWrapper: Her instance’ı WorkflowRuntime içerisinde izole eden sınıftır. Böylelikle Aynı WorkflowRuntime içerisinde farklı parameterelerle çalışabilen farklı workflowlar host edebileceğiz. Aşağıdaki diyagram WorkflowInstanceWrapper sınıfına aittir.



 2-     WorkflowLogEventArgs:    WorkflowRuntime içerisinde bulunan WorkflowInstance larının ve WorkflowRuntime’ın durumlarını loglarken fırlatacağımız olay için gerekli bir sınıf.

 

 3-     WorkflowRuntimeManager: Yapı içerisindeki en önemi sınıf olan WorkflowRuntimeManager’ı, içerisinde bulundurduğu WorkflowInstanceWrapper nesnelerini yönetmek için kullanacağız.

 

Kütüphanemizi Geliştirmeye Başlayalım

Kütüphanemizi geliştirmek için ilk yapmanız gereken bir tane Empty Workflow Project Başlatmak. Ben Projenin Adına “Cagdas.Workflow.Hosting” ismini verdim.



WorkflowInstaceWrapper Sınıfını Tanımlayalım

1-     Başlattığınız Empty Workflow projesine WorkflowInstanceWrapper adında bir sınıf ekleyin.



2-     WorkflowInstanceWrapper sınıfına Seializable attribute’unu ekleyin. Bu attribute ekleyerek ilerleyen aşamalarda workflow’unuzu serialize etme fırsatı bulacaksınız.



3-     WorkflowInstanceWrapper.cs uzantılı dosyaya System.Workflow.Runtime, System.Workflow.Runtime.Hosting ve System.Threading isim alanlarını ekleyin.

4-     Sınıfın fieldlarını tanımlayın. __workflowInstance field’ı, WorkflowInstanceWrapper sınıfının izole edeceği WorkflowInstance’tır. WorkflowInstanceWapper sınıfı kullanılırken arka planda WorkflowInstance nesnesini kullanacak. __waitHandle field’ı ise WokrflowInstanceWrapper nesnesinin işi bittiğinde Workflow Runtime Engine ile haberleşmesi için kullanacağız. __outputParameter field’ını workflow’un döndüreceği parametreleri tutmak için kullanacağız. Workflow çalışırken hata oluştuğunda, hatayı Workflow Runtime Engine’e bildirmek için __exception field’ını kullanacağız. Herhangi bir nedenden dolayı workflow’un çalışması durdurulursa, durdurulma nedenini __reasonSuspended fied’ı aracılığıyla Workflow Runtime Engine’e bildireceğiz.



5-     Sınıfın contructor’ını yazın. Contructor’da dışarıdan aldığımız WorkflowInstance tipindeki parametreyi izlode edeceğiz. WorkflowInstanceWrapper sınıfı kendi içerisinde direkt olarak bir WorkflowInstance oluşturmuyor, dışarıdan aldığı parametreyi izole ediyor.



6-     Exception özelliğini tanımlayın.



7-     Id özelliğini tanımlayın. Kullanacağımız WorkflowInstance’ın InstanceId’sini verir. Bu özelliklik birden fazla WokflowInsantance nesnesinin aynı ortamda bulunduğu noktalarda işe yarayacaktır.



8-     OutputParameters özelliğini tanımlayın. WorkflowInstance’a ait çıktı parametrelerine erişeceğimiz özellik.



9-     ReasonSuspended özelliğini tanımlayın.



10- WaitHandle özelliğini tanımlayın.



11- StopWaiting metodunu tanımlayın. StopWaiting metodu WorkflowInstace’ın çalışmasını tamamladığını Workflow Runtime Engine’e haber vermek için kullanacağız.



WorkflowLogEventArgs Sınıfını Tanımlayalım

1-     Projenize WorkflowLogEventArgs adında bir sınıf ekleyin.
2-     Message adında ve string tipinde bir özellik tanımlayın.



3-     Sınıfa ait bit tane constructor tanımlayın.



WorkflowRuntimeManager Sınıfını Tanımlayalım
 

  1. Sınıf içerisine aşağıdaki isim alanları ekleyin.


  2. Sınıf içerisine field ları ekleyin. __workflowRuntime filed’ını WorkflowInstance larını yönetmek için kullanacağız. Bu sınıf içerisinde WorkflowInstance lara direkt olarak erişim yok. WorkflowInstaceWrapper sınıfı ile her WorkflowInstance nesnesi izole edilmekte.__workdflows field’ını WrokflowRuntimeManager sınıfı içerisideki WorkflowInstanceWrapper tipindeki elemanlara erişmek için kullanacağız.


  3. WorkflowRuntime özelliğini tanımlayın. WorkflowRuntimeManager sınıfının içerisine constructor’da parametre olarak verilen WorkflowInstance’ına erişim sağlıyoruz.


 

  1. Workflows özelliğini tanımlayın. Bu özellik, sınıf içerisindeki WorkflowInstanceWrapper koleksiyonuna erişim sağlıyor. Yönetimi kolay olsun diye, Guid ve WorkflowInstanceWrapper tipindeki Dictionary yapısı içerisinde saklama işlemini yapıyoruz.


  2. LogStatus metodunu tanımlayın. LogStatus metodu, Workflow Runtime Engine’in olayları tetiklendiğinde hangi olayın tetiklendiğini ve hangi olayın hangi WorkflowInstance için tetiklendiğini kayıt altına almak için kullanacağımız bir metottur. Bu metot sonunda sıfın en başında tanımlanan MessageEvent’i tetiklenir.


  3. FindWorkflowInstance metodunu tanımlayın. Bu metot verilen Guid tipindeki workflowId’ye göre sınıf içerisindeki Workflows özelliğinde tutulan koleksiyon yapısında arama yapar. Eğer o workflowId’ye sahip bir eleman varsa onu geri döndürür.


  4. SubscribeToEvents metodunu tanımlayın. Gelen WorkdflowRuntime nesnesinin bütün olaylarına abone olan metot. Bu olaylar tetiklendiğinde, WorkdflowRuntime nesnesinin bütün durumlarını yakalamış oluyoruz. Buradan sonra 20. adıma kadar aşağıdaki olaylar gerçekleştiğinde çalışacak metotları tanımlayacağız.


  5. runtime_WorkflowUnloaded metodu WorkflowInstance kaldırılırken çalışacaktır.

      
  6. runtime_WorkflowTerminated metodu WorkflowInstance içerisinde hata oluştuğunda çalışacaktır. Bu metot içerisinde durumu kayıt altına aldıktan sonra, hangi workflow’da hata çıkmışsa onun InstaceId’sini alıp kendi koleksiyonumuz içerisindeki ilgili WorkflowInstanceWrapper nesnesini buluyoruz. Wrapper nesnesinin Exception özelliğine workflow içerisinden fırlatılan hatayı koyuyoruz. Böylelikle istediğimiz zaman WorkflowInstance içinde hangi hata çıkmış görebileceğiz.


  7. runtime_WorkflowSuspended metodu WorkflowInstance beklemeye alındığında çalışacaktır. Bizde burada beklemeye alından workflow’u koleksiyonumuz içerisinden bulup, ReasonSuspended özelliğine gelen hatayı koyuyoruz. Böyle yaparak herhangi bir anda WorkflowInstanceWrapper nesnesinin neden beklemeye alındığını görebiliriz.


  8. runtime_WorkflowStarted metodu WorkflowInstance çalışmaya başladığında çalışacaktır.


  9. runtime_WorkflowResumed metodu WorkflowInstance suspend olduktan sonra çalışmaya başladığında çalışacaktır.


  10. runtime_WorkflowPersisted metodu WorkflowInstance kalıcı hale getirildiğinde çalışacaktır. WorkflowInstance ları SqlServer içerisinde kalıcı hale getirebiliyoruz.


  11. ?runtime_WorkflowIdled metodu WorkflowInstance çalışmaya işlemini beklemeye aldığında çalışacaktır.


  12. runtime_WorkflowCreated metodu WorkflowInstance oluşturulduğunda çalışacak metottur.


  13. runtime_WorkflowCompleted metodu WorkflowInstance çalışmasını bitirdiğinde çalışacak metottur. Bu metot içerisinde WorkflowRuntimeManager sınıfı içerisindeki çalışmasını tamamlayan WorkflowInstanceWrapper nesnesinin OutputParameters özelliğine WorkflowCompletedEventArgs ile gelen e parametresindeki OutputParameters özelliğinin değeri atanır. Böylelikle WorkflowInstanceWrapper nesnelerinin çıktı parametrelerinin tipleri, sayıları… gibi farklılıkları da yakalayabiliriz.Wrapper nesnesinin StopWaiting metodunu çalıştırarak Workflow Runtime Engine’ wrapper’ın işini bitirdiğini haber veririz.


  14. runtime_WorkflowAborted metodu WorkflowInstance’ın çalışması iptal edildiğinde çalışacak metotdur.


  15. runtime_Stopped metodu WorkflowRuntime’ın çalışması durduğunda çalışacak metotdur.


  16. runtime_Started metodu WorkflowRuntime’ın çalışması başladığında çalışacak metotdur.


  17. Sınıfın constructor’ını tanımlayın. Burada dışarıda oluşturuşmuş bir WorkflowRuntime nesnesini sınıfa kabul ediyoruz. Yönettiğimiz nesne dışarıdan gelen WorkflowRuntime nesnesidir. Eğer gelen nesne null ise NullReferenceException fırlatıyoruz. Eğer dışarıdan parametre olarak gelen nesne null değilse, SubscribeToEvents metodunu çağırarak WorkflowRuntime nesnesinin bütün olaylarına abone oluyoruz. Bir önceki yazımda geliştirdiğimiz host uygulamasında sınırlı sayıda olay ile çalışabiliyorduk. Burada ise WorkflowRuntime nesnesinin her olayını yakalayarak, her durumunu loglama imkânı buluyoruz.


  18. AddWorkflowInstance metodu gelen WorkflowInstance tipindeki instance parametresini Workflows koleksiyonuna ekler.


  19. StartWorkflow metodu belirtilen tipteki workflowu oluşturur ve dışarıdan alınan girdi parametreleriyle çalıştırır. Daha sonra oluşturduğu instance’ı sıfın içerisindeki Workflows koleksiyonuna WorkflowInstanceWrapper olarak ekler.

  

  1. Clear metodu, Workflows koleksiyonu içerisindeki dışarıdan parametre olarak alınan workflowId’ye sahip elemanı siler.

  

  

  1. ClearAllWorkflows metodu, Workflows koleksiyonu içerisindeki bütün elemanları siler.

  

  

  1. WaitAll metodu, WorkflowRuntimeManager sınıfı içerisindeki tüm workflowlar işlerini bitirene kadar bekler. Böylelikle Bütün workflow çalışırlar, çıktılarını verirler ve çıktılar işlenmek için hazırlanmış olur.

  


 

  1. WorkflowRuntimeManager sınıfına IDisposable interface’ini implement ettirin. Dispose metodu içerisinde ilk önce __workflowRuntime nesnesini durdurun, sonra __workflowRuntime nesneisnin Dispose metodunu çağırın.



    Buraya kadar WorkflowInstanceWrapper, WorkflowLogEventArgs ve WorkflowRuntimeManager sınıfını tanımlamış olduk. Buradan sonraki aşamalarda workflow kalıcılığını sağlamak için Sql Server’da bazı işlemler yapacağız. Son olarak bir tane test uygulaması geliştirerek Host kütüphanemizi test edeceğiz.

    Workflow’un Sql Server Yapılandırması

    Bu bölüm ilerleyen aşamalarda daha çok işinize yarayacak. Özellikle State Machine Workflow konusuna geldiğimde bu yapılandırmanın neden önemli olduğunu anlayacaksınız. Bu uygulama içerisinde workflow’un kalıcılığını sağlayabilmek için Sql Server içerisinde bir veri tabanı oluşturmamız gerekiyor. Bu veritabanının şablonunu aşağıdaki adreste bulunan scriptler aracılığıyla sağlayacağız. Şimdi aşağıdaki klasörü açın.



    Ben burada Sql Server Express Edition kullanıyorum. Server’a bağlantı yaparken authentication modeli olarak SQL Server Authentication kullanıyorum. Server’ınıza bağlanın.



    WorkflowPersistence adında bir veritabanı oluşturun. Bu veri tabanının adının özel bir anlamı yok. İstediğiniz isimde veritabanı oluşturabilirsiniz.



    Veritabanını oluşturduktan sonra yukarıda gösterdiğim klasör altındaki SqlPersistenceService_Schema.sql script’ini çalıştırın.



    Daha sonra SqlPersistenceService_Logic.sql script’ini çalıştırın.



    Bu iki script’i çalıştırarak Workflow ların ihtiyaç duyduğu veritabanı yapısına sahip olacaksınız. Bu yapılandırmayı uygulamamızı test ederken kullanacağız.



    Test Uygulaması Geliştirelim

    İlk başta Solution Explorer pencrenize baktığınızda aşağıdaki çıktıyı göreceksiniz.


  

  1. Uygulamanıza bir önceki makalemde anlattığım workflow uygulamasını ekleyin. Eklediğiniz uygulamanın Program.cs dosyasını silin ve uygulamaya WorkflowTest adında bir sınıf ekleyin.


      
  2. Eklediğiniz projenin referanslarına Cagdas.Workflow.Hosting’i ekleyin. Eklediğiniz WorkflowTest sınıfının içerisine static ve geriye void döndüren Run adında bir metot etkleyin. Bu metot içerisine aşağıdaki kodları yazın.

    Bu metot içerisinde ilk başta geliştirdiğimiz Cagdas.Workflow.Hosting isim alanının altında bulunan WorkflowRuntimeManager sınıfının bir örneğini oluşturuyoruz. Oluşturma işlemini using bloğu içerisinde yaptığımızdan, using bloğundan çıkıldığında oluşturduğumuz manager nesnesi otomatik olarak dispose edilecektir. Daha sonra manager nesnesinin MessageEvent olayına abone oluyoruz. AddServices Metodunu kullanarak WorkflowRuntime nesnesinin kalıcı olmasını sağlıyoruz. wfArguments koleksiyonu içerisinde girdi parametrelerini belirlioruz ve ilk oluşturduğumuz WorkflowInstance nesnesi için “bir“ parametresini belirliyoruz. Daha sonra manager nesnesinin StartWorkflow metodunu kullanarak Workflow1’i çalıştırıyoruz. Bu işlemi 3 kere daha yapıyoruz.

    Normalde bu işlemler çalışmak için birbirlerini bekleyeceklerdi fakat biz çalıştırma işlerini yaptıktan sonra manager’ın WaitAll metodunu kullanarak manager içerisinde bulunan tüm WorkflowInstance ların işlerini tamamlamasını bekliyoruz. Bütün hepsi işlerini tamamladığında teker teker çıktıları ekrana yazdırarak metodu sonlandırıyoruz. Bu arada WorkflowInstance lar çalışırken fırlatılan olayları manager_MessageEvent metodu içerisinde ekrana yazdırıyoruz.


      
  3. Manager_MessageEvent metodunu tanımlayın.


     
  4. AddServices metodunu tanımlayın. Bu metot içerisinde gelen WorkflowRuntime nesnesini Core Workflow Servislerinden biri olan SqlWorkflowPersistentService’e ekliyoruz. Ekleme işlemini yaparken bir tane connection string’e ihityacımız var. SqlWorkflowPersistentService workflow’un kalıcılığını sağlayan servistir.


  5. Projeniz içerisindeki Workflow1’i açın ve Code aktivitesinin altına bir tane delay aktivitesi ekleyin. Delay aktivitesini Workflow’un Idled gibi olaylarını yakalamak için kullanacağız.


  6. Projeniz içerisine bir tane program.cs dosyası ekleyin ve Main metodu içerisine aşağıdaki kodları yazın.



    Burada sadece WorkflowTest sınıfının Run metodunu çalıştırıyoruz.
  7. Uygulamanızı F5 tuşuna basarak çalıştırın. Aşağıdaki ekran görüntüsünü elde edeceksiniz.



    Bu ekran görüntüsüne dikkat edecek olursanız, ilk önce workflowlar oluşturuluyor, çalıştırılıyor, parametreleri yollanıyor ve son olarak çıktı parametrelerinin değerleri ekrana yazdırılıyor. Bir önceki yazımda anlattığım karmaşık düzenden arındık.

    WorkflowRuntime Sınıfını App.Config Dosyasında Yapılandırmak

    İsterseniz AddServices metodunu kullanmak yerine App.Config dosyası içerisinde yapacağınız yapılandırma ile WorkflowRuntime nesnenizi oluşturabilirsiniz. Bunun için ilk yapmanız gereken uygulamanıza bir tane applicaton configuration file eklemek. Ekleme işlemini yaptıktan sonra aşağıdaki xml yapısını App.Config dosyası içerisine yazın. Burada AddService metodu içerisinde yaptığınız işi xml olarak tanımlıyorsunuz. Birazdan AddSercice metodunu kaldıracaksınız. Aşağıdaki XML yapısında WorkflowRuntime adında bir bölüm oluşturduk.



Uygulamanıza System.Configuration referansını ekleyiniz. Daha sonra WorkflowTest sınıfına gelin ve Run metodunun başında aşağıdaki değişiklikleri yapın.



Burada AddServices metodunu yorum haline getirdik. Ayrıca new WorkflowRuntime(“WorkflowRuntime”) bölümünde ayarların App.Config dosyasından alınmasını sağladık. Böylelikle ayarlarda değişiklik yaptığımızda kodu tekrar derlememize gerek kalmayacak. Yani bize esneklik kazandıran bir işlem yapmış olduk.

Uygulamayı tekrar çalıştırdığınızda problemsiz bir şekilde çalıştığını göreceksiniz.



Özet

  1. WorkflowRuntime ve WorklfowInstance nesneleirnin tekrar kullanılabilirliğini arttırıdık. Kısa ve uzun vadede işe yarar bir işlem.
  2. Geliştirilen host uygulaması birden fazla WorkflowInstance nesnesini host edebiliyor.
  3. Her WorkflowInstance farklı output parametreleri yönetimi yapılabiliyor.
  4. Aynı host uygulaması içerisinde birden fazla farklı tipte WorklfowInstance host edilebiliyor.
  5. Her workflow için ayrıntılı olay yakalama mekanizmasına sahip.

    Böylelikle Workflow hosting makale serisinin son bölümü olan ikinci bölümünü de tamamlamış oldum. Sizin için Workflow Host işleminin Bruce Bukovics yaklaşımının faydalı olacağını düşünüyorum. Bir sonraki WWF makalemde görüşmek üzere…