İlkay İlknur

just a developer...

Add Service Reference Ekranında Alınan Proxy Authentication Required Hatasının Çözümü

Merhaba Arkadaşlar,

Kimi zamanlar şirket içerisinde geliştirdiğimiz uygulamalarda 3rd party web servislerini kullanmak zorunda kalabiliyoruz. Bu servisleri Visual Studio içerisinde Add Service Reference ekranını kullanarak eklemeye çalıştığımızda ise "Proxy Authentication Required" hatası ile karşı karşıya kalabilmekteyiz. Bunun nedeni ise Visual Studio içerisinde bulunan Add Service Reference ekranının default olarak bizim ayarlamış olduğumuz proxy ayarlarını kullanmaması.

Bu sorunun çözümü için ise yapmamız gereken tek şey aşağıdaki konfigürasyonu servis referansını eklemeden önce config dosyasına eklemek.

<system.net> <defaultProxy useDefaultCredentials="true" /> </system.net>

Kolay Gelsin,



MetadataResolver Tipini Kullanarak WCF Metadatasını Kullanmak

Merhaba Arkadaşlar,

Bu yazımızda MetadaResolver isimli tipi kullanarak WCF servisleri ile ilgili dışarıya sunulan metadata bilgilerinin nasıl kullanılacağını işliyor olacağız . Bildiğimiz üzere WCF tarafındaki servislerimiz birtakım farklı bindingler üzerinden ve farklı endpoint adresleri kullanılarak dışarıya açılabilmekte. Bu adresleri ve binding konfigürasyonlarını uygulamaların konfigürasyon dosyalarından yapabileceğimiz gibi servisleri host ettiğimiz ortamlarda programatik(self-hosting) olarakta yapabilmekteyiz.

Şimdi isterseniz basit bir örnek üzerinden ilerleyelim ve somut örnekler üzerinden konuşalım.

[ServiceContract]
public interface IMathService
{
    [OperationContract]
    double Add(double x, double y);

    [OperationContract]
    double Substract(double x, double y);
}

 İlk olarak yukarıdaki ServiceContractına sahip basit bir servis tanımlayalım.

public class MathService:IMathService
{
    #region IMathService Members

    public double Add(double x, double y)
    {
        return x + y;
    }

    public double Substract(double x, double y)
    {
        return x - y;
    }

    #endregion
}

 Son olarak ise servisimiz ile ilgili binding ayarlarını ve endpoint ayarlarını gerçekleştirelim.

Şimdi yukarıda gerçekleştirdiklerimizi kısaca özetlersek;

  • Öncelikle gerçekleştirimini yapacağımız WCF servisimizin sahip olacağı Service Contract'ı tanımladık.(IMathService)
  • Sonrasında ise tanımladığımız Service Contract'ını implemente eden servisimizin geliştirimini gerçekleştirdik. (MathService)
  • Son olarak ise WCF'in ABC(Address, Binding ,Contracts)'si olarak nitelendirdiğimiz bu özelliklerden Contract özelliğini yukarıdaki adımlarda tanımlamıştık. Geriye kalan Address ve Binding tanımlamalarını ise config dosyasında gerçekleştirdik.

Config dosyasına baktığımızda MathService isimli servisimizin 3 faklı adres üzerinden ve farklı binding özellikleri ile dışarıya sunulduğunu görmekteyiz. Bunlar,

Yukarıda son olarak bahsettiğimiz endpoint adresine baktığımızda bu adresin MexHttpBinding kullandığını görmekteyiz. Bu şekilde tanımlanan endpointlere baktığımız zaman bu endpointler üzerinden servis'in metadatasının dışarıya sunulduğunu görmekteyiz. config dosyasına baktığımızda da zaten kullanacağı contractın diğer endpointlerin kullanacağı contract'tan farklı olduğunu anlayabiliriz. Servis metadatasını dışarıya sunan endpointler özel olarak IMetadataExchange interface'ini kullanırlar.

Şimdi gelelim bu servis metadatasını nasıl kullanabileceğimize. Zaten yazımızın esas konusunu da bu oluşturmakta. :)

Bir servisin metadatası ne olabilir diye düşündüğümüzde aslında aklımıza yine WCF servisinin Address - Binding - Contract (ABC) 'si gelmekte. Servisin sahip olduğu ABC bilgileri doğrudan servisin metadatası olarak elde edilebilmekte. Şimdi gelelim bu metadayı nasıl kullanabileceğimize.

System.ServiceModel.Description namespace'i içerisinde MetadataResolver isimli bir tip bulunmakta. Bu tip içerisine baktığımızda statik olarak tanımlanmış BeginResolve, EndResolve ve Resolve metotlarını görmekteyiz. Bu metotlar parametre olarak servis metadatasının dışarıya sunulduğu adresi alarak buradaki metadata bilgisini elde edebilmekteler. Metodun dönüş tipine baktığımızda ise EndpointCollection tipini görmekteyiz.

Şimdi basit bir Console Application yaratalım ve bize gelen servis metadatasını kullanarak runtime sırasında farklı bindingleri kullanan servislere bağlanalım ve işlemler gerçekleştirelim.

Not : System.ServiceModel.Description namespace'i System.ServiceModel assemblysi içerisinde bulunmaktadır.

static void Main(string[] args)
{
ServiceEndpointCollection collection = MetadataResolver.Resolve(typeof(MathService),�
 new System.ServiceModel.EndpointAddress("http://localhost:8080/MathService/mex"));

   foreach (ServiceEndpoint endpoint in collection)
   {
       Console.WriteLine("Servis bağlantısı {0} adresi üzerinden 
{1} bindingi kullanılarak oluşturulmakta",
        endpoint.Address.ToString(),endpoint.Binding.Name);
       MathServiceClient client = new MathServiceClient(endpoint.Binding,
                                  endpoint.Address);
       Console.WriteLine(client.Add(3, 2));
   }
}
Yukarıdaki kodlara baktığımızda ilk olarak MathService isimli WCF servisinin metadatasının dışarıya sunulduğu mex uzantılı adresi MetadataResolver tipinin statik Resolve metoduna vererek buradaki endpoint bilgilerinin alınmasını sağlıyoruz. Daha sonrasında ise buradaki her endpointe göre uygun WCF Servis clientlarını oluşturarak işlemlerimizi gerçekleştiriyoruz. Uygulamayı çalıştırdığımızda ise aldığımız sonuçların ekran görüntüsünü ise aşağıda görebilirsiniz. Sonuç olarak baktığımızda WCF Servislerimizle ilgili metadata bilgilerinin dışarıya sunulduğu endpointler özellikle dinamik olarak birtakım bağlantıların kurulabileceği yada ilgili wcf servislerine nasıl bağlanılacağı ile ilgili konfigürasyonların yapılabileceği durumlarda bizlere oldukça yardımcı olmakta. Sizler de bu gibi durumlarda hiç tereddüt etmeden bahsettiğimiz MetadataResolver tipini kullanabilirsiniz. Görüşmek Üzere,


Farklı Endpointler Üzerinden Sunulan Web Servislerin Referanslarının ve Ortak Kullanılan Tiplerinin İstemci Tarafında Tek Bir Noktadan Yönetimi

Merhaba Arkadaşlar,

Günümüzde yazılım geliştirme süreçlerine baktığımızda artık sıklıkla web servisler ile karşılaşmaktayız. Kimi zaman Silverlight gibi istemci taraflı uygulamalar geliştirirken data erişimi veya sunucu taraflı işlemler gibi nedenlerle web servislerini kullanırken kimi zamanda bir takım güvenlik veya uygulamadaki bağımlılıkları birbirinden ayırmak için iş kurallarını web servisler üzerinden dışarıya sunmaktayız.

Yukarıda bahsettiğimiz nedenlerden veya daha pek çok nedenden dolayı web servislerini uygulamalarımız içerisinde sıkça kullanmaktayız. Web servislerini kimi zaman kendimiz yazarken kimi zamanda hazır olarak yazılmış olan web servislerini kullanmaktayız.

