WCF 4.0 Yenilikleri - Workflow Services [Beta 2]

Merhaba Arkadaşlar,

WCF ve WF arasında ilişkiyi anlatan güzel bir cümle vardır. .Net Framework 3.0' da arkadaş olan WCF ve WF, .Net Framework 3.5 sürümünde nişanlanmış, .Net Framework 4.0 sürümünde ise evlenmişlerdir. Wink Bu ikilinin bir arada ele alınması sonucu Workflow Services adı verilen bir ufaklıkta ortaya çıkmıştır. Aslında bir akışın servis bazlı olması son derece önemlidir. Nitekim WF tarafında, uzun süreli işlemlerin ele alınması(Long Running Process), akışın çeşitli noktalarından farklı anlar için kalıcı olarak saklanabilmesi(Persistence), asenkron çalışma zamanı motorunun zaten hazır olarak bulunması söz konusudur. Bu özelliklerin WCF Servis Noktaları(Endpoints) ile desteklenmesi, bir akışın ağ üzerindeki istemciler tarafından başlatılabilmesine olanak tanımaktadır. Üstelik bu akışlar sonuçlar üreterek bunları geriye de döndürülebilir. Bu durum aslında WCF 3.5 sürümünde de gerçeklenebilmektedir. Ne varki, WCF 4.0 tarafında tamamen declerative olarak tanımlanabilen ve saf XAML(eXtensible Application Markup Language) içeriğinden oluşan bir Workflow Servisinin geliştirilebilmesi mümkündür. Üstelik Visual Studio 2010 WF Designer sayesinde bu XAML içeriğinin üretilmesi için IDE desteği de sunulmaktadır.

Kişisel Not : Windows XP, Vista ve 7 sürümlerinde kullandığım Visual Studio 2010 Beta 1 ürününde, WF Designer sürekli olarak hata verip IDE' yi Restart işlemine zorlamaktaydı.Undecided Neyseki bu sorun Visual Studio 2010 Beta 2 sürümünde ortadan kaldırılmış durumdadır.Wink

Dolayısıyla bu yazımızın konusu şimdiden belli oldu. Çok basit ve büyük ihtimalle pek işe yaramayan bir Workflow Service' in nasıl geliştirilebileceğini incelemeye çalışıyor olacağız. İşe ilk olarak Declerative Sequential Service Library tipinden bir WCF projesi oluşturarak başlayacağız. Aşağıdaki ekran görüntüsünde olduğu gibi.

Örnek olarak AdventureWFServices ismiyle oluşturduğumuz projede Service1.Xamlx dosyası hemen dikkati çekmektedir. Bu örnek olarak oluşturulan Workflow Service örneğidir. Yani Microsoft tarafından sunulan hazır Hello World örneği Wink Tamamen XAML içeriğinden oluşmaktadır ve istemcilere servis olarak nasıl sunulacağına ilişkin çalışma zamanı bilgileri(WCF Servis konfigurasyon bilgileri) Web.config dosyasında tutulmaktadır. (Bu bir WCF kütüphanesi projesi olduğundan doğrudan çalıştırılabilir ve Web.config içerisindeki ayarlar sayesinde HTTP bazlı olarak host edilir. Diğer taraftan test etmek için WcfTestClient aracından kolayca yararlanılabilir.)

