Basit bir wcf servis örneği

Ne zaman servis yazmalı?


Yazılımcılar olarak üzerinde çalıştığımız işlev birden çok uygulama tarafından kullanılacaksa, bu işlevi yeninden kullanılabilir ve diğer sistemlere monte edilebilir bir şekilde üretmek pek tabii ki akıl karı bir iş olacaktır. Peki bunu başarmak nasıl mümkün olabilir?

 

Örnek olarak bir firmanın faturalama işlemini ele alalım. Üzerinde çalıştığımız otomasyon uygulamasının bir faturalama işlevi sağlaması gerektiğini varsayalım. Bu faturalama işlevini nesne yönelimli programlama prensiplerini kullanarak bir sınıf (veya sınıflar)içinde modüler bir biçimde toplayabiliriz. Ancak bu işlevi başka bir uygulamada kullanmak için yazdığımız sınıfları diğer uygulamaya kopyalamamız gerekecektir. Aynı sınıfların farklı projelerde ayrık bir biçimde var olması demek, ileride faturalama işlevinde gerekli olabilecek değişikliklerin birden çok yerde güncellenme zorunluluğu yaratacağından, bu durum pek istenen bir durum olmayacaktır.

 

Faturalama işlevi sağlayan sınıfları bileşen yönelimli programlama prensiplerini kullanarak bir bileşen kütüphanesi (mesela bir DLL paketi)içinde toplayabiliriz ve nerede faturalama işlemine ihtiyaç duyarsak, geliştirdiğimiz paketi kullanabiliriz. Bu yaklaşımla yukarıda bahsettiğimiz problemi elimine etmiş olmakla birlikte, şimdi de kurulum ve entegrasyon problemi ile baş etmek durumunda kalmış oluruz. Ne zaman DLL paketinde bir değişiklik yapılırsa, yeni versiyonun kurulumu ve başarılı entegrasyonu gerekecektir. Bu da DLL cehennemi adıyla da bilinen probleme meyilli bir alternatiftir.

 

Faturalama işlevi sağlayan tüm sınıf ve kütüphaneleri servis yönelimli programlamaprensiplerini kullanarak bir servis olarak geliştirirsek, yukarıda bahsi geçen problemleri büyük ölçüde elimine etmiş ve söz konusu işlevi diğer uygulamalardan bağımsızbir şekilde ve merkezi bir noktada toplamış oluruz. Kurulum problemi tek bir ortama (servis sunucusuna) inmiş ve entegrasyon problemleri büyük ölçüde “servis” ve “kullanıcıları” tarafından icabet edilen kontratlara indirgenmiştir. Servis olarak geliştirilen işlevler birtakım standartlara uygun olmak koşulu ile hangi dil ve platformda geliştirilirse geliştirilsin, değişik platform ve teknolojiler tarafından kullanılabilirler.

 

WCF ile örnek servis projemize geçmeden önce servis ve kullanıcıları arasındaki iletişim nasıl olur, çok fazla detaya girmeden bunu inceleyelim. Kullanıcı tarafından bir servisin kullanılabilmesi için kullanıcı tarafında yerel olarak var olacak vekil (proxy) yapılara ihtiyaç vardır. Vekil yapılar genellikle servis tarafından ilan edilen meta-verilerle tanımlanırlar. Kullanıcılar bu meta-verilerle tanımlandığı şekliyle vekil yapıları oluştururlar. Bu vekil yapılar sayesinde “kullanıcı kod” servis tarafında var olan asil yapıları sanki yerel yapılar gibi yerel olarak nesnelleştirir. Vekil yapılar aracı görevi görürler ve bu vekil yapılara ait metotlar çağırıldığında iletişim kanalları aracılığıyla (mesela HTTP) servis tarafında vekil yapıların asil kopyaları yaratılırlar. Asil yapılar servis tarafında işlevlerini tamamladıklarında yine aynı iletişim kanalları aracılığıyla kullanıcı tarafına geri taşınıp oradaki orijinal vekil yapıları güncellerler. WCF’in gücü ve potansiyeli bu karmaşık iletişim ve entegrasyon aktivitelerini soyutlamasından ve programcıdan saklayabilmesinden gelmektedir. Vekil yapıların servis sunucusuna intikali, servis tarafında oluşan sonuçların kullanıcı tarafına geri iadesi gibi teknik olarak karmaşık konular programcının değil WCF teknolojisinin sorumluluğundadır. WCF programcıya araca değil amaca (yani gerçek işleve) odaklanma lüksü sağlar.

 

Servis yönelimli programlama konusuna özet bir giriş yaptıktan sonra, Visual Studio 2010 ve .NET Framework 4.0 ile bir faturalama modülü yazalım ve bu modülü bir WCF servisi olarak diğer uygulamaların kullanıma sunalım.