Servis implementasyonlarında dikkat etmemiz gereken noktalardan biri de servislerden dışarıya sunulan operasyonların sayısı. Juval Löwy, WCF servislerinin tasarlanması konusunda bir servisten 20’den fazla metot sunulmaması gerektiğini tavsiye eder ve 12 operasyonun en uygun rakam olacağını belirtir. Bu rakam geliştirdiğiniz servisin durumuna göre artabilir de azalabilir de ancak önemli olan birbirleriyle ilişkili olan operasyonların aynı serviste durmasıdır.

Yukarıda okumuş olduklarımızı bir web servis geliştirirken göz önünde bulundurmamızda fayda var ;) Özellikle de yazacağımız web servislerini başka uygulamalar kullanacaksa !!! Şimdi gelelim yazımızın esas konusuna :) Web servislerini geliştirirken bahsettiğim şekilde servislermizi mantıksal olarak bölersek hem yönetimsel hem de ölçeklenebilirlik olarak daha iyi bir yapı sağlayabiliriz. Peki ya bu web servislerini kullanacak olan client(istemci) uygulamalar için durum nasıl ?

Uygulamada İçerisinde Artan Proxy Sayısı ve Aynı Tiplerin Defalarca Oluşması

Şimdi client(istemci) uygulamaların web servislerin sayısının artmasıyla beraber ne gibi sıkıntılarla karşılaşabileceğini incelemeye çalışalım. Örnek senaryonuz ise şu şekilde: İstemci uygulamamız fatura ve vergi ödemeleri için dışarıdaki bir firma tarafından sunulacak olan web servislerini kullanacak. Fatura ve vergi işlemleri için ayrı web servislerini kullanan firma fatura ve vergi ödeme fonksiyonu için aynı tipteki parametreleri beklemekte. BillPaymentService ve TaxPaymentService adını verdiğimiz servisleri ServiceContract’ları şu şekilde olabilir.

[ServiceContract] public interface IBillPayment 
{    
[OperationContract]    
bool BillPayment(Payment payment); 
}

[ServiceContract
public interface ITaxService 
{    
[OperationContract]
bool PayTaxes(Payment payment); 
} 

Servislerimizin implementasyonlarını ise basit bir şekilde aşağıdaki gibi yapabiliriz.

public class BillPaymentService : IBillPayment 
{     
#region IBillPayment Members
 public bool BillPayment(Payment payment)
 {
         return true;
 }     
#endregion 
}
public class TaxPaymentService :ITaxService  
{     
#region ITaxService Members 
public bool PayTaxes(Payment payment)     
 {       
 return true;
 }    
#endregion 
}
Servis tarafında kullanılan Payment sınıfını ise şu şekilde geliştirebiliriz.
[DataContract
public class Payment 
{
 [DataMember]    
public int BillNumber { get; set; }
 [DataMember] 
 public string NameSurname { get; set; } 
} 

Servis tarafında yapacağımız geliştirmeler şimdilik bu kadar. Şimdi gelelim bu servisi kullanacak olan istemci uygulamaya.

Hemen test etmek için bir console uygulaması(Söz konusu hızlı bir şekilde test yapmaksa console uygulaması candır :) ) yaratalım ve servislerimizin referanslarını uygulamamıza ekleyelim. Ben BillPaymentService isimli web servisinin referans adını BillService, TaxPaymentService isimli web servisin referans adını da TaxService olarak vereceğim. (Bundan sonraki kodlarda göreceğiniz namespace’lerde bu isimler bulunacaktır.)

Servis referanslarını ekledikten sonra şimdi sıra geldi yarattığımız servisleri kullanmaya. Hemen basit bir şekilde yazdığımız servisleri çağıracak kodu yazalım ve uygulamamızı test edelim.

class Program {    
static void Main(string[] args)
{
    BillService.Payment billPayment = new BillService.Payment()       
 {           
 BillNumber = 12300,           
 NameSurname = "İlkay İlknur"       
 };
BillService.BillPaymentClient billServiceClient = new BillService.BillPaymentClient();       
 bool billResult = billServiceClient.BillPayment(billPayment);     
 TaxService.Payment taxPayment = new TaxService.Payment()       
 {
           BillNumber = 78313,           
 NameSurname = "İlkay İlknur"       
 };
TaxService.TaxServiceClient taxServiceClient = new TaxService.TaxServiceClient();       
 bool taxResult = taxServiceClient.PayTaxes(taxPayment);       
 Console.WriteLine("Bill Payment Result {0}, Tax Payment Result {1}", 
 billResult, taxResult);     
} 

Uygulamamız oldukça basit olduğundan uygulamayı çalıştırınca hemen True yanıtını alıyor olacağız. Ancak bizim burada düşüneceğimiz nokta ise istemci tarafında yazdığımız kodlar ! Yukarıdaki kodu incelediğimizde her bir servisle ilgili tiplerin ayrı namespaceler içerisinde oluşturulduğunu görmekteyiz. Bu nedenle de yukarıdaki senaryoda Payment tipi her bir namespace içerisinde ayrı ayrı oluşturulduğundan dolayı her servis çağrısında kendi namespace’i içerisindeki tipi kullanmak zorundayız. Yukarıdaki kullanımın dezavantajlarından biri de yazdığımız kodun okunabilirliğinin çok az olması. Bir de servis tarafını düşündüğümüzde aslında Payment tipi ortak bir tip. Bu nedenle servis çağrımız ilgili servise gittiğinde bu ortak tipe çevrilmekte.

Bu noktada aklımıza gelen soru ise şu : Acaba biz de web servis tarafında yaptığımız gibi istemci tarafından tek bir Payment tipi yaratıp bu tipi bütün servislere parametre olarak gönderebilir miyiz ? Böylece hem kodumuz daha temiz ve anlaşılır olacaktır hem de developer olarak geliştirim sırasında farklı namespace’ler içerisindeki tiplerin yönetimiyle uğraşmıyor olacağız. Bu noktada aklımıza gelen bir yöntem olmakla beraber bir de bu yazının konusu olan başka bir yöntem de bulunmakta. Şimdi bu yöntemleri sırayla inceleyelim. İlk olarak aklımıza gelen ilk yöntem :)

Reuse Types in All Referenced Assemblies

Eğer daha önce bu seçeneği kullandıysanız aklınıza ilk gelen yöntem bu olacaktır. Kısaca bu yöntemden bahsetmemiz gerekirse bir web servisinden dönen tiplerimizi bir class library(dll) içerisinde tutuyoruz.(Anlamsal olarak farklı class libraryler içerisinde de tutabilirsiniz.Tek class library olma zorunluluğu bulunmamakta.) Daha sonra bu class libraryleri hem web servisimize hem de istemci uygulamamıza referans olarak ekliyoruz. Sonrasında ise “Add Service Reference” ekranında ilgili class libraryleri seçip bu libraryler içerisindeki tiplerin web servis çağrılarında kullanılmasını istediğimizi belirtiyoruz. Böylece Visual Studio da buna göre ilgili proxy kodlarını yaratıyor.

Yukarıdaki örneğimizde bu yöntemi uygulamamız gerekirse ilk olarak iki servisinde ortak olarak kullandığı Payment sınıfını Common ismini verdiğimiz bir class library içerisine alıyoruz.

Daha sonra ise kullandığımız 2 web servise ve istemci uygulamamıza Common ismini verdiğimiz class library referansını ekliyoruz. Sıra geldi eklediğimiz referansı güncellemeye. Hemen service referansına sağ tıkla Configure Service Reference  diyelim ve gelen ekrana bir göz atalım.

İşaretli kısma baktığımızda aslında herşey anlaşılmakta. :) Burada projeler arasında ortak olarak referans edilmiş olan libraryler içerisindeki tiplerin yeniden kullanımının sağlanması gerektiğini Visual Studio’ya bildiriyoruz. Böylece de Visual Studio ilgili bildirime göre Proxy sınıflarını oluşturuyor.

Bu kısım service referansı ekleme sırasında default olarak işaretli durumda gelmekte. Eğer siz sadece belirli libraryler içerisindeki referansların içerisindeki tiplerin yeniden kullanımını sağlamak istiyorsanız bir aşağıdaki checkbox’ı seçip istediğiniz libraryleri tek tek seçebilirsiniz.

Bu yöntemi kullandığımızda sağlayacağımız avantajlardan biri de eğer servisten dışarıya sunduğumuz tipin bir parametreli constructor’ı varsa bu constructor da doğrudan istemci tarafına gelmekte. Aslında yaptığımız doğrudan ilgili dll içerisindeki tipi yaratmak. ;)

Örneğin eklediğimiz BillService referansını güncelleyelim ve nasıl bir kullanım oluşuyor bir bakalım.

Yukarıdaki resimden de anlayacağınız gibi metodumuzun parametresi bu sefer Common namespace’i içerisindeki Payment tipini alacak şekilde değişti. Eğer TaxService için de aynısını uygularsanız aynı değişikliği görüyor olacaksınız. Böylece 2 servisimizi de Common namespace’i içerisindeki Payment tipini alacak şekilde kullanabiliriz.

Fakat bu yöntemi her zaman kullanamıyoruz. Özellikle kendi yazdığımız servis ve istemcilerde bu yöntemi kullanabilme imkanımız varken web üzerinde bulunan ve implementasyonunu bizim yapmadığımız servislerde bu yöntemi kullanmak mümkün olmuyor. Çünkü ortada yeniden kullanılabilirliği sağlayabileceğimiz bir class library bulunmamakta. Ayrıca yine bir çok farklı servis referansımız bulunmakta. Aslında hem tek bir referans içerisinden tüm servisleri yöntebilsek ve ortak tipleri de kullanabilsek ne güzel olur :) İşte şimdi inceleyeceğimiz yöntem işte tam da bunu sağlayacak :)

Reference.svcmap Dosyasını Manuel Editleme ve Namespace Mapping

Visual Studio içerisinden bir web servis referansı eklemeye çalıştığımızda karşımıza çıkan ekrandan seçimlerimizi yaptıktan sonra Visual Studio aslında arka planda svcutil.exe uygulamasını çalıştırmakta. Ancak Visual Studio sağladığı kullanıcı arayüzüyle biz developerlara hızlı bir şekilde bir web servis referansı ekleme sürecini geçirmeyi hedeflediği için aslında bizler çokta bu detaylarla ilgilenmiyoruz.

Ancak Visual Studio içerisinde kullandığımız Add Service Reference penceresi svcutil uygulamasının özelliklerini tam anlamında kullanmamızı sağlamamakta. Bunun temel nedeni de aslında Visual Studio’nun en önemli özelliği olan yazılım geliştirme sürecini kolaylaştırmak. Oldukça basit bir arayüzle hemen servis referansını yaratıp uygulamayı geliştirmeye devam etmek. Eğer svcutil’in tüm özelliklerini sunmak için bu arayüze eklentiler yapılsaydı çoğu developer için kafa karıştırıcı durumlar oluşabilirdi.

Svcutil uygulamasının işlevlerinden biri de web servisler tarafından sunulan tiplerin web servis tarafındaki namespace’lerine göre istemci taraftaki ilgili namespace’lerle eşleştirmesi. Nasıl mı ? hemen bakalım.

Not : Bu noktadan sonra yapacağımız işlemler Visual Studio’nun otomatik olarak yarattığı dosya üzerinden olacaktır. Bu dosyayı yanlış bir şekilde değiştirmeniz durumunda Visual Studio referansı güncellerken hata verebilir.

İstemci uygulamamıza bir web servis referansı ekledikten sonra arka planda Visual Studio bir takım dosyalar oluşturmakta. İstemci uygulamasını seçtikten sonra Visual Studio’dan Show All Files derseniz bu dosyaları rahatlıkla görebiliriz.

Gördüğünüz gibi aslında web servis referansımız tek bir dosyadan oluşmuyor. Wsdl,disco,xsd,svcinfo gibi uzantılara sahip pek çok dosya bulunmakta ve en altta da Reference.cs isimli proxy sınıfı bulunmakta. Burada bizim ilgileneceğimiz dosya ise Reference.cs’in hemen üzerindeki Reference.svcmap xml dosyası. Hemen bu dosyayı xml olarak açalım ve inceleyelim.

<ReferenceGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
ID="055d32c1-7b73-4547-91e3-815871cca568" 
xmlns="urn:schemas-microsoft-com:xml-wcfservicemap">   
<ClientOptions>     
<GenerateAsynchronousMethods>false</GenerateAsynchronousMethods>     
<EnableDataBinding>true</EnableDataBinding>     
<ExcludedTypes />     
<ImportXmlTypes>false</ImportXmlTypes>     
<GenerateInternalTypes>false</GenerateInternalTypes>     
<GenerateMessageContracts>false</GenerateMessageContracts>     
<NamespaceMappings />     
<CollectionMappings />     
<GenerateSerializableTypes>true</GenerateSerializableTypes>     
<Serializer>Auto</Serializer>     
<UseSerializerForFaults>true</UseSerializerForFaults>     
<ReferenceAllAssemblies>true</ReferenceAllAssemblies>     
<ReferencedAssemblies />     
<ReferencedDataContractTypes />     
<ServiceContractMappings />   
</ClientOptions>   
<MetadataSources>     
<MetadataSource Address="http://localhost:51919/BillPaymentService.svc" 
Protocol="http" SourceId="1" />   
</MetadataSources>   
<Metadata>     
<MetadataFile FileName="BillPaymentService.xsd" 
MetadataType="Schema" 
ID="362ccb94-7e45-4c9e-b180-d69a87e2f884" 
SourceId="1" 
SourceUrl=http://localhost:51919/BillPaymentService.svc?xsd=xsd2 />     
<MetadataFile FileName="BillPaymentService1.xsd" 
 MetadataType="Schema" 
ID="053220d3-d932-47ea-a256-009545da0853" 
SourceId="1" 
SourceUrl=http://localhost:51919/BillPaymentService.svc?xsd=xsd1 />     
<MetadataFile FileName="BillPaymentService.wsdl" 
 MetadataType="Wsdl" 
 ID="df9ef587-0317-43ed-aa36-8d5e042f3ced"
  SourceId="1"
  SourceUrl="http://localhost:51919/BillPaymentService.svc?wsdl" />     
<MetadataFile FileName="BillPaymentService2.xsd" 
 MetadataType="Schema"
  ID="7f21e958-3083-44e9-adef-0f50c89c5926"
  SourceId="1"
  SourceUrl="http://localhost:51919/BillPaymentService.svc?xsd=xsd0" />     
<MetadataFile FileName="BillPaymentService.disco" 
 MetadataType="Disco"
  ID="1d841d8c-58c9-4aa1-a73d-1a00399c5546"
  SourceId="1"
  SourceUrl="http://localhost:51919/BillPaymentService.svc?disco" />   
</Metadata>   <Extensions>     
<ExtensionFile FileName="configuration91.svcinfo" 
Name="configuration91.svcinfo" />     
<ExtensionFile FileName="configuration.svcinfo" 
Name="configuration.svcinfo" />   
</Extensions> 
</ReferenceGroup>

Dosyanın yapısına baktığımızda özetle bu xml dosyada servis referansı ile ilgili seçeneklerin tutulduğunu görmekteyiz. Örneğin MetadataSources içerisinde proxy’nin hangi endpointler için kullanıldığını görürken Metadata içerisinde ise proxy içerisinde bulunan dosyalarla ilgili bir takım tanımlamalar görüyor olacağız.

Yazımızın başında 2 amacımız olduğundan bahsetmiştik. Bunlardan biri tek bir referans içerisinde bir den fazla web servisinin referansını yönetmek. Diğeri ise web servisler arasındaki ortak tipleri bir kere yaratıp tüm servislerde bu ortak tipleri kullanmak. İlk olarak birinci maddeden başlayarak ilerleyelim. Sanırım bunu yapmak için nasıl hareket etmemiz gerektiğin bir önceki paragraftan az çok tahmin etmişsinizdir :) Svcmap dosyası içerisideki MetadataSources içerisine yeni bir MetadataSource ekleyeceğiz.

<MetadataSources>     
<MetadataSource Address="http://localhost:51919/BillPaymentService.svc" 
Protocol="http" SourceId="1" />     
<MetadataSource Address="http://localhost:51548/TaxPaymentService.svc" 
Protocol="http" SourceId="2" /> 
</MetadataSources>
 

Burada dikkat etmemiz gereken nokta SourceId değeri, bu değer her bir MetadataSource için unique(tekil) bir değer olmalıdır. Gelelim amacımızın ikinci maddesine. Svcmap dosyasının xml’ini incelediğinizde ClientOptions içerisinde NamespaceMappings isimli tagi göreceksiniz. İşte bu tag tam da bizim istediğimizi gerçekleştirmemize yarayacak olan bölüm. Bu tag içerisinde yapacağımız tanımlamalarla web servis üzerindeki belirli namespace’lerdeki tipleri uygulamamız içerisindeki tanımladığımız namespace’ler içerisinde yaratacağız. Yani kısaca “web servis üzerindeki www.tempuri.org/x namespace’inde olan tipleri git benim uygulamamdaki X namespace’inde yarat”’ın bildirimini yapacağız.

Ancak öncelikle yazımızın en başında yarattığımız web servislerinde ilgili namespace tanımlamalarını yapalım.

[ServiceContract(
 Namespace="http://www.ilkayilknur.com/PaymentService.BillPayment")] 
public interface IBillPayment 
{     
[OperationContract]     
bool BillPayment(Payment payment); 
} 
[ServiceContract(
 Namespace = "http://www.ilkayilknur.com/PaymentService.TaxPayment")] 
public interface ITaxService 
{     
[OperationContract]     
bool PayTaxes(Payment payment); 
} 
[DataContract(Namespace="http://www.ilkayilknur.com/PaymentService.Common")] 
public class Payment {     
[DataMember]     
public int BillNumber { get; set; }     
[DataMember]     
public string NameSurname { get; set; } 

NamespaceMappings içerisine tanımlayacağımız NamespaceMapping içerisinde 2 attribute bulunmakta. Bunlar ClrNamespace ve TargetNamespace. TargetNamespace içerisinde web servis üzerindeki namespace’i verirken ClrNamespace içerisinde ise TargetNamespace içerisinde bulunan tiplerin istemci uygulaması içerisinde hangi namespace içerisinde yaratılması gerektiğini belirtiyor olacağız. Bu tanımlamaları da aşağıdaki gibi gerçekleştirebiliriz.

<NamespaceMappings>       
<NamespaceMapping 
TargetNamespace="http://www.ilkayilknur.com/PaymentService.BillPayment" 
ClrNamespace="TestApp.BillPayment" />       
<NamespaceMapping 
TargetNamespace="http://www.ilkayilknur.com/PaymentService.TaxPayment" 
ClrNamespace="TestApp.TaxPayment" />       
<NamespaceMapping 
TargetNamespace="http://www.ilkayilknur.com/PaymentService.Common" 
ClrNamespace="TestApp.Payment.Common" /> 
</NamespaceMappings>

Yapacağımız tüm işlemler bu kadar. Şimdi svcmap dosyasını kaydedelim ve servis referansını güncelleyelim.

Yukarıdaki değişiklikleri BillPaymentService  web servisi referansını eklediğimiz BillService referansı içerisinde gerkçekleştirdik. Bu nedenle BillService referansını güncellediğimizde yaptığımız tanımlamalar sayesinde TaxService referansı da BillService referansı içerisine eklenmiş oldu. Artık TaxService referansını istemci uygulamamızda tutmamıza da gerek kalmadı. :) Tek servis referansı üzerinden 2 serviside çağırabilir durumdayız. Hemen istemci uygulamamıza geri dönelim ve güncellemeleri yapalım.

static void Main(string[] args) {
Payment.Common.Payment payment = new Payment.Common.Payment()
{
    BillNumber = 12300,
    NameSurname = "İlkay İlknur"
};

BillPayment.BillPaymentClient billServiceClient = 
 new BillPayment.BillPaymentClient();
bool billResult = billServiceClient.BillPayment(payment);
 payment.BillNumber = 78313;
TaxPayment.TaxServiceClient taxServiceClient = 
 new TaxPayment.TaxServiceClient();
 bool taxResult = taxServiceClient.PayTaxes(payment);
 Console.WriteLine("Bill Payment Result {0}, Tax Payment Result {1}",
  billResult, taxResult); 

Evet gördüğünüz üzere artık servis proxy sınıflarını svcmap içerisinde yaptığımız namespace tanımlamaları içerisinde bulunan sınıflar içerisinden kullandık ve Payment nesnesini Common namespace’i içerisinde  bulunan sınıftan yaratarak Tax servisi içerisinde parametre olarak göndermeden önce sadece BillNumber değerini değiştirerek aslında her ne kadar tam anlamda yeniden kullanılabilirlik olmasada ortak tipi web servislere parametre olark geçirebileceğimizi gördük.

Bu yönteme baktığımızda tüm servislerin referansını tek bir proxy üzerine toplayarak aslında proxylerin yönetimlerini biraz zorlaştırmış oluyoruz. Çok basit olarak bir serviste yapılan değişiklikten dolayı referansı güncellemek isterseniz içerisinde bulunan tüm web servislerin referansları güncelleniyor. Ancak baktığımızda tiplerin ortak kullanımını da tam anlamıyla burada sağlamış oluyoruz. Üstelik herhangi bir şekilde class library referansı olmadan. Hem de bu yöntemi kullanacağımız third-party web servislerde de kullanabilmekteyiz.  Özellikle web servislerinizin sayısı oldukça fazlaysa ve hemen hemen her servis çağrısında göndermeniz gereken ortak bir parametre varsa bu yöntemi mutlaka kullanmanızı öneriyorum. İstemci tarafı kodlarınızı yazarken işinizi oldukça kolaylaştıracaktır.

Bir başka yazımızda görüşmek üzere :)

Hoşçakalın,



Asenkron WCF Servis Çağrımlarında C# 5.0 ile Gelen Async & Await Kullanımı

Merhaba Arkadaşlar,

C# 5.0 ile gelecek olan Asenkron Programlama yeniliklerinin önemli bir kısmını önceki yazılarımızda inceledik. Şu ana kadar farkettiyseniz asenkron programalama yaptığımız belki de en önemli noktalardan biri olan bir konuya hiç değinmedik. Bu konu da asenkron web servis çağrıları. Özellikle Silverlight ya da Windows Phone uygulamaları gibi istemci tarafında çalışan uygulamalar geliştiriyorsanız asenkron web servis çağrısı yapmamak neredeyse imkansız. Tabi aslında asenkron servis çağrıları sadece bu teknolojiler ile sınırlı değil. WPF veya ASP.NET tabanlı web uygulamalarımızda da asenkron web servis çağrıları gerçekleştirebilmekteyiz.

Asenkron olarak  yaptığımız web servis çağrılarının yanıtını almak için bildiğiniz gibi callback yöntemini kullanmaktayız. Yani asenkron olarak web servis çağrısını yaptıktan sonra kontrol bize geri dönmekte ve web servisten yanıt döndükten sonra callback olarak bildirdiğimiz kod parçası işletilmekte. Bu yöntemin dezavatajlarını, kodumuzu nasıl düzensiz hale getirdiğini ve uygulamamızın ölçeklenebilirliğini nasıl düşürdüğünü önceki yazılarımızda incelemiştik. İşte tam bu noktada web servis çağrılarında C# 5.0 ile gelen async & await ifadelerinin kullanılamayacağını sorduğunuzu duyar gibiyim :)

Bu noktada aslında web servis proxy’lerinin awaitable pattern’a göre oluşturulması bizim async & await kullanmamız için yeterli. Örnek vermek gerekirse bir web servis metodu int bir değer döndürüyorsa dönüş tipinin Task<int> tipi olması o metodun asenkron olarak işletilmesi ve yanıtının async ve await kullanılarak alınması için yeterli olmakta.

Şu anda Visual Studio içerisinde baktığımızda Add New Service Reference penceresi içerisinde yukarıda bahsettiğimiz biçimde proxy kodu üretecek bir mekanizma bulunmamakta. Bu tabi ki hiç olmayacak anlamına gelmiyor. Gelecek Visual Studio sürümünde bu özellikle geliyor olacak. Peki biz bu özelliği beklemeden asenkron web servis çağrılarımızı async ve await ile nasıl yönetebiliriz ?

Öncelikle basit bir WCF servis metodu yazalım ve ardından da bu metodumuzu WPF istemcisi tarafından çağıralım.

Yazacağımız web servis metodu oldukça basit olacak ve parametre olarak aldığı 2 sayıyı toplayarak geri döndürecek. Ancak bu işlemi yapmadan önce biz işlemi 3 saniye bekleteceğiz o şekilde metottan değeri geri döndüreceğiz. Böylece web servis metodumuz bizim için asenkron çağrı yapmak için anlamlı hale gelecek. ;) Aksi takdirde zaten 2 sayısı toplayan bir web servis metodu için asenkron çağrı yapamaya çok ta gerek yok sanki :)

İlk olarak WCF servisimizin Service Contract’ının geliştirelim.

[ServiceContract]
public interface IMathService
{
 [OperationContract]
 double Add(double x, double y);     
}

Sıra geldi contract'ını belirlediğimiz WCF servisimizin implementasyonuna.

public class MathService : IMathService 
{    #region IMathService Members
   
 public double Add(double x, double y)
 {
 Thread.Sleep(3000);
 return x + y;
 }    #endregion
}

Gördüğünüz gibi metot sonucunu dönmeden önce Thread.Sleep metodunu kullanarak işlemin 3 saniye beklemesini sağladıktan sonra 2 sayıyı toplayıyıp servis metodundan geriye döndük.

WCF tarafında son olarak ise servisimizin konfigürasyonunu yapıyoruz ve servisi http://localhost/MathService endpoint’i üzerinde dışarıya açıyoruz.

<system.serviceModel>     
<services>       
<service name="MathServiceLib.MathService">         
<endpoint address="" binding="wsHttpBinding" 
contract="MathServiceLib.IMathService">           
<identity>             
<dns value="localhost" />           
</identity>         
</endpoint>         
<endpoint address="mex" binding="mexHttpBinding" 
contract="IMetadataExchange" />         
<host>           
<baseAddresses>             
<add baseAddress="http://localhost/MathService" />           
</baseAddresses>         
</host>       
</service>    
 </services>     
<behaviors>       
<serviceBehaviors>         
<behavior>           
<serviceMetadata httpGetEnabled="True"/>           
<serviceDebug includeExceptionDetailInFaults="False" />         
</behavior>       
</serviceBehaviors>    
 </behaviors>   
</system.serviceModel>
Şimdi sıra geldi WPF istemci uygulamamıza. İstemci uygulamamızın arayüzü de şu şekilde olacak.
<Grid>    
<Label Content="1. Sayı" Height="28" HorizontalAlignment="Left"
 Margin="12,12,0,0" VerticalAlignment="Top" />    
<Label Content="2. Sayı" Height="28" HorizontalAlignment="Left"
 Margin="12,44,0,0" VerticalAlignment="Top" />    
<TextBox Height="23" HorizontalAlignment="Left" Margin="81,12,0,0"
 Name="txtSayi1" VerticalAlignment="Top" Width="120" />    
<TextBox Height="23" HorizontalAlignment="Right" Margin="0,46,302,0"
 Name="txtSayi2" VerticalAlignment="Top" Width="120" />    
<Button Content="Topla" Height="23" HorizontalAlignment="Left"
 Margin="12,78,0,0" Name="btnTopla" VerticalAlignment="Top"
  Click="btnTopla_Click" Width="75" />    
<Label  Height="28" HorizontalAlignment="Left" Margin="106,73,0,0"
 Name="lblSonuc" VerticalAlignment="Top" /> 
</Grid>
 

Arka plan kodlarına geçmeden son olarakta WCF servisimizin referansını WPF projemize ekliyoruz. Servis referansını eklerken de Add New Service Reference penceresindeki Advanced butonuna tıklayıp açılan pencere içerisindeki asenkron çağrım kodlarının da üretilmesini sağlayan checkbox’ı seçiyoruz.

Yukarıdaki gibi asenkron web servis çağrım kodlarını ürettiğimizde ilgili proxy tipi içerisinde AddAsync isimli bir metodun olduğunu göreceğiz. Bunun yanında bir de tip içerisinde AddCompleted isimli bir event bulunmakta. Şu anda elimizde var olan imkanlarla callback mekanizmasını kullanarak asenkron çağrımları gerçekleştirdiğimizi hatırlamamız gerekirse şu anda yapabileceğimiz implementasyon aşağıdaki gibi olacaktır.

private void btnTopla_Click(object sender, RoutedEventArgs e) {
 MathServiceClient client = new MathServiceClient();
 client.AddCompleted += (_sender, _e) =>
 {
 lblSonuc.Content = String.Format("Sonuc : {0}", _e.Result);
 };
 client.AddAsync(double.Parse(txtSayi1.Text), double.Parse(txtSayi2.Text));
}

Peki esas konumuza geri dönersek async&await kullanarak ve callback mekanizmasını kullanmadan asenkron çağrılarımızı nasıl  yönetebiliriz.

Bu noktada şu anda kullanabileceğimiz 2 yöntem bulunmakta.

Task.Factory.FromAsync

Yöntemlerimizden ilki Task tipi içerisinde bulunan FromAsync metodu. Bu metot Begin/End yapısı ile asenkronluğu sağlayan yani Asynchronous Programming Model Pattern’nına uygun olan işlemlerin Task tipi içerisine alınması ve Task tipi ile yönetilmesini sağlamakta. Böylece artık yapacağımız çağrımlarda ve diğer işlemlerde de bu metottan dönen Task tipini rahatça kullanabileceğiz.

İşte bu yöntemi kullanarak aslında proxy tipimiz içerisinde metotlara yeni bir metot ekleme imkanına sahibiz. Öyleyse hemen proxy tipimiz için bir extension metot gerçekleştirelim.

public static class Extensions
{
 public static Task<double> AddTaskAsync(
 this MathServiceClient client, double x, double y)
 {
 return Task.Factory.FromAsync<double>(client.BeginAdd(x, y, null, null), 
 client.EndAdd);         
 }
}

Şimdi web servis metodumuz async & await kullanabileceğimiz duruma geldi. Yani awaitable pattern'a uydu. Öyleyse async ve await kullanarak asenkron çağrımızı gerçekleştirelim.

private async void btnTopla_Click(object sender, RoutedEventArgs e)
{
 MathServiceClient client = new MathServiceClient();
 double result = await client.AddTaskAsync(double.Parse(txtSayi1.Text), 
 double.Parse(txtSayi2.Text));             
 lblSonuc.Content = String.Format("Sonuc : {0}", result);
}

Evet gördüğünüz gibi asenkron çağrımı gerçekleştirdik. Ancak uyguladığımız yönteme bakarsak bizim içim maliyetli bir yöntem olduğunu görmekteyiz. Bunun nedeni de her bir web servis metodu için yeni bir extension metot gerçekleştirmemiz. Bize aslında otomatik proxy kodu üretimi sırasında devreye girecek olan birşeyler lazım. Öyleyse gelin diğer yöntemimizi inceleyelim.

TaskWsdlImporterExtension

Visual Studio Async CTP kütüphanesini bilgisayarımıza kurduktan sonra Documents\Microsoft Visual Studio Async CTP\Samples klasörü içerisinde hem C# hem de VB ile yazılmış çeşitli asenkron programlama örnekleri gelmekte. Bu örneklerden biri de Stock Quotes isimli örnek. Bu örnek içerisinde bir istemci uygulaması tarafından WCF servis çağrılmakta ve bu servis üzerinden de hisse senedi kağıtlarının fiyatları alınmakta. Örnek içerisinde yapılan WCF servis çağrıları ise bizim örneklerimizde olduğu gibi asenkron çağrılar. Ancak burada client tarafında proxy codelarının üretilmesi sırasında bir extension kütüphanesi kullanılmış ve kullanılan extension kütüphanesi ise proje içerisine konulmuş. Bu kütüphane biz WCF servisini projemize servis referansı eklerken devreye girmekte ve proxy kodlarını async & await keywordleri ile beraber kullanılabilir biçimde yani awaitable pattern’a uygun olarak üretmekte. Şimdi bu örneği açıp derleyelim ve oluşan dll’i projemize referans olarak ekleyelim.

Şimdi bir önceki yöntemimizde projemize eklemiş olduğumuz WCF servis referansını kaldıralım ve sonrasında istemci uygulamamızın config dosyasına aşağıdaki config ayarları ekleyelim. Böylece projemize eklemiş olduğumuz extension, servis proxy kodunun üretilmesi sırasında devreye giriyor olacak.

<system.serviceModel>     
<client>       
<metadata>         
<wsdlImporters>           
<extension type="TaskWsdlImportExtension.TaskAsyncWsdlImportExtension, 
 TaskWsdlImportExtension" />         
</wsdlImporters>       
</metadata>     
</client>   
</system.serviceModel>
Şimdi WCF servisimizin referansını yine yukarıda yaptığımız gibi asenkron çağrım metotlarının da üretilmesi seçeneğini seçerek yeniden ekleyelim. Referansı ekledikten sonra oluşturulan kodlara baktığımızda AddTaskAsync isimli bir servis metodu göremiyoruz. Ancak bakın  AddAsync isimli metodun dönüş tipi ne oldu :)

Task<double> tipi demek bizim için awaitable demek :) Yani async & await kullanımı demek. O zaman kodumuzu yeni metot ismine göre güncelleyelim ve uygulamamızı çalıştıralım.

private async void btnTopla_Click(object sender, RoutedEventArgs e)
{
 MathServiceClient client = new MathServiceClient();
 double result = await client.AddAsync(double.Parse(txtSayi1.Text), 
 double.Parse(txtSayi2.Text));             
 lblSonuc.Content = String.Format("Sonuc : {0}", result);
}

Uygulamayı çalıştırdığımızda gördüğünüz gibi uygulamamız istediğimiz gibi çalıştı. Toplama isteğini servise gönderdik ve gönderdikten sonra kontrol uygulamaya geri döndü ve sonrasında ilgili kod işletilerek yanıt ekrana yazdırıldı. Peki bu extension arka planda nasıl bir kod üretti :) Hemen metot üzerinden F12’ye basarak arka planda üretilen koda sıçrayalım :)

public System.Threading.Tasks.Task<double> AddAsync(double x, double y) 
{     return System.Threading.Tasks.Task<double>.Factory.FromAsync(
 new System.Func<double, double, System.AsyncCallback, 
 object, 
 System.IAsyncResult>(((IMathService)(this)).BeginAdd), 
 new System.Func<System.IAsyncResult, double>(((IMathService)(this)).EndAdd),
 x, y, null); }
Siz ilk yönteme göre bir fark görebildiniz mi ? Ben göremedim :) Sadece FromAsync metodunun bir başka overload hali kullanılmış :)

Asenkron web servis çağrımları bana göre biz developerlar için en büyük sıkıntıyı oluşturan durumların başında gelmekte. Sürekli olarak callback atamaları developerlar için yönetmesi ve ölçeklendirilmesi oldukça sıkıntılı durumlar doğurmakta. Bu nedenle C# 5.0 ile beraber gelecek olan Async&Await ile artık bu sorunları da geride bırakmış olacağız.

Makalemizi sonlandırmadan Web Service Referansı ekleme sırasında bu özelliğin C#’ın 5.0 ve dolayısıyla da Visual Studio’nun bir sonraki sürümüyle beraber built-in olarak geleceğini sizlere tekrardan hatırlatmak isterim. Bu makalede işlemiş olduklarımız sadece şu anda bizlere kolaylık sağlayabilecek 1-2 trick nokta. ;)

Yazımız boyunca yaptığımız örneği aşağıdaki linkten indirebilirsiniz.

AsyncWCFCalls

Bir sonraki makalemizde görüşmek üzere,

Hoşçakalın



WCF Serileştirme Problemi ve WCF Tracing

Merhaba Arkadaşlar,

Bu yazımda sizlere WCF Servisleri ile çalışırken karşılaştığım bir sorundan ve sorunun çözümünden bahsetmek istiyorum. Öncelikli olarak senaryodan bahsetmek gerekirse; Geliştirdiğim uygulama gereğince WCF servisinden döndürülen sonuçları tek bir wrapper sınıf içerisinde döndürmeyi ve web servisinin her çağrımında aynı zamanda çağrımla ilgili bir takım bilgileri de bu wrapper tip içerisine koyarak, web servis metotlarından sadece bu wrapper sınıfı döndürmeyi hedefledim. Böylece web servisi sonuç dönüşlerinde ortak bir format yakalaycaktım ve bazı işlemleri daha standart bir şekilde yapacaktım. ServiceResult ismini verdiğim wrapper tipimin gerçekleştirimi ise şu şekilde idi.

public class ServiceResult
{
public bool Completed { getset; }
public object Result { getset; }
public string Message { getset; }
}

Daha sonra web servisi içerisindeki tüm metotlar içerisinde tip içerisindeki alanları uygun bir şekilde doldurduktan sonra aslında web servisinden döndürmem gereken nesneyide ServiceResult tipi içerisinde bulunan Result propertysi içerisine koyarak web servisinden ServiceResult nesnesini döndürmekteyim.

Örnek bir web servisi gerçekleştirimini ise şöyle verebilirim.

    [ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyService
{
[OperationContract]
public ServiceResult Call(int i)
{
ServiceResult result = new ServiceResult();
if (i == 0)
{
result.Completed = true;
result.Result = new Book()
{
Author="Martin Fowler",
Header = "Domain Spesific Languages"
};
result.Message = "i="+i.ToString()+" değerine göre kitap nesnesi üretildi";
}
else
{
result.Completed = true;
result.Result = new Album()
{
Singer = "Lady Gaga",
SongNumber = 13
};
result.Message = "i="+i.ToString()+" değerine göre albüm nesnesi üretildi";
}
return result;
}

Sizin de farkedebileceğiniz gibi örneğin metot içerisinde verilen parametreye göre ServiceResult içerisindeki Result propertysine parametrenin 0 olmasıyla yeni bir Book nesnesi konulmuş, parametrenin 1 olmasıyla da Album nesnesi konulmuştur.

Buraya kadar olan kısma baktığımızda herşey oldukça normal gözükmekte. O zaman uygulamaya devam ederek bir Silverlight uygulamasına bu web servisini bağlayalım ve ilgili metodu çağıralım.

Silverlight tarafının gerçekleştirimi ise şu şekilde olabilir.

MyServiceNamespace.MyServiceClient proxy = new MyServiceNamespace.MyServiceClient();
private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
proxy.CallCompleted += new EventHandler<CallCompletedEventArgs>(proxy_CallCompleted);
proxy.CallAsync(0);
proxy.CallAsync(1);
}

void proxy_CallCompleted(object sender, CallCompletedEventArgs e)
{
if(e.Result.Completed)
{
dynamic obj = e.Result.Result;
MessageBox.Show(e.Result.Message);
}
}

Herşey güzel ve sorunsuz giderken bir de uygulamayı çalıştıralım ve sonuca bakalım. Smile

Peki bu hata da nereden çıktı. Herşey gayet güzel gidiyordu. Uygulamaları derledik hiçbir sorun da yoktu. Runtime sırasında ne oldu da hata verdi ? Bu şekilde düşündükten sonra "Not Found" sonucuna bakarak hiçbir sonuca ulaşamayız. Onun yerine WCF servisine trace etmemiz çok daha yerinde olacaktır.

WCF Servisleri Nasıl Trace Edilir ?

WCF Servislerini trace etmek için her zaman olduğu gibi yine System.Diagnostics namespace'i içerisindeki tiplerden yararlanmaktayız. Hemen işi uzatmadan MSDN içerisinde de bulunan konfigürasyon kısmını uygulamanızın tipine göre app.config veya web.config dosyaları içerisinde yerleştirerek tracinge  başlayabilirsiniz.

<system.diagnostics> <trace autoflush="true" /> <sources> <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true"> <listeners> <add name="sdt" type="System.Diagnostics.XmlWriterTraceListener" initializeData= "SdrConfigExample.e2e" /> </listeners> </source> </sources> </system.diagnostics>

Daha sonra servisin çalışması süresi içerisinde elde edilen bilgiler "SdrConfigExample.e2e" dosyası içerisinde oluşacaktır. XML kullanılarak oluşturulan bu dosyayı okumak için bir de yardımcı bir tool olsa hiç fena olmazdı dediğinizi duyar gibiyim. Smile Öyleyse WCF Service Trace Tool tam size göre. WCF Service Trace Tool'a "C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin" uzantısı altından "SvcTraceViewer.exe" isimli uygulamayı çalıştırarak erişebilirsiniz. WCF Service Tracing ile ilgili çok daha detaylı bilgiyi MSDN'den alabilirsiniz.

Konumuza Geri Dönelim :)

Uygulamamızı config dosyasında yaptığımız değişiklikten sonra bir daha çalıştırıyoruz ve oluşturulan dosyayı WCF Service Trace tool yardımıyla açtıktan sonra incelemeye başlıyoruz. Uygulamayı açtıktan sonra sol kolonda bulunan activity bölümüne baktığımızda hata ile karşılaştığımız yer kırmızı olarak bizim karşımıza çıkmakta.

 

Hatanın detaylarına baktığımızda karşımıza nerede hata olduğu çok açık bir biçimde çıkmakta.

 

There was an error while trying to serialize parameter :CallResult. The InnerException message was 'Type 'SilverlightApplication5.Web.Album' with data contract name 'Album:http://schemas.datacontract.org/2004/07/SilverlightApplication5.Web' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'.  Please see InnerException for more details.

Karşımıza çıkan hatanın açıklaması ise şu şekilde :)  Biz developer olarak işin kolayına kaçarak hemen bir object koyduk tipin içerisine ve aklımızca istediğimiz tipten nesneleri bu property içerisine koyarak servisten döndürebileceğimizi düşündük. Ama unuttuğumuz bir nokta var. Bu objectlerin nasıl serileştirileceğini belirtmedik.Smile Yani normalde bu tiplerden birini web servisinden döndürseydik tipi serileştirme konusunda bir problem olmayacaktı. Ancak WCF servisine hangi tipleri koyabileceğimizi bile bildirmeden servisten döndürmeye çalıştık ve hata ile karşılaştık. Serviste kibar bir şekilde bari bu object içerisine koyabileceğin muhtemel tipleri bana söyle ki bende ona göre hazırlıklı olayım mesajını verdi.

Peki bu olayın çözümü nasıl olacak ? Çözüm = KnownType

Yapmamız gereken biraz önce de söyledğim gibi object kısmına muhtemel olarak önceden gelebilecek olan tipleri bildirmek. Bunu da System.Runtime.Serialization isim uzayı içerisinde bulunan KnownType attribute kullanarak gerçekleştiriyor olacağız. ServiceResult tipine yapacağımız muhtemel tip bildirimi ise şu şekilde olacak.

[KnownType(typeof(Book))][KnownType(typeof(Album))]public class ServiceResult

 Tüm bu işlemleri gerçekleştirdikten sonra uygulamayı çalıştırırsak başarılı bir şekilde MessageBox'ları görebiliriz. Smile

 

Herkese Kolay Gelsin,

Görüşmek Üzere,



WCF Servislerinde Generic Parametre Kullanımı

Merhaba Arkadaşlar,

Günlük uygulama geliştirme sürecimiz içerisinde en çok kullandığımız yapılardan birisi de servisler olmakta. Servis kelimesini kullanınca da .NET Framework tarafında aklımıza hemen WCF gelmekte. Bunun yanında uygulamalarımızı geliştirirken en çok kullandığımız yapılardan biri de generic tipler. Genericler bildiğiniz üzere C# 2.0 ile beraber dilin içerisinde katılmakla beraber uygulamaların genişletilmesi ve daha esnek bir hale getirilmesi genericlerin dile katılması ile beraber kolaylaştı. Bu makalemizin konusu ise aslında generic tipler ile WCF servislerinin birleşiminden oluşmakta.

Örnek senaryomuzu ise şu an şirkette geçici süreliğine atandığım ASP.NET MVC projesi üzerinde yapılan bir uygulamadan yola çıkarak vermek istiyorum. Öncelikli olarak senaryoyu aktararak başlayalım. Geliştirilen web uygulamalarının herhangi bir şekilde veritabanına erişimi bulunmadığından dolayı web uygulamaları içerisindeki tüm business ve veritabanı işlemleri WCF servisleri tarafından gerçekleştirilmekte. Bu doğrultuda da yapılan işlemler farklı servisler tarafından dışarıya açılmakta. Ancak metotlara baktığımızda gerçekleştirilmiş olan tüm metotların tanımlanan bir Request sınıfını parametre olarak aldığını ve işlemini tamamladığında da Response ismiyle tanımlanan bir nesne örneğini geri döndürdüğünü görmekteyiz. Böylece eğer bir metoda parametre göndermek istiyorsak ilgili parametreyi Request sınıfı içerisinde bulunan bir object property içerisine koyup servis tarafına göndermekteyiz. Servis tarafından ise gerekli Convert işlemi gerçekleştirilerek gereken adımlar uygulanmakta.

Tam da geçtiğimiz günlerde sabah serviste gelirken bu senaryoyu gözlerimi kapatıp canlandırırken birden aklıma başka bir yöntem geldi.(Tabi "serviste başka işin yok muydu da bunları düşünüyorsun uyusaydın ya" da diyebilirsiniz)Smile Her ne kadar object ile ilgili parametreyi alma yöntemini daha önce kendi projelerimde kullanmış olsam da unboxing işlemlerinde bir performans kaybı yaşadığımı görmekteydim. Bunun yerine acaba bu Request sınıfına bir generic parametre eklesem ve benim Request sınıfı içerisine yerleştireceğim parametremde otomatik olarak benim parametreyi tanımlarken verdiğim tipte olsa ve ben bu gereksiz unboxing işleminden kurtulsam nasıl olur ? Kulağa kötü gelmiyor Smile  O zaman hemen deneyelim ve ilk olarak basit bir Request sınıfı yaratalım.

[DataContract]
public class Request<T>
{
[DataMember]
public T RequestObject { getset; }
}

Bir de örneğimiz içerisinde bize yardımcı olması amacıyla bir Person sınıfı yaratalım ve servis içerisine yazacağımız bir metodunda aldığı Person nesnesini loglayacağını düşünelim.

[DataContract]
public class Person
{
[DataMember]
public int Age { getset; }
[DataMember]
public string Name { getset; }
[DataMember]
public string Surname { getset; }
}

Son olarak Service Contract'ımızı yaratalım.

[ServiceContract]
public interface IGenericService
{
[OperationContract]
void LogPerson(Request<Person> person);
}

Şimdi ise Service Contract'ımızı implemente edecek olan servis metodunu yazalım.

public class GenericService : IGenericService
{    
#region IGenericService Members

public void LogPerson(Request<Person> person)
{
//Loglama İşlemleri
}            
#endregion
}

Evet servisimiz artık hazır durumda. Şimdi istemci uygulamamıza geçerek servis referansını ekleyelim ve sınıfımızı yaratarak ilgili metodumuzu çağıralım.

Uppps !! Gerçekleştirdiğimiz metodun aldığı parametreye bakarsanız biraz şaşkınlığa uğrayabilirsiniz. Belki de proxy tipi yaratırken bir hata ile karşılaştı da diyebilirsiniz. Undecided Gelin hep beraber durumu inceleyelim.

İstemci Tarafında Generic Parametre Alan Tiplerin Üretimi

Yukarıdaki örneğimizde aslında karşılaştığımız durum biraz özel bir durum. Eğer bir metodumuz içerisinde Generic bir tip alan bir parametre bulunuyorsa bunun için özel olarak bir DataContract üretilmekte ve bu metot içerisinde yaratılan özel tip kullanılmakta. Peki tip ismini üretirken kullanılan herhangi bir yöntem var mı derseniz, şu şekilde bir yaklaşımın kullanıldığını söyleyebiliriz.

TypeName+"Of"+ParameterType+Hash

TypeName : İlk olarak ilgili tipin ismi yazılmakta.

ParameterType : İçerisine Generic olarak aldığı tipin adı. Eğer birden fazla generic parametre alınırsa yine tüm tip isimleri yaratılan tip isminin içerisine konulmakta.

Hash : Başka metotlarda da aynı şekilde bir kullanım olduğu durumlarda tiplerin karışmaması amacıyla ve tiplerin unique olmasının sağlanması amacıyla konulan hash.

İşte yukarıda bahsettiğimiz durumlar nedeniyle istemci tarafında bu şekilde bir tip adı ile karşılaştık.

Bu noktada da aklımıza şu soru gelmekte : Yaratılan bu tip ismini kendimiz belirleyemezmiyiz yada müdahele edemezmiyiz ?

Bu soruya cevap olarak kullanabileceğimiz yöntem ise DataContract içerisinde bulunan Name parametresi ile tipin ismini belirleme yöntemi olacak.

Yine servis tarafına geçip Request tipini tanımladığımız kısma geri dönersek, DataContract tanımlaması yaparken kullandığımız Name parametresi ile tipimizin isminin belirlenmesine müdahele edebilmekteyiz. Burada istersek yukarıda bahsettiğim ParameterType ve Hash elemanlarını tip ismi içerisinde bulunup bulunmamasını da belirleyebilmekteyiz. Bunları yaparken de string formatlama yöntemini kullanıyor olacağız.

[DataContract(Name="Request_{0}_{#}")]
public class Request<T>
{
[DataMember]
public T RequestObject { getset; }
}

Burada Name içerisinde yaptığımız bildirimle aslında default olarak kullanılan yöntemi belirtmekteyiz. Yani {0} belirttiğimiz yere ParameterType gelmekte ve ondan sonra da {#} ifadesinin yerine de tekil bir Hash bilgisi üretilmekte.(Eğer tipimiz birden fazla generic parametre alırsa tüm generic parametrelere {0},{1}... şeklinde erişebiliriz.

[DataContract(Name="MyRequestClass")]
public class Request<T>
{
[DataMember]
public T RequestObject { getset; }
}

Yukarıdaki kullanımda ise sadece bizim belirttiğimiz isim ile tip üretilmekte. Şimdi isterseniz bu şekilde istemci tarafına geçerek servis referansını güncelleyelim ve metodumuzu çağıralım.

protected void Page_Load(object sender, EventArgs e)
{
GenericServiceClient client = new GenericServiceClient();
MyRequestClass myRequest = new MyRequestClass()
{
RequestObject = new GenericServiceReference.Person()
{
Name = "İlkay",
Surname = "İlknur",
Age = 20
}
};
client.LogPerson(myRequest);}

Böylece yukarıda gördüğümüz gibi adını kendimiz belirlediğimiz tipi metoda parametre olarak vererek işlemlerimizi gerçekleştirdik. Yazının en başına dönersek bana göre bu yöntem daha doğru gibi geldi. Özellikle sınıflarımız içerisinde bulunan elemanların sayısı artmasıyla ve daha kompleks tiplerin bulunmasıyla unboxing süresinin daha da artacağını düşünmekteyim. En kısa zamanda da bu konu ile ilgili performans araştırmalarını da yapıyor olacağım. Bunun yanında generic kullanmanın bana göre gelen tek dezavantajı ise proxy içerisindeki tip sayısının artması. Bunun haricinde eğer sizlerin de bu konuda herhangi bir tecrübeniz oldu ise yorum olarak yazarsanız yazıyı güncelleyip daha da faydalı bir hale getirebiliriz.

Bir sonraki yazımızda görüşmek üzere,

Hoşçakalın



WCF-SelfHosting-Görsel Ders

Merhaba Arkadaşlar, Bu görsel dersimizde WCF Hosting türlerinden biri olan SelfHosting türünü inceliyor olacağız. Get Microsoft Silverlight Görsel dersi buradan indirebilirsiniz. Görüşmek Üzere,


Java Platformundan WCF Servisine Erişim

Merhaba Arkadaşlar, Bildiğimiz gibi web servisleri bizim belirli bir uygulama mantığını network üzerinde kullanıma açtığımız bir altyapı olarak karşımıza çıkmaktadır. Ayrıca web servisleri belirli standartlar (XML,WSDL,SOAP vb. ) üzerinde çalıştığından dolayı platform bağımsız bir çözüm olarak tanımlanmaktadır. Yani yazdığımız bir web servisi ister JAVA olsun ister .NET olsun tüm platformlardan çağırılabilmekte ve döndürdüğü sonuçlar da yine farklı platformlardaki uygulamalar tarafından kullanılabilmektedir. Bu yazımızda ise WCF servislerine Java platformundan nasıl ulaşıldığını ve tüketildiğini inceliyor olacağız. İlk olarak tüketilecek olan web servisini hazırlayarak başlıyor olacağız. PointService adını vereceğimiz web servisi iki Point tipinde aldığı parametreyi toplayıp geriye tek bir Point tipi döndürecek. Burada Point sınıfını da servis içerisinde tanımlayarak kompleks tipte veri işlemi yapılmasını da sağlıyor olacağız.

PointService Tanımlanması

Öncelikle servisimizin operasyonlarını belirlemek amacıyla IPointService adında bir Interface oluşturacağız. Ayrıca yine servis içerisinde kullanacağımız Point sınıfını da burada tanımlayacağız. [ServiceContract] interface IPointService{     [OperationContract()]     Point Add(Point point1, Point point2); } [DataContract] public class Point{     [DataMember]     public double x { get; set; }     [DataMember]     public double y { get; set; } } Daha sonra ise bu yazdığımız interface'i gerçekleştirecek edecek olan web servisimizi tanımlayacağız.
public class PointService : IPointService
{
    public Point Add(Point point1, Point point2)
    {
         Point point = new Point();
         point.x = point1.x + point2.x;
         point.y = point1.y + point2.y;
         return point;
    }
}
WCF tarafında yapacaklarımız bu kadar. Artık servisi çalıştırıp test edebiliriz. Test ederken servisin adresini bir kenara not etmemizde fayda var. Çünkü Java tarafından servisi çağırırken bu adresi kullanıyor olacağız.Wink

Java Tarafı Nasıl Olacak ?

Java tarafına geçtiğimizde ise senaryo aslında .NET tarafındaki proxy oluşturma mekanizmasına kısmen benzemekte. Ben bu kısımda Eclipse editörünü kullanıyor olacağım. Başlangıç olarak PointServiceClient isimli bir konsol uygulaması açıyoruz. Daha sonra ise kullanacağımız WCF servisini projemize referans etmemiz gerekiyor. Bunun için projemizin üzerine sağ tıklayıp New menüsünden Other seçeneğine tıklıyoruz ve gelen ekrandan Web Service Client'ı seçiyoruz. Next ile devam ettikten sonra web servisinin WSDL dökümanını isteyen bir ekran ile karşılaşıyoruz. WSDL dökümanları bizim servislerimizin davranışını, içerisindeki tipleri, binding tiplerini tanımlayan XML tabanlı dökümanlar. Bu ekrana doğrudan WCF servisimizin WSDL dökümanı uzantısını veriyoruz. Daha sonra Finish butonuna tıklayarak proxy oluşturma işleminin sonuna geliyoruz. Bu noktada tıpkı .NET tarafında Visual Studio üzerinde yaptığımız Add Service Reference işleminin aynısını yapmış olduk. Eclipse ise kullanmamız üzere bize aşağıdaki sınıfları oluşturuyor. Gördüğümüz gibi servisimiz içerisinde kullanacağımız Point sınıfı ve diğer gerekli tüm sınıflar oluşturuldu. Bizim servisi çağırmak için kullanacağımız sınıf ise IPointServiceProxy sınıfı. Şimdi gelin Main metot içeren bir Test sınıfı yaratalım ve servisi kullanalım. Baktığımız zaman aslında farklı olarak karşıyacağımız iki nokta karşımıza çıkmakta. Bunlardan biri bizim .NET tarafında automatic property olarak tanımladığımız x ve y attributeleri için getter ve setter metotları oluşturulması. Aslında .NET tarafında da aynı şey yapılmakta idi ama biz bundan habersiz oluyorduk. Smile Bir diğer farklı kullanım ise double parametre alan setter metotta primitive tip olan double değilde bu tipi sarmalayan Double sınıfının kullanılması. Bunun dışındaki kullanımlar ise bizim .NET tarafından da aşina olduğumuz kullanımlar. Uygulamayı çalıştırdığımızda ise sonucu konsoldan gözlemleyebiliriz. Evet arkadaşlar bu yazımızda yarattığımız WCF servislerinin Java platformunda nasıl kullanıldığını incelemiş olduk. Ayrıca Web Servislerinin cross-platform olduğunu da görmüş olduk.Smile Görüşmek Üzere,