Burak Selim Senyurt(MVP)
Matematik Mühendisi bir .NET Severin Yazıları...

WF 4.0 - Kod Yoluyla Workflow Service Oluşturmak, Kullanmak [Beta 1]

Cuma, 16 Ekim 2009 09:00 by bsenyurt

Merhaba Arkadaşlar,

Yükseklik korkum olmasına rağmen her zaman yandaki gibi tırmanışta olanlara imrenmişimdir. Bu fotoğrafa konu olan kişinin tek yaptığı yoğun bir mücadele ve efor ile yukarı doğru tırmanmaktır. Bana göre sonuçta elde edilebilecek tek şey zirveye ulaşmak ve oranın eşsiz manzarasını izlemekten ibarettir. Tabi bunun birde inişi olduğunu düşünmek gerekiyor Sealed Hangi açıdan bakarsak bakalım bizde hayatımızda zaman zaman böyle mücadeleler içerisine gireriz. Özellike yazılım geliştirirken Laughing

Örneğin her zaman elimizin altında Visual Studio IDE' sinin sunduğu gibi gelişmiş arayüzler bulunmayabilir. Örneğin Visual Studio 2010 Beta 1 üzerinde yaşadığım sorunlardan birisi WPF tabanlı Designer' ı Workflow uygulamaları için kullanamıyor oluşumdu. Bu gerçekten çok üzücü bir durum. Undecided Ama çaresiz değiliz. Çaresizliğin çözümü bazı işlemleri basit bir Console uygulamasında, gereklilikleri fark ederek(örneğin hangi Assembly' ların referans edilmesinin gerektiğinin bilinmesi...), kod bazında yapmaktan ibarettir. Bunun bize sağlayacağı pek çok fayda bulunmaktadır. Kod tarafında her ne kadar mücadeleci bir yol izlesekte, neyin nasıl oluşturulması gerektiğini, hangi durumlarda ne gibi istisnalara(Exceptions) düşebileceğimizi, nelere ihtiyaç duyduğumuzu ve arka planda aslında işlemlerin nasıl değerlendirildiğini görmek açısından yararlı bir yoldur. Bu kadar cümleyi elbetteki sizi yazının kalanına motive etmek için sarfettiğimi düşünebilirsiniz. Cool Haydi gelin kamera arkasına bakalım.

Kişisel Not : Aslında Beta 2 sürümünde(ki henüz public olarak yayınlanmadığını biliyorsunuz), designer tarafındaki sorunların aşıldığını ifade edebilirim. Bizzat tecrübe ile sabitlenmiştir Smile Hatta bu konu ile ilişkili bir yazımı söz konusu sürüm public hale geldikten sonra yayınlıyor olacağım.

Bu yazımızda .Net Framework 4.0 Beta 1 ile bir Workflow Service' in oluşturulması, host edilmesi ve bir istemci tarafından kullanılması konusu irdelenmeye çalışılacaktır. Workflow Service tek yönlü bir operasyon(One Way) için hizmet vermekte olup istemci tarafına bir geri bildirimde bulunmamaktadır. Her iki uygulamada birer Console Application olarak tasarlanmıştır. İstemci tarafında Workflow Service' in kullanılabilmesi için gerekli Proxy nesnesi yine kod yardımıyla(WSDL dökümanından yararlanmadan) oluşturulmaktadır. Aslında tamamlanmış olan uygulamalara baktığımızda servis ve istemci tarafı için gerekli olan referans Assembly' ların aşağıdaki şekilde görüldüğü gibi olduğunu fark edebiliriz.

Tamam çok güzel ama biz bu yazımızda tam olarak neyi hedeflemekteyiz?

Yapmak istediğimiz ilk şey WF 4.0 bazlı olarak bir Workflow Service geliştirmek olacaktır. Bilindiği üzere Workflow Service' ler, istemcilerin uzaktan erişerek başlatabileceği akışları içerebilecek hizmetler olarak düşünülebilir. Öyleyse Workflow Service' in istemciden gelecek talepleri alabilmesi, gerektiğinde istemciye dönüş yapabilmesi ve kendi içerisinde bir akışı barındırması gerekmektedir. Tahmin edileceği üzere istemci üzerinden gelecek taleplerin alınması veya cevapların gönderilmesi sırasında hazır aktivite bileşenlerinden yararlanılır. Örneğin Receive veya SendReply...Elbette bu tip bir servisin geliştirilmesi yeterli değildir. Bu servisin istemcilere hizmet verebilmesi için ayrıca host edilmesi de gerekmektedir. İstemci tarafı ise standart bir Console, WinForms, WPF, Asp.Net ucu olabileceği gibi başka bir Workflow Service de olabilir. Biz işe ilk olarak Workflow Service tarafını geliştirerek başlayacağız. Bu amaçla WithWCF isimli Console uygulamamızın kodlarını aşağıdaki gibi geliştirdiğimizi düşünebiliriz.

Workflow Service tarafı kodları;

using System;
using System.Activities;
using System.Activities.Statements;
using System.ServiceModel;
using System.ServiceModel.Activities;
using System.Xml.Linq;

namespace WithWCF
{
    class Program
    {
        static void Main(string[] args)
        {
            // Örnekte yer alan akış tek-yönlü bir Workflow Service' idir. Sequence tipinden olan bu akış, Receive aktivitesi ile başlamakta olup istemciden gelen double tipinden değişkenin karekökünü hesaplamakta ama istemci tarafına bir bilgilendirmede bulunmamaktadır.

            #region Sequence Aktivitesi Oluşturulma İşlemleri

            // Sequence tipinden bir aktivite örneklenir
            Sequence squareRootFlow = new Sequence();           

            // Aktivitede kullanılacak olan Variable tanımlamaları yapılır
            Variable<double> number = new Variable<double>(); // Receive aktivitesi tarafından alınan değişken
            Variable<double> square = new Variable<double>(); // Sonuç değişkeni
            Variable<CorrelationHandle> corHandle = new Variable<CorrelationHandle>();
            // Xml Namespace tanımlaması yapılır. XNamespace kullanılmadığı takdirde örneğin Receive aktitivesinin ServiceContractName özelliğine adres bilgisi text tabanlı olarak atandığında şekilde görülen exception üretilir.
            XNamespace ns = "http://www.buraksenyurt.com/WF4";

            // Variable tanımlamaları Sequence aktivitesinin Variables koleksiyonuna dahil edilir.
            squareRootFlow.Variables.Add(number);
            squareRootFlow.Variables.Add(square);
            squareRootFlow.Variables.Add(corHandle);

            // Akışın içerisinde yer alan ilk aktivite Receive tipindendir.
            Receive receive1 = new Receive
            {
                 OperationName="SquareRoot",
                 DisplayName="Square Root Calculation",
                 ServiceContractName = ns + "CalculatorService",
                 CanCreateInstance=true,
                 Value=new OutArgument<double>(number),
                 AdditionalCorrelations={
                      {"ChannelBasedCorellation",new InArgument<CorrelationHandle>(corHandle)}
                  }
            };

            // Receive aktivitesi ile istemciden gelen sayısal değer number değişkenine alındıktan sonra çalışan InvokeMethod aktivitesi ile Calculator isimli sınıf içerisindeki FindSquareRoot metodu çağırılır.            
            InvokeMethod<double> invokeMethod1 = new InvokeMethod<double>
            {
                 TargetType=typeof(Calculator),
                 MethodName = "FindSquareRoot",
                 Result=new OutArgument<double>(square) // FindSquareRoot metodunun çalıştırılması sonucu elde edilen sonuç square isimli değişken tarafında yakalanabilecektir.
            };
            // Metoda parametre olarak number değişkeninin değeri gönderilir. Bu değer Receive aktivitesi içerisinde set edilmiş olup istemci tarafından gelmektedir.
            invokeMethod1.Parameters.Add(new InArgument<double>(number));

            // Ekrana bilgilendirme yapılır. Bu bilgilendirmede istemciden akışa gelen sayının karekökü yazdırılmaktadır.
            WriteLine writeLine1 = new WriteLine
            {
                Text=new InArgument<string>(e=>String.Format("Sonuç {0}",square.Get(e).ToString()))
            };

            // Aktivitelere sırasıyla Sequence aktivitesi içerisine ilave edilir
            squareRootFlow.Activities.Add(receive1);
            squareRootFlow.Activities.Add(invokeMethod1);
            squareRootFlow.Activities.Add(writeLine1);
           
            #endregion

            #region Workflow Servis Oluşturma İşlemleri

            // Workflow' un Service olarak host edilmesi için gerekli hazırlıklar başlar.
            // İlk olarak bir Service nesne örneği oluşturulur
            Service service = new Service();

            // Service nesne örneğinin Implementation özelliğine WorkflowServiceImplementation türünden bir referans atanırken, Body özelliğine yukarıda oluşturulan Workflow aktivitesi bildirilir
            service.Implementation = new WorkflowServiceImplementation
            {
                Name = ns+"CalculatorService",
                Body=squareRootFlow               
            };

            // Sonuçta Workflow bir WCF servisi olarak host edileceğinden bir Endpoint bilgisine sahip olmalıdır
            // Bu nedenle basit bir Endpoint bildirimi yapılır
            // Örnekte Tcp bazlı servis iletişimi tercih edilmiştir
            service.Endpoints.Add(new Endpoint
            {               
                Uri = new Uri("SquareRoot", UriKind.Relative), //Address bilgisi
                Binding=new NetTcpBinding(), // Binding bilgisi
                ServiceContractName = ns + "CalculatorService" // Contract bilgisi(Burada verilen isim ile Receive aktivitesine ait ServiceContractName özelliğindeki değerler aynı olmalıdır. Aksi halde Exception2 alınır)
            });

            #endregion

            // Workflow Service' i çalıştıracak olan WorkflowServiceHost nesnesi örneklenir
            // İlk parametre host edilecek olan servistir. İkinci parametre ise servisin host edileceği adres bilgisidir.
            WorkflowServiceHost host = new WorkflowServiceHost(service, new Uri("net.tcp://localhost:4501/Calculator/Workflows/"));
            host.Open(); // Host açılır

            Console.WriteLine(host.State.ToString()); //Hostun durumu hakkında bilgilendirme yapılır
            Console.WriteLine("Kapatmak için bir tuşa basınız.");
            Console.ReadLine();

            host.Close(); //Host kapatılır
        }
    }

    class Calculator
    {
        public double FindSquareRoot(double number)
        {
            return Math.Sqrt(number);
        }
    }
}

Kod içerisinde işleyiş ile ilişkili gerekli açıklamalar bulunmaktadır.

Ancak geliştirme sırasında dikkat edilmesi gereken bazı durumlar da vardır. Örneğin XNamespace tipinin kullanılmaması, bunun yerine URL bilgisinin text tabanlı olarak atanması halinde çalışma zamanında aşağıdaki ekran görüntüsünde yer alan XmlException istisnası alınacaktır.

Exception 1 (Xml Namespace kullanılmaması halinde);

Diğer yandan ServiceContractName değerinin hem Endpoint hemde Receive aktivitesi için aynı olması gerekmektedir ki aksi durumda aşağıdaki ekran görüntüsünde yer alan çalışma zamanı istisnası fırlatılmaktadır.

Exception 2 (ServiceContractName' lerin Receive aktivitesi ve Endpoint içerisinde farklı olmaları halinde);

Gelelim istemci tarafına. Bu noktada kendimizi yine zora sokuyor olacağız. Yell Elimizde servisin Publish edilen bir WSDL dökümanı olmadığını ve herhangibir şekilde Proxy üretimi için bir araç kullanmadığımızı farz edeceğiz. Bu durumda istemci tarafındaki proxy sınıfının ve hatta istemciden servis tarafına gönderilecek olan mesaj sözleşmesinin manuel kod ile oluşturulması gerekmektedir. Aynen aşağıda olduğu gibi;

İstemci tarafı sınıf diagramı;

ve kodları;

using System;
using System.ServiceModel;

namespace ClientApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Başlamak için bir tuşa basınız");
            Console.ReadLine();

            // Binding oluşturulur
            NetTcpBinding binding = new NetTcpBinding();
            // Endpoint tanımlaması yapılır. Adres bilgisinin servisin yeri ve çağırılmak istenen operasyonun adıo yer almaktadır. Bu bilgileri service tarafındakiler ile aynı olmalıdır
            EndpointAddress endpoint = new EndpointAddress("net.tcp://localhost:4501/Calculator/Workflows/SquareRoot");
            // Kanal fabrikası üretilir. İlk parametre bağlayıcı ikinci parametre ise EndPoint bilgisidir
            ChannelFactory<CalculatorService> factory = new ChannelFactory<CalculatorService>(binding, endpoint);
            // Kanal fabrikasından yararlanılarak istemcinin kullanacağı Transparant Proxy nesnesi üretilir.
            CalculatorService proxy = factory.CreateChannel();

            // Talep oluşturulur ve parametre olarak servis tarafındaki akışın ilk aktivitesi olan Receive aktivitesinin alacağı sayısal değer bildirilir
            SquareRootRequest request = new SquareRootRequest()
            {
                Number = 16
            };
            // Operasyon çağırılır
            proxy.SquareRoot(request);

            Console.WriteLine("İşlemler tamamlandı. Kapatmak için bir tuşa basınız.");
            Console.ReadLine();
        }
    }

    // Talep olarak gidecek mesaj sözleşmesi tanımlanır
    [MessageContract(IsWrapped = false)]
    public class SquareRootRequest
    {
        [MessageBodyMember(Namespace= "http://schemas.microsoft.com/2003/10/Serialization/",Name = "double")]
        public double Number { get; set; }
    }

    // Proxy tanımlaması yapılır
    // Workflow Servisi varsayılan isim alanı(namespace) ile sunulmadığından Namespace bildiriminin istemci tarafındaki proxy için açık bir şekilde yapılması gerekmektedir.
    [ServiceContract(Namespace = "http://www.buraksenyurt.com/WF4")]
    interface CalculatorService
    {
        [OperationContract(IsOneWay=true)] // Operasyonun OneWay olduğunu belirtmessek Exception3' teki çalışma zamanı hatasını alırız.
        void SquareRoot(SquareRootRequest request);
    }
}