Fatura.cs

public class Fatura

{

publicint FaturaID { get; set; }

publicstring MusteriReferans { get; set; }

publicdouble ToplamMiktar { get; set; }

publicstring FaturaBilgi { get; set; }

}

 

IFaturaServis.cs

public interface IFaturaServis

{

Fatura FaturaKaydet(Fatura fatura);

Fatura FaturaBul(int faturaID);

}

FaturaServis.cs

public class FaturaServis : IFaturaServis

{

public Fatura FaturaKaydet(Fatura fatura)

{

//normalde burada faturayı veri tabanına kaydetmeliyiz

// örneğimizde bu kısma girmiyoruz

// test edebilmek için FaturaID alanını rastgele bir sayıyla dolduralım

fatura.FaturaID = (newRandom()).Next(1000);

return fatura;

}

public Fatura FaturaBul(int faturaID)

{


//yine örneğimizi basit tutmak adına

// test edebilmek için rastgele bir fatura gönderelim


return new Fatura

{

FaturaID = (newRandom()).Next(1000),

MusteriReferans = “Deneme “ + faturaID + ” Ltd.”,

ToplamMiktar = 11.22 * faturaID,

FaturaBilgi = “Test faturası “ + faturaID

};

}

}


İşlevsel olarak pek bir değeri olmamakla birlikte örnek olarak kullanabileceğimiz modüler bir yapıya ulaştık. Ancak bu yapının henüz WCF ve servisler ile bir yakınlığı yok. Ulaştığımız bu yapıyı
FaturaServis sınıfı aracılığıyla dış dünyanın kullanımına sunmak istiyoruz. Bu aşamada akla şu soru gelebilir:

 

Neden IFaturaServis ara-yüzünü tanımladık ve FaturaServis sınıfını bu ara-yüzden kalıt alarak yazdık? Aslında FaturaServis sınıfını normal ve bağımsız bir sınıf olarak tanımlayarak da bir WCF servisi yaratmak mümkündür ancak WCF servis sınıflarını bir ara-yüz yapısından kalıt alarak yaratmak yazılım mühendisliğinin önemli prensiplerinden biri olan yapılar arasındaki bağımlılıklar somut sınıflardan ziyade soyut yapılar (abstract sınıflar veya ara-yüzler) üzerinden olmalı prensibinden dolayıdır. Bu bize özellikle servisimizin test edilebilirliği açısından büyük yarar sağlamasının yanı sıra servis kullanıcısı tarafında gerçekleşecek vekil yapıların üretimini basitleştirme adına da faydalı olacaktır.

 

Projemize bir servis dosyası (.svc) eklemeden önce yapmamız gereken önemli bir iş daha var. O da projemizde mevcut olan ve dış dünya tarafından bilinmesi gereken yapıların bir takım özel sınıf ve metot nitelikleri ile dekore edilmesidir. Şimdi bu özel nitelikleri tanımlayalım.

 

ServiceContract:

DLL: System.ServiceModel.dll

Tam kalifiye adı: System.ServiceModel.ServiceContract

Servisi tanımlamak için kullanılır. Eğer servisimizi (bu örneğimizde olduğu gibi) bir ara-yüz üzerinden yazıyorsak bu ara-yüze, veya direkt olarak normal (bağımsız) bir sınıf aracılığıyla yazıyorsak bu sınıfa uygulanır.

 

OperationContract:

DLL: System.ServiceModel.dll

Tam kalifiye adı: System.ServiceModel.OperationContract

Kullanıcılar tarafından kullanılacak metotları tanımlamak için kullanılır. Ara-yüz veya sınıf metotlarına uygulanır.

Örneğimizde servisimiz bir ara-yüz üzerinden yazıldığından IFaturaServis ara-yüzüne aşağıdaki eklemeleri yapıyoruz..

[System.ServiceModel.ServiceContract]

public interface IFaturaServis

{

[System.ServiceModel.OperationContract]

Fatura FaturaKaydet(Fatura fatura);

[System.ServiceModel.OperationContract]

Fatura FaturaBul(int faturaID);

}

 

DataContract:

DLL: System.Runtime.Serialization.dll

Tam kalifiye adı: System.Runtime.Serialization.DataContract

Servis ve kullanıcıları arasındaki iletişimde parametre olarak gidip gelecek yapıları tanımlamak için kullanılır.

 

DataMember:

DLL: System.Runtime.Serialization.dll

Tam kalifiye adı: System.Runtime.Serialization.DataMember

DataContract olarak nitelendirilmiş yapılar üzerinde serileştirilecek alanları tanımlamak için kullanılır.

 

Örneğimizde Fatura servis ve kullanıcıları arasında gidip gelecek olan tek karmaşık yapı… Bu yüzden Fatura sınıfını ve içerdiği alanları DataContract ve DataMember nitelikleri ile dekore etmeliyiz.

 

[System.Runtime.Serialization.DataContract]

public class Fatura

{

[System.Runtime.Serialization.DataMember]

publicint FaturaID { get; set; }

[System.Runtime.Serialization.DataMember]

publicstring MusteriReferans { get; set; }

[System.Runtime.Serialization.DataMember]

publicdouble ToplamMiktar { get; set; }

[System.Runtime.Serialization.DataMember]

publicstring FaturaBilgi { get; set; }

}

Şimdi projemize otomatik olarak eklenen Service1.svc dosyasında birtakım değişiklikler yaparak bu dosyayı FaturaServis yapısına ev sahipliği yapacak duruma getirelim.


Önce
Service1.svc.cs kod dosyasını silelim. Sonra Service1.svc dosyasını açalım ve aşağıdaki duruma getirelim.

 

<%@ServiceHostLanguage=”C#”Service=”F5.WCF.FaturaServisi.FaturaServis %>

Service.svc dosyası açık iken projemizi çalıştıracak olursak, bize servisimizi test etme imkanı verecek olan WCF Test Client programı çalışacaktır. Bu program aracılığıyla servisimize ait FaturaKaydet ve FaturaBul metotlarını kolaylıkla test edebiliriz.

 

Her ne kadar çalışır durumda bir servis yaratmış olsak da, WCF’in nasıl çalıştığını, servis-kullanıcı arasındaki etkileşim ve iletişimin nasıl olduğunu gerçek anlamda kavrayabilmek için Son Nokta (Endpoint) kavramını iyi anlamamız gerekir.

 

WCF son noktaları WCF servislerinin dış dünyayla olan iletişim kapılarıdır. Üç öğeden oluşurlar:

 

Adres (address)

Son noktanın URL adresini belirler. Eğer servis bir .svc dosyası ile servis ediliyorsa son nokta adresleri .svc dosyasının adresine göreceli olarak belirtilmelidir.

 

Kontrat (contract)

Servis ara-yüzünün (ya da sınıfının) tam kalifiye adıyla son noktanın arkasındaki WCF servisini belirler. Örneğimizde bu değer IFaturaServis ara-yüzünün tam kalifiye adı olan F5.WCF.FaturaServisi.IFaturaServis değeridir.


Bağlantı Bilgileri (binding)

Servis ve kullanıcıları arasında oluşturulacak bağlantı detayları kümesini belirler. Örnek: basicHttpBinding, wsHttpBinding, netTcpBinding, netNamedPipeBinding, netMsmqBinding.

Son noktalar WCF servisleri tarafından tanımlanırlar ve “servis kullanıcıları” bu tanımlar aracılığıyla kendi konfigürasyonlarını ve vekil yapıları oluştururlar. Peki örneğimizdeki WCF servisinin son noktası nerede tanımlanıyor? Şimdi bunu inceleyelim.

 

Son nokta tanımlamaları SVC dosyaları ile sunulan servisler için web.config dosyasında yapılırlar. Ancak örneğimizdeki web.config dosyasını açıp bakarsak göreceğiz ki herhangi bir son nokta tanımı mevcut değil. O zaman nasıl oluyor da servisimizi çalıştırdığımızda WCF Test Client programı başarılı bir şekilde servisimize bağlanabiliyor ve bize servisimizi test etme imkanı veriyor?

 

Microsoft WCF ekibi .NET 4 ile önceki versiyonlarında oldukça karmaşık olan ve sıklıkla problemlere sebep olan WCF Servis konfigürasyonunu basitleştirme amacıyla Varsayılan Son Noktalar (Default Endpoints) kavramını geliştirmiştir. Bu kavram sayesinde web.config dosyasında <system.serviceModel> içinde <services>services> ve <bindings>bindings> blokları olmasa da servisimiz için ASP.NET tarafından basicHttpBinding ayarlı bir son nokta otomatik olarak varsayılacak ve servisimiz çalışacaktır. Ne zaman basicHttpBinding değil de başka bağlantı bilgileri (binding) kullanmak istersek <services>services> ve <bindings>bindings>bloklarını bizim yazmamız gerekecektir.

 

Şimdi web.config dosyamızdaki <system.serviceModel> bloğunu aşağıdaki hale getirerek, servisimizi basicHttpBinding ile değil wsHttpBinding ile servis edelim.

 

<system.serviceModel>

<services>

<servicename=Fatura>

<endpointaddress=“”binding=wsHttpBindingbindingConfiguration=FaturaServiscontract=IFaturaServis>endpoint>

service>

services>

<bindings>

<wsHttpBinding>

<bindingname=FaturaServis>

<securitymode=None>security>

binding>

wsHttpBinding>

bindings>

<behaviors>

<serviceBehaviors>

<behavior>

<serviceMetadatahttpGetEnabled=true/>

<serviceDebugincludeExceptionDetailInFaults=false/>

behavior>

serviceBehaviors>

behaviors>

<serviceHostingEnvironmentmultipleSiteBindingsEnabled=true/>

system.serviceModel>

 

<system.serviceModel> bloğunun içine baktığımızda ilk alt blok olarak <services> alt bloğunu görüyoruz. Burada projemizde servis edilmesini istediğimiz servis ya da servisleri listeliyoruz. <servicename=F5.WCF.FaturaServisi.FaturaServis> bloğunda ise dikkat edilmesi gereken bir husus name alanında var olan değerin ilgili .svc dosyasındaki Service alanıyla aynı değerde olmasıdır.

 

Şimdi örneğimizdeki endpoint bloğunu incelemeye devam edelim.

 

<endpointaddress=“”

contract=F5.WCF.FaturaServisi.IFaturaServis

binding=wsHttpBinding>

endpoint>

 

Yukarıdaki konfigürasyonda address değeri boş. SVC dosyaları ile servis edilen servisler için tanımlanan address değeri SVC dosyasına göreceli bir değer olmalıdır ve bu değerin boş olması tanımlanan endpoint’e SVC dosyasının fiziksel adresi üzerinden ulaşılacağı anlamına geliyor. (Bir WCF servisi SVC dosyası olmadan da, System.ServiceModel.ServiceHost aracılığıyla da servis edilebilir. Bu konuya ileriki sayılarımızda inceleyeceğiz.)


Konfigürasyonumuzda binding değerini de wsHttpBinding olarak belirttik. Bu da .NET Framework tarafından wsHttpBinding adıyla bilinen bağlantı bilgileri kümesine işaret ediyor. Opsiyonel olanbindingConfiguration ifadesi ile de bu ayarlarda yapmak istediğimiz değişiklikleri tanımladığımız <bindings> bloğunda yer alan <bindingname=FaturaServis> bloğuna işaret ediyoruz. Bu blokta

<securitymode=None>security> ifadesi ile wsHttpBinding ayarlarında küçük bir değişiklik yapıyoruz.

Son olarak, <behaviors> (yani servis davranışları) konfigürasyonuna bakacak olursak burada da isimsiz bir davranış tanımı görüyoruz (<behaviorname=“”>). İsimsiz olması tanımlanan servislere otomatik olarak uygulanacak olması anlamına geliyor. Bu tanımın içerisinde de <serviceMetadatahttpGetEnabled=true /> ifadesi var. Bu da servisimiz tarafından tanımlanan meta-verilerin internet tarayıcıları aracığıyla görüntülenmesine ve daha önemlisi kullanıcı sistemlerin vekil yapıları yaratabilmesine olanak sağlayacaktır.

 

Eğer konfigürasyonda <serviceMetadatahttpGetEnabled=true /> ifadesi mevcut değilse servis kullanıcıları spesifik olarak Son Nokta adresini, bağlantı bilgilerini (binding) ve, ServiceContract, OperationContract, DataContract, DataMember olarak tanımlanan yapıların detaylarını bilmek zorunda olacaklardır.

 

Basit bir örnek de olsa faturalama işlevi sağlayan yapılarımızı bir WCF servisi aracılığıyla dış dünyanın kullanımına açtık. Servis kullanıcıları artık servisin URL adresini bilmek kaydıyla servis tarafından sunulan işlevden faydalanabilirler.

 

Gelecek sayımızda WCF konusunu kullanıcı kod perspektifinden inceleyeceğiz. Bu makale boyunca geliştirdiğimiz örnek WCF servisini test etmek isteyen okulcularımız http://lab.f5dergi.com/FaturaServisi/Service1.svc adresinden servise ulaşabilir ve WCF Test Client (C:Program Files (x86)Microsoft Visual Studio 10.0Common7IDEWcfTestClient.exe) ya da örnek projelerinizden Add Service Reference şeklinde referans eklemek kaydıyla test edebilirisiniz.

 

ÖNEMLİ NOT: Şirket içi ağlardan bağlanarak test etmek için aşağıdaki bloğu web.config (ya da app.config) dosyasına eklemek zorunda olabilirsiniz.


<system.net>

<defaultProxyuseDefaultCredentials=true>

<proxyusesystemdefault=True />

defaultProxy>

system.net>


Kaynak kodlar:

http://f5dergi.com/indir/Kod/F5.WCF.FaturaServisi.zip

(üyelik gerektirmektedir)

 

Alıntıdır

Leave a reply:

Your email address will not be published.