Biz tabiki bu içerik yerine kendi Workflow Service örneğimizi kullanacağız. Ama öncesinde ReceiveRequest ve SendResponse isimli bileşenlerin ne işe yaradıklarını kısaca anlamaya çalışmakta yarar vardır. ReceiveRequest isimli bileşen aslında Receive tipinden bir aktivitedir. Benzer şekilde SendResponse isimli bileşende, SendReply tipinden bir aktivitedir. Tahmin edileceği üzere Receive aktivitesi ile, istemci tarafından gelen talep(Request) alınmakta ve SendReply aktivitesi yardımıylada bir cevap(Response) döndürülmektedir.(Bu iki bileşen arasında ise istenen bir akışın yürütüldüğü düşünülebilir) Bir başka deyişle WCF bazlı mesaj alınması ve cevap döndürülmesi amacıyla kullanılan iki aktivite tipi söz konusudur. ReceiveRequest isimli bileşen aynı zamanda istemci tarafından çağırabilecek bir operasyon sunduğundan OperationName isimli bir özelliği de sahiptir. Diğer taraftan istemci tarafından gelecek olan talep içerisindeki değişkenler Content özelliği tarafından taşınabilirler ki bu özellik aslında bir koleksiyon olarak düşünülebilir. Çünkü operasyonun birden fazla parametre alması gerekebilir. Receive aktivitesi için önem arz eden noktalardan biriside, CanCreateInstance özelliğinin değeridir. Bu değer örneğimizde true olarak set edilmiştir ve söz konusu aktiviteye bir talep gelmesi ile birlikte Workflow örneğinin oluşturulup oluşturulmayacağını işaret etmektedir. Önemli olan noktalardan biriside, SendResponse isimli bileşenin Request özelliğinin değerinin, Receive tipinden olan ReceiveRequest isimli bileşen olmasıdır. Dolayısıyla bu iki mesajlaşma aktivitesinin birbirleriyle haberleştiklerini söyleyebiliriz. (İlerki yazılarımızda burada sözü edilen korelasyon konusuna değiniyor olacağız ki XAML içeriğinde bu konunun izlerini görmekteyiz.)

Bu arada Sequential Service tipinin üzerinde tanımlanmış bazı değişkenler(Variables) olduğu görülebilir.

data isimli Int32 tipinden olan değişken, Receive aktivitesi için Content değerini de temsil etmektedir. Diğer taraftan, SendReply aktivitesine ait Content özelliğinde kullanılmış ve string karşılığı üretilerek geriye döndürülmüştür. Buna göre akışın senaryosunu şu şekilde düşünebiliriz. İstemci talep olarak Int32 tipinden bir değer gönderir. Bu değer ReceiveRequest isimli aktivite bileşeni tarafından alınır ve üst aktivitesi olan Sequential Service' in data değişkenine atanır. Bu değişken SendResponse isimli aktivite bileşeninin de ulaşabileceği kapsamda(Scope) olduğundan, istemciye geriye gönderilecek cevabın içeriğinde kullanılabilir. Şimdi bu bilgilerden yola çıkarak kendi Workflow Servisimizi oluşturalım.

Örnek olarak istemciden gelen iki sayının toplamını bulup geriye bu sonucu döndürecek bir Workflow Servis geliştirmeyi planlayabiliriz. Böylece XAML içeriğini anlamamız daha kolay olacaktır. Bu amaçla projemize yeni bir Declerative Sequential Service öğesi ekleyerek devam edebiliriz.

MathService.xamlx servisinin tasarım görüntüsü aşağıdaki gibidir.

Aslında global seviyede tanımladığımız GlobalX ve GlobalY isimli iki değişken haricinde çok fazla detay görülmemektedir. Bu nedenle konuyu anlamanın en iyi yolu XAML içeriğine bakmamız olacaktır(Zaten ilerleyen zamanlarda bu blog girişinin destekleyici görsel videosunuda yayınlıyor olacağım) İşte MathService.xamlx içeriği;