Yine istemci tarafı açısından olaya baktığımızda dikkat etmemiz gereken bazı hususlar olduğu ortadadır. Örneğin, Workflow Service içerisinden geriye bir dönüş yapılmamaktadır. Bir başka deyişle tek yönlü bir istek söz konusu olabilir. Bu nedenle istemci tarafındaki SquareRoot metoduna uygulanan OperationContract niteliğinde IsOneWay özelliğine true değeri atanmıştır. Bu yapılmadığı takdirde çalışma zamanında aşağıdaki istisna mesajı ile karşılaşılacaktır.

Exception 3 (Service operasyonunun OneWay olduğunu belirtmediğimiz durumda);

Piuuuuuvvvvv!!! Smile Biraz uğraştık ama faydalı bir çalışma oldu sanıyorum ki. Gerçi elde edeceğimiz sonuç hiç bir anlam ifade etmesede, bir Workflow Service' in kod yardımıyla nasıl oluşturulabileceğini ve kullanılacağını görmüş olduk. İşte bir yazılımcı olarak tırmandığımız dağın zirvesindeki görüntü...

Ne kadar muhteşem değil mi? Tongue out Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

WithWCF.rar (42,98 kb)

Kişisel Not : Örnekler bildiğiniz üzere Visual Studio 2010 Beta 1 sürümünde ve .Net Framework Beta 1 üzerinde geliştirilmektedir. Beta 2 ile arasında farklılıklar olabilir. Hatta Relase sürümde çok daha fazla farklılık görülebilir.

Tags:   , , ,
Categories:   WF 4.0 Beta 1
Actions:   E-mail | del.icio.us | Permalink | Yorumlar (0) | Comment RSSRSS comment feed
Bookmark and Share

WF 4.0 - Veri(Data)[Beta 1]

Pazartesi, 12 Ekim 2009 23:48 by bsenyurt

Merhaba Arkadaşlar,

Bir süredir Workflow Foundation 4.0 ile ilişkili blog yazılarını, makaleleri ve görsel dersleri takip etmekteyim. Bu araştırmalarım sırasında Workflow Foundation 4.0 modelinde veriye(Data) olan bakış açısının WF 3.X sürümüne göre oldukça farklılaştığını gördüm. WF 3.X tabanlı modelde, aktivite bazlı verileri temsil etmek için genellikle standart sınıf özelliklerinden(Property) veya WPF' ten esinlenilen bağımlı özelliklerden(Dependency Property) yararlanılmaktadır. WF 4.0 modelinde ise veriyi temsil etmek amacıyla Variable veya Argument türevli tiplerden yararlanıldığı görülmektedir. Bu tiplerin türevleri veriyi doğrudan tutmazlar. Bunun yerine veriyi tanımlar ve elde edilmesini sağlarlar. Verinin içeriği Workflow üzerinde bir yerlerde saklanmaktadır. Bu noktada Variable kavramını aynen Imperative programlama dillerindeki kullanılış biçimi ile düşünebiliriz. Bu nedenle Variable veya Argument türevli tipler tanımlandıkları scope dahilinde kullanılabilirler. Dolayısıyla bir Variable bir Workflow için kök seviyede(Root Level) tanımlanırsa tüm alt aktiviteler tarafından kullanılabilir. Bugünkü örneğimizde bir önceki blog yazımızda olduğu gibi kod bazlı bir Workflow örneği geliştirecek ve Variable kullanımı ile veriyi nasıl ele alacağımızı görmeye çalışacağız. Aslında .Net 4.0 içerisindeki oluşuma baktığımızda Variable<T> için Variable isimli bir abstract sınıftan türetme yapıldığını görebiliriz. Aşağıdaki sınıf diyagramında bu durum görülmektedir.

Burada dikkat edilmesi gereken noktalardan birisi, Variable<T> tipinin sealed olarak tanımlanmış olmasıdır. Yani kendisinden türetme yapamayız. Diğer yandan Variable tipi abstract bir sınıftır ve bu nedenle kendisinden türetme yapılarak özel Variable türevlerinin üretilmesi mümkündür.

Evet bu kadar laf kalabalığından sonra yeni veri modeline kısaca bakmaya çalışalım. Bu amaçla Visual Studio 2010 Beta 1 üzerinden açtığımız basit bir Console projesini kullanıyor olacağız. Projede Workflow alt yapısını ele almak için tek yapmamız gereken System.Activities isimli assembly' ın referans edilmesi olacaktır.

Uygulama kodlarını ise aşağıdaki gibi geliştirdiğimizi düşünebiliriz.

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

namespace UsingVariables
{
    class Program
    {
        static void Main(string[] args)
        {
            // Değişken tanımlanır. Tanımlanırken varsayılan olarak(Default) bir nesne örneğide verilir
            Variable<Player> firstPlayer = new Variable<Player>
            {
                Name = "FirstPlayer",
                Default = new Player { Name = "Zen-R2", Location = new Location { X = 10, Y = 12, Altitude = 100 }, PlayerType = PlayerType.Computer, TotalPoint=10 }              
            };

            // Bir Sequence aktivitesi tanımlanır. Root aktivite.
            Sequence gameFlow = new Sequence();
            // Variable Sequence nesne örneğinin Variables koleksiyonuna eklenir.
            gameFlow.Variables.Add(firstPlayer);

            // Atama işlemi için basit bir Assign aktivitesi gameFlow isimli Sequence nesne örneğinin Activities koleksiyonuna eklenir.
            // To kısmında kime atama yapılacağı belirlenir. Tasarım zamanından farklı olarak bir expression kullanılır. firstPlayer isimli değişkenin işaret ettiği veri alanında TotalPoint değeri alınır.
            // Value özelliğine verilen değer ile atama yapılır. Atamada o anki variable' ın TotalPoint değeri 10.1 birim arttırılmaktadır.
            gameFlow.Activities.Add(
                new Assign<double>() {
                     To=new OutArgument<double>(v=>firstPlayer.Get(v).TotalPoint),
                     Value=new InArgument<double>(v=>firstPlayer.Get(v).TotalPoint+10.1)
                }
                );

            // Örnek olarak birde Location özelliğinin işaret ettiği içerik değiştirilmektedir.
            gameFlow.Activities.Add(
                new Assign<Location>()
                {
                    To = new OutArgument<Location>(v => firstPlayer.Get(v).Location),
                    Value = new Location { X = 10, Y = 15, Altitude = 99 }
                }
                );

            // Ekrana bilgi yazdırmak için WriteLine tipinden bir aktivite daha eklenir
            // Text özelliğinde ekrana bilgi yazdırabilmek için InArgument nesne örneğinden yararlanılır ve o anki firstPlayer variable' ının Name ve TotalPoint değerleri yazdırılır.
            gameFlow.Activities.Add(
                new WriteLine {
                     Text=new InArgument<string>(v=>String.Format("{0} isimli oyuncunun puanı {1}. Deniz seviyesinden yüksekliği {2}",firstPlayer.Get(v).Name,firstPlayer.Get(v).TotalPoint.ToString(),firstPlayer.Get(v).Location.Altitude))
                }
                );
           
            // gameFlow isimli Workflow çağırılır ve başlatılır
            WorkflowInvoker.Invoke(gameFlow);
        }
    }

    class Player
    {
        public string Name { get; set; }
        public Location Location { get; set; }
        public PlayerType PlayerType { get; set; }
        public double TotalPoint { get; set; }
    }

    class Location
    {
        public double X { get; set; }
        public double Y { get; set; }
        public double Altitude { get; set; }
    }

    enum PlayerType
    {
        Computer,
        Human,
        Hybrid
    }

Örneğin herhangibir gerçek hayat işlevselliği bulunmamaktadır ancak kullanıcı tanımlı bir tipten(örneğimizde Player isimli sınıf) oluşturulan Variable' ın Sequence tipinden bir nesne örneği içerisinde nasıl değerlendirilebildiği açık bir şekilde görülmektedir. Aslında konsept son derece basittir. Variable<T> tipinden bir nesne örneği tanımlanırken Default özelliği ile ilk değer ataması yapılmaktadır ki zorunlu değildir. Yani Variable' ın içeriği akışa dışarıdan gelebilir. Sonrasında bu değişkenin kullanılmak istenen scope içerisine bildirilmesi gerekmektedir. Örnekte gameFlow isimli Sequence aktivite örneğinin Variables koleksiyonuna yapılan ekleme ile bu bildirim gerçekleştirilmektedir. Nitekim söz konusu değişkeninin, içeride yer alan tüm aktiviteler tarafından kullanılması istenmektedir.

Kodun ilerleyen kısımlarında sembolik olarak firstPlayer isimli Variable<Player> tipine ait özelliklerin bazılarında değişiklikler yapılmıştır. Bu işlemler için Expression' lardan yararlanılmaktadır. Değiştirme işlemlerinde dikkat edileceği üzere Assign<T> isimli aktiviteden yararlanılmaktadır. İlk Assign aktivitesine ait To ve Value özelliklerinde InArgument ve OutArgument tiplerinden yararlanılarak verinin elde edilmesi ve değer ataması işlemleri yapılmaktadır. İkinci Assign aktivitesinde ise generic olarak Location tipi belirtildiğinden Value kısmında doğrudan yeni bir Location nesne örneğine atama yapılmaktadır. Her iki aktivite içinde dikkat edilmesi gereken noktalardan birisi, değeri alınmak istenen özelliğe erişilirken firstPlayer isimli değişkenin Get metodundan yararlanılıyor olmasıdır. Zaten kodlama sırasında intelli-sense özelliği devreye girmekte ve Get metodu sonrasında kullanılabilecek tüm Player tipi özellikleri gösterilmektedir. Programda son olarak Workflow örneğinin çalıştırılması sağlanmaktadır. Uygulamanın çalışma zamanı görüntüsü aşağıdaki gibi olacaktır.

Dikkat edileceği üzere başlangıçta 10 olan puan 10.1 birim arttırılmış ve Location tipi üzerinde tutulan Altitude değeride 1 birim azaltılmıştır.

Elbette Workflow 4.0' ın WPF tabanlı bir IDE kullanıyor olması göz ardı edilemez. Bir başka deyişle söz konusu Variable ekleme işlemleri aslında tasarım zamanında çok daha kolay bir şekilde gerçekleştirilebilmektedir. Ancak yazıyı hazırladığım zaman diliminde kullandığım ve public olan Visual Studio 2010 Beta 1 sürümüne ait WF designer ne yazıkki IDE tekrardan başlatılmasına nedenl olmaktadır. Undecided Bu nedenle şimdilik kod tarafı ile idare etmeniz gerekiyor.Wink Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

UsingVariablesV2.rar (26,44 kb)

Tags:   ,
Categories:   WF 4.0 Beta 1
Actions:   E-mail | del.icio.us | Permalink | Yorumlar (0) | Comment RSSRSS comment feed
Bookmark and Share

WF 4.0 - Workflow Yapısı ve Object Initialization[Beta 1]

Pazartesi, 5 Ekim 2009 08:29 by bsenyurt

Merhaba Arkadaşlar,

Workflow Foundation 4.0 ile ilgili yenilikleri araştırdığım şu günlerde, yaptığım araştırmalar sırasında ilgimi çeken noktalardan biriside, bir Workflow' un kod tarafında tek bir ifade satırı ile oluşturulabiliyor olmasıydı. Burada Workflow sisteminin hiyerarşik yapısının, Object Oriented seviyede etkili bir kullanımının söz konusu olduğunu belirtmek isterim. Ancak konuya çekirdek bilgilerden başlayarak yaklaşmakta yarar var.

Workflow içerisindeki lego parçalarının temelini aktiviteler(Activities) oluşturmaktadır. WF 4.0 mimarsinde tüm aktiviteler WorkflowElement bileşeninden türemektedir. Aktivitleri aslında Workflow' ların iş birimleri(Work Units) olarak düşünebiliriz. İki ve daha fazla iş biriminin bir araya gelerekten aktivite oluşturmaları da çok doğal olarak mümkündür. Aslında buradan ilginç olan bir tespit vardır. Bir aktivite, hiyerarşinin en üstünde yer alıyorsa bir Workflow halini alır ve kendi içerisinde pek çok aktiviteyi barındırabilir. Bunu şu şekilde de düşünebiliriz; "Bir metodun kendi içerisinde birden fazla metodu çağırması". Nitekim metodun kendi içerisinde çağırdığı ardışıl fonksiyonlar, yukarıdan aşağıya doğru hareket eden bir iş akışını oluşturmaktadır. Bu açıdan bakıldığında Workflow Foundation olmasa dahi, kod bazında iş akışlarının en temel seviyede fonksiyonlar yardımıyla gerçekleştirilebileceğini bilmek gerekir(Hatta çalıştığım son projede bu tip bir mimari kullanılmaktadır). Workflow Foundation 3.X sürümünden beridir Sequence gibi üst seviye(Top Level) aktiviteleri barındırmaktadır. Bunlar Top Level olarak kullanıldıklarında bir Workflow' u ifade etmektedir. Tabiki Sequence örnekleri kendi içlerinde başka Sequence örnekleri de barındıralabilirler. 

Burada ilginç olan noktalardan birisi, Workflow' ları sadece tek bir ifade içerisinde oluşturabilmemizdir. Surprised Bunun için nesne başlatıcılarından(Object Initializers) yararlanılmaktadır. Nasıl mı? Gelin Visual Studio 2010 Beta 1 üzerinde basit bir Console Application oluşturup Program.cs içeriğini aşağıdaki gibi oluşturduğumuz düşünelim.

Not: Console uygulaması üzerinde Workflow aktivitileri kullanılacağından, projeye System.Activities.dll Assembly' ının referans edilmesi gerekmektedir.

namespace WorkflowStructure
{
    using System;
    using System.Activities;
    using System.Activities.Statements;

    class Program
    {
        static void Main(string[] args)
        {
            // Yeni bir Sequence aktivitesi oluşturulur. Top Level olduğu için workflow' un kendisidir.
            // Tek satırlık ifade içerisinde bir Workflow tanımlandığına dikkat edilmelidir
            Sequence flow1 =new Sequence
            {
                DisplayName = "Hello Workflow World",
                // Activity tipinden elamanlar taşıyan koleksiyonun içerisinde alt aktiviteler tanımlanır
                Activities =
                {
                    new WriteLine{ DisplayName="Workflow Start", Text="Starting..."},//Basit bir WriteLine aktivitesi
                    new InvokeMethod{ DisplayName="5 Times Say Hello", MethodName="SayHello", TargetType=typeof(Logic)} // Logic sınıfından SayHello metodunu çağıracak olan InvokeMethod aktivitesi tanımlanır
                }                
            };

            WorkflowInvoker.Invoke(flow1); // Workflow örneği çalıştırılır :)
        }
    }

    // Harici bir sınıf ve metod
    class Logic
    {
        public static void SayHello()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("\tHello");
            }
        }
    }
}

Bu örnek kod parçasında Sequence tipinden bir nesne örneği oluşturulmaktadır. Nesne oluşturulurken, Activities özelliği içerisinde alt aktivite örnekleri kullanılmıştır. Activities özelliği Collection<Activity> tipinden bir koleksiyonu işaret etmektedir. Dolayısıyla herhangibir Activity referansına ait örnek, söz konusu koleksiyona eklenerek akışın gövdersi oluşturulabilir. Örnekte ilk olarak System.Activities.Statements isim alanında yer alan WriteLine aktivite bileşeni kullanılarak ekrana basit bir çıktı verilmektedir. Takip eden adımda, InvokeMethod aktivite bileşeni kullanılmaktadır. Bu bileşenin özellikleri ile, Logic sınıfı içerisinde yer alan SayHello metodunun çalıştırılacağı belirtilmiştir(Burada TargetType veya TargetObject özelliklerinden birisinin mutlaka set edilmesi gerekir. Böylece MethodName özelliğinde belirtilen fonskiyonun çalışma zamanında hangi tipe ait nese örneği içerisinden çağırılacağı belirtilmiş olur) Tabiki flow1 isimli Sequence nesne örneğinin çalıştırılması için WorkflowInvoker tipi üzerinden static Invoke metodu kullanılmıştır. Olaya noktalı virgüller açısından baktığımızda sadece iki satırda bir Workflow' un tasarlanıp yürütüldüğünü ifade edebiliriz. Ancak görsel bir tasarım ortamının olması elbetteki çok daha mühimdir ve tercih edilmelidir. Nitekim gerçek hayat çözümlerindeki iş akışlarının çoğu bu kadar basit Workflow örnekleri ile ifade edilememektedir. Geliştirdiğimiz Workflow herhangibir anlam içermesede Smile önemli olan, tek satırlık bir ifade ile object initializer kavramından da yararlanarak, bir Workflow örneğinin tesis edilebileceğinin farkında olmaktır. Örneği yürüttüğümüzde çalışma zamanı için aşağıdaki sonuçları aldığımızı görürüz.

Görüldüğü üzere akış başarılı bir şekilde işletilmiştir. Bu kısa yazımızda Workflow yapısının kod tarafında Object Initializers yardımıyla ele alınışını incelemeye çalıştık. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

WorkflowStructure.rar (19,38 kb)

Tags:  
Categories:   WF 4.0 Beta 1
Actions:   E-mail | del.icio.us | Permalink | Yorumlar (0) | Comment RSSRSS comment feed
Bookmark and Share

5000 Feet Yüksekten Workflow Foundation 4.0[Beta 1]

Perşembe, 1 Ekim 2009 22:54 by bsenyurt

Merhaba Arkadaşlar,

Paraşütle atlamak gerçekten zevkli olsa gerek. Yerden binlerce feet(1 feet=30,48 cm) yüksekten atlayıp özgür bir şekilde kendinizi yer çekimi gücüne bırakıp, saniyeler boyunca serbest düşüşü yaşamak...Size yandaki resimde atlayan kişinin ben olduğumu söylemek isterdim ama ne yazık ki değilim. Olmayı istermiydim bilemiyorum. Oldukça yüksek görünüyor. Sealed Bir paraşütçü için en güzel duygulardan birisi sanıyorum ki atladığı noktadan itibaren altındaki Dünyayı görebildiği kadar yüksekten izleyebilmenin verdiği mutluluktur. Tabiki atlanılan noktadan aşağıya doğru düştükçe ve paraşütü çekme noktasına yaklaştıkça alttaki Dünyanın resimlerinin daha da büyüdüğü çok açık bir gerçektir. Büyük bir dikdörten alan...Sonrasında içerisinde başka geometrik şekiller...Sonrasında bu geometrik şekiller içerisinde daha da netleşen çayırlar, dağlar, kayalar, yollar, binalar...Sonrasında bir anlık yavaşlama ve sakin bir şekilde(bazende hızlı bir şekilde) yere ayak basmak. Kısaca yaşanan bu duruma "yüksekten görülebileceği kadar büyük bir alanı görüp, yaklaştıkça daha fazla detay fark edebilmek durumu" Cool adını verebiliriz. Şimdi bu noktaya nereden vardım diyebilirsiniz. Hemen açıklayayım.

Yazılım ile ilişkili pek çok kaynakta şu tip başlıklar görmüşsünüzdür. 50 bin feet yukarıdan X mimarisi...İşte bizde bu felsefeyi bu günkü blog yazımızda kullanıyor olacağız. Ama biraz daha alçak mesafeden Wink

5000 feet yukarıdan fotoğraflandığında, WF 4.0 modelinin ne gibi özellikleri dikkat çekiyor? İşte görülen tespitler...

  • WF 3.X' teki kısıtlı olan XAML bazlı Workflow modeli yerini tam desteklenen(Full XAML Based) Workflow modeline bırakıyor. Böylece bir Workflow' un basit bir text editorü yardımıyla tamamen dekleratif(declerative) olarak geliştirilmesi ve çalışma zamanına devredilmesinin mümkün olabildiğini (Bu özelliğin en önemli açılımlarından biriside, çeşitli 3ncü parti araçların XAML içeriklerini ele alarak akışları koda girmeden değiştirebilecek olmaları. Oslo, Dublin ve Quadrant üçlemesinin önemle üzerinde durduğu noktalardan biriside zaten bu dekleratif açılım.)
  • WF 3.X' te daha zor olan özel aktivite(Custom Activity) geliştirme yeteneğinin WF 4.0 için daha da basitleştirildiğini ve bu amaçla temel workflow hiyerarşisinde de değişikliklere gidildiğini ve ata tip olarak WorkflowElement ve kendisinden türeyen Activity, CodeActivity, NativeActivity gibi alt tiplerin geliştirildiğini,
  • WF 3.X' te ilkel ve daha az genişletilebilir olan kurallar motorunun(Rules Engine) dahada zenginleştirilmiş olduğunu,
  • Yeni Workflow bileşenlerinin System.Activities.* assembly' ları altında olduğunu ama .Net Framework 4.0 içerisinde yer alan ve geriye uyumluluk(Backwards Compatibility) amacıyla kullanılan Workflow bileşenlerininse System.Workflows.* assembly' ları içerisinde yer aldığını, bu anlamda WF 4.0' da geriye uyumluluğa da büyük önem verildiğini,
  • Tamamen WPF(Windows Presentation Foundation) temelli bir tasarım ortamının söz konusu olduğunu ve bu sayede geliştirici deneyiminin dahada zenginleştiğini,
  • Bir Workflow içerisine veya dışarısına yapılan veri akışlarının(Data Flow) çok daha kolay ele alınması için Designer desteği ile birlikte Arguments kavramının geldiğini,
  • Bir aktivitenin kendi içerisinde veriyi saklaması ve farklı seviyedeki alanlarda(Scope) kullanabilmesinde rol oynayan Variables kavramını ayrıca Arguments kavramında olduğu gibi, Variables içinde Designer desteğinin olduğunu,
  • Bir veya daha fazla input argümanı alıp bunlar üzerinde çeşitli operasyonlar gerçekleştiren ve geriye değer döndürebilen ifadeler(Expressions) yazılabildiğini, üstelik bunların XAML bazlı olabildiğini,
  • FlowChart, ForEach, Parallel, ParallelForEach (Parallel versiyonların Ekimdeki PDC' de yayınlanacak sürümde olması bekleniyor) ve daha pek çok aktivite tipi ile zengineştirilmiş olan temel aktivite kütüphanesi(Base Activity Library) ni,
  • Sequential ve State Machine arasında duran ama geliştirici ve iş analistlerinin, bilinen iş akışı tasarım modeline çok yakın olması nedeniyle kolayca kullanabildiği yeni Flow Chart modelini,
  • Workflow ve Activity' ler için Unit Test' lerin kolayca geliştirilebiliyor olduğunu,
  • Workflow' ların, dışarıdaki Activity' ler ile haberleşmenin dahada kolaylaştırılmış olduğunu,
  • 4.0 versiyonunda evlenebilmerleri için WCF ve WF tarafında;
    • Workflow tarafında yenilenen çalışma zamanı motoru bulunduğunu,
    • Workflow Service' lerin host edilebiliyor olduğunu,
    • Workflow' lar içerisinde XAML bazlı olarak WCF servis materyallerinin tanımlanabiliyor olduğunu,(Service Contract, Data Contract, EndPoint vb...)
    • Visual Studio' da Workflow Service' ler için Add Service Reference desteğinin getirildiğini,
    • Yeni mesajlaşma aktivitileri(SendAndReceiveReply, ReceiveAndSendReply gibi) ve bunların mesaj korelasyon(Message Corellation) desteğine de sahip olduğunu,
  • WF 3.X ile yazılmış olan Workflow' ların 4.0 çalışma zamanı tarafından da yürütülebildiğini,
  • WF 3.X tarafından yazılmış olan aktivitelerin sarmalanarak(Wrap) 4.0 içerisinde de kullanılabildiğini,(Interop activity)
  • Ayrıca WF 3.0' dan geçiş yapacaklar için bir klavuzun bulunduğunu,
  • Daha detaylı bilgiler içinse şu adrese başvruabileceğimizi,

görüyoruz. Vooovvvvv!!!! Artık paraşütümüzü açalım mı ne dersiniz? Cool Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

Tags:  
Categories:   WF 4.0 Beta 1
Actions:   E-mail | del.icio.us | Permalink | Yorumlar (0) | Comment RSSRSS comment feed
Bookmark and Share