<Service mc:Ignorable="sad1" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/servicemodel" xmlns:a="clr-namespace:AdventureServices;assembly=AdventureServices" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:p="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:p1="http://adventure/" 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:sad="clr-namespace:System.Activities.Debugger; assembly=System.Activities" xmlns:sad1="http://schemas.microsoft.com/netfx/2009/xaml/activities/design" 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:sd1="clr-namespace:System.Data;assembly=System.Data.DataSetExtensions" xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" xmlns:st="clr-namespace:System.Text;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <WorkflowServiceImplementation ConfigurationName="MathService" Name="MathService">
    <p:Sequence DisplayName="Basic Math Service" sad:XamlDebuggerXmlReader.FileName="C:\My Dot Net World\Projects\2010\WCF\AdventureWFServices\AdventureWFServices\MathService.xamlx" sad1:VirtualizedContainerService.HintSize="303,348.553333333333" mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces">
      <p:Sequence.Variables>
        <p:Variable x:TypeArguments="CorrelationHandle" Name="handle" />
        <p:Variable x:TypeArguments="x:Int32" Name="GlobalX" />
        <p:Variable x:TypeArguments="x:Int32" Name="GlobalY" />
      </p:Sequence.Variables>
      <sad1:WorkflowViewStateService.ViewState>
        <scg3:Dictionary x:TypeArguments="x:String, x:Object">
          <x:Boolean x:Key="IsExpanded">True</x:Boolean>
        </scg3:Dictionary>
      </sad1:WorkflowViewStateService.ViewState>
      <Receive x:Name="__ReferenceID0" CanCreateInstance="True" DisplayName="Sum" sad1:VirtualizedContainerService.HintSize="257,85.2766666666667" OperationName="Sum" ServiceContractName="p1:IMathService">
        <Receive.CorrelatesOn>
          <MessageQuerySet />
        </Receive.CorrelatesOn>
        <Receive.CorrelationInitializers>
          <RequestReplyCorrelationInitializer CorrelationHandle="[handle]" />
        </Receive.CorrelationInitializers>
        <Receive.KnownTypes>
          <x:Type Type="x:Int32" />
        </Receive.KnownTypes>
        <ReceiveParametersContent>
          <p:OutArgument x:TypeArguments="x:Int32" x:Key="X">[GlobalX]</p:OutArgument>
          <p:OutArgument x:TypeArguments="x:Int32" x:Key="Y">[GlobalY]</p:OutArgument>
        </ReceiveParametersContent>
      </Receive>
      <SendReply Request="{x:Reference __ReferenceID0}" DisplayName="Sum Response" sad1:VirtualizedContainerService.HintSize="257,85.2766666666667">
        <SendParametersContent>
          <p:InArgument x:TypeArguments="x:Int32" x:Key="Result">[GlobalX + GlobalY]</p:InArgument>
        </SendParametersContent>
      </SendReply>
    </p:Sequence>
  </WorkflowServiceImplementation>
</Service>

İlk etapta biraz korkutucu görünebilir. Sealed Ancak şu an için üzerinde duracağımız önemli noktalar Bold olarak işaretlenmiştir. Sequence.Variables elementi içerisinde 3 değişken tanımı görülmektedir. Bizim eklediklerimiz GlobalX ve GlobalY isimli int tipinden olanlardır. Bu değişkenler Sequence elementi içerisinde tanımlandıklarından, takip eden Receive ve SendReply elementleri veya gelecek başka aktiviteler tarafından ortaklaşa kullanılabilirler. Receive elementinde operasyon ile ilişkili bilgiler dışında, servisin sözleşme ismide belirtilmektedir. Receive aktivitesi için en önemli parça ReceiveParametersContent kısmıdır. Burada X ve Y isimli OutArgument tipinden argümanlar tanımlamıştır. Bu argümanlarda önemli olan kısım GlobalX ve GlobalY atamalarıdır. X ve Y aslında istemcinin çağıracağı servis operasyonunun parametreleridir. İstemciden gelen bu değerler, aktivitenin GlobalX ve GlobalY değerlerine set edilmektedir. SendReply aktivitesi içerisinde yer alan SendParametersContent kısmında ise InArgument tipinden bir argüman tanımlanmıştır. Result ismi ile tanımlanan bu argümanın içeriği, GlobalX ve GlobalY değerlerinin toplamından oluşmaktadır. Bir başka deyişle istemci tarafına döndürelecek cevap içeriği belirlenmiş olur. İçeriğin tipi ise TypeArguments niteliğine atanan Int32 yapısıdır. Görüldüğü üzere herhangibir kod dosyası kullanılmamıştır. Tüm işlemler XAML bazlı olaraktan tanımlanabilmektedir. Buda çalışma zamanı için önemli bir esnekliktir.

Oluşturduğumuz sınıf kütüphanesinin Web.config dosyasının içeriği ise son derece sadedir.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceDebug includeExceptionDetailInFaults="False" />
          <serviceMetadata httpGetEnabled="True"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Görüldüğü gibi belirli bir Endpoint tanımlaması yoktur. Ancak exception detayının gönderilmesi ve Metadata bilgisinin elde edilebilmesi için gerekli davranışlar(Behaviors) tanımlanmıştır. Buna göre Workflow Servisimiz, HTTP bazlı olaraktan host edilecektir. Uygulamayı F5 ile başlattığımızda ve MathService.xamlx içeriğini talep ettiğimizde aşağıdaki ekran görüntüsü ile karşılaşırız.

Görüldüğü üzere WSDL içeriğide doğrudan talep edilebilmektedir. Buda istemciler için gerekli Proxy üretiminin kolayca yapılabileceği anlamına gelmektedir. Burada WSDL içeriğinide inceleyebiliriz. SealedAncak benim özellikle göstermek istediğim nokta http://localhost:65193/MathService.xaml?xsd=xsd1 talebinin sonucudur. Nitekim WSDL dökümanına baktığımızda, Sum isimli operasyonun kullandığı X ve Y parametrelerini göremeyiz. Ancak bu parametrelerin tanımlandığı XSD içeriğine bir referans bildirildiğini fark edebiliriz. Bu XSD içeriğinde aşağıdaki ekran görüntüsünde olduğu gibi, X,Y ile istemci tarafına döndürelecek Result değişkenlerine ait tanımlamalar yer almaktadır.

Dilerseniz artık servisimizi test edelim. Bu amaçla daha önceden de belirttiğimiz gibi WcfTestClient aracını kullanabiliriz. İşte örnek bir toplama işlemi;

İlk olarak Request için X ve Y değerleri girilir sonrasında Invoke düğmesine tıklanır. Bir süre beklemenin ardından servisten geri dönüş elde edilir. BasicHttpBinding tabanlı olarak host edilen Workflow Servisimiz başarılı bir şekilde çalıştırılmıştır. Yapılan Invoke işlemi sonrası üretilen XML içeriğine bakıldığında ise aşağıdaki çıktının oluştuğu görülebilir. 

İşte bu kadar. Smile Workflow Servisleri sayesinde bir iş akışının servis bazlı olarak başlatılabilmesi ve WF Çalışma zamanının nimetlerinden yararlanabilmesi mümkün olmaktadır. Üstelik, .Net 4.0 tarafında gelen yeni modele göre söz konusu Workflow Servislerinin tamamen XAML içeriğinden oluşacak şekilde dekleratif olarak tanımlanabilmesi söz konusudur. Bu da çalışma zamanında koda müdahale etmeye gerek duymadan akış ile ilişkili bazı değişikliklerin yapılabileceğinin bir göstergesidir. Bakalım WCF&WF kardeşliğinde başka ne gibi yenilikler bizleri bekliyor. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

AdventureWFServices.rar (442,66 kb)

Yorumlar (2) -

  • Burak Bey merhaba,

    Güzel makale için teşekkürler. Elinize sağlık. Fakat kavramsal olarak anlayamadığım bir nokta mevcut;

    -Biz X + Y işlemini yapacak ve geriye Int döndürecek bir WCF servisi de yazabilirdik. Bu noktada WF Servisinin WCF servisinden tek farkı XAML içinde geçirilen parametrelere ne yapılacağını gene XAML için de belirlemiş olmamız mı? Aynı işlemi WCF servisi ile yapabilirdik, fark nedir?

    Bir de VS 2010 + .NET 4.0 tam olarak ne zaman yayınlanacak, bilginiz var mı?

    Teşekkürler
  • Merhabalar,

    Aslında şu şekilde düşünmek daha doğru olacaktır. Örneğimizdeki Workflow' un servis üzerinden host edilmesi ve istemciler tarafından tetiklendiğinde çalıştırılmaya başlaması söz konusu. Bir başka deyişle, X+Y işlemi yerinde gereçek hayat iş akışlarını koyduğumuzda Workflow modelinin bazı avantajlarından ve görsel tasarım zenginliğinden yararlanabiliriz. Söz gelimi Persistence mekanizması, long-running process için gerekli desteğin sağlanması veya designer yardımıyla akışın kolayca tasarlanması.

    Diğer yandan XAML tabanlı tasarlanan Workflow örneklerinin farklı 3ncü parti uygulamalar tarafından değerlendirilmesi de mümkün olabilir. Visual Studio 2010 son sürümünün sanıyorum ki 22 Mart 2010 tarihinden yayınlanması planlanıyor.

Yorum ekle

Loading