Burak Selim Şenyurt | Dayanıklı WCF Servisleri(Custom Persistence Providers)
← Ado.Net Data Services Ders Notları - 7 (Security)
Dayanıklı WCF Servisleri (Durable WCF Services) →
Hatırlayacağınız üzere bir önceki makalemizde, dayanıklı WCF servislerinin(Durable WCF Services) nasıl geliştirilebileceğini incelemeye başlamıştık. Kısaca hatırlatmak gerekirse dayanıklı WCF servislerini şu şekilde tanımlayabiliriz; belirli bir süre için durumlarını bir depolama alanında saklayarak koruyabilen ve t süre sonrasında istemci uygulama tarafından bırakıldığı haliyle kullanılabilen servisler. Konunun ilk aşamasında, varsayılan olarak SQL sunucusu üzerindeki bir depolama alanının kullanıldığı senaryo üzerinde durmuştuk. (Hatırlayaım, SQL sunucusu üzerinde veriyi saklamak yönetsel açıdan pek çok işi kolaylaştırmaktadır.) Bu makalemizde ise kaldığımız yerden devam edeceğiz ve aşağıdaki iki soruya yönelik çözümler geliştirmeye gayret edeceğiz.
1- İstemci uygulama servis örneğini kullandıktan sonra kapatılır(İstemcinin kapatma işlemi sırasında servis örneğinin saklama alanından silinmesini sağlayacak metodu çağırmadığı varsayılır). Bu durumda t süre sonrasında istemci uygulama aynı servis örneğinin içeriğini kullanmak isterse nasıl bir süreç izlenmeli ve kodlama yapılmalıdır?
2- Varsayılan olarak kullanılan SqlPersistenceProviderFactory tipinin sağladığı SQL tabanlı saklama stratejisi yerine özelleştirilmiş başka bir sağlayıcı kullanılabilir mi?(Örneğin servis örnekleri dosya tabanlı bir sistemde veya farklı bir veritabanı üzerinde saklanabilirler mi? Oracle, Access vb.)
İlk olarak 1nci sorumuza cevap arayarak yazımıza devam edelim. Bilindiği üzere istemci uygulama, servise ait bir proxy nesnesi ürettiğinde ve bunun üzerinde bazı uzak operasyonları çağırdığında, her iki taraf arasında oturum(Session) bazlı olarak dolaşan bir InstanceId değeri söz konusudur. Buna göre istemci uygulamanın daha önceden depolama alanına attığı bir servis içeriğini tekrardan kullanabilmesi için, depolama alanında duran bu servis örneğinin InstanceId değerine ihtiyacı vardır. Bu değer bilindiği üzere servis tarafında üretilip istemciye içerik ile birlikte gönderilir ve operasyon çağrılarında karşılıklı olarak kullanılır. Dolayısıyla istemci uygulama, kullanmak istediği dayanıklı WCF servisine ait GUID tipinden olan InstanceId değerine sahip ise, saklanan veri içeriğini kullanıp işlemlerine bıraktığı yerden devam edebilir. Bu noktada tabiki istemcinin doğru InstanceId değerine sahip olması önemlidir.
Diğer taraftan Exception oluşmasına neden olabilecek pek çok durum vardır. Söz gelimi istemci uygulama servis örneğinin depolama alanından kaldırılmasına neden olan bir operasyon çağrısı yaptıktan sonra, InstanceId değerini kullanarak aynı servisin içeriğine kesinlikle ulaşamaz. Çünkü servis tarafındaki depolama alanında bu InstanceId değerine sahip içerik artık bulunmamaktadır.(Hemen hatırlayalım, varsayılan olarak CompleteInstance=true değerine sahip olan servis operasyonu, Instance içeriğinin depolama alanından silinmesine neden olmaktadır.) Geliştirme sırasında bu ve benzeri istisnai durumlara dikkat edilmesi önemlidir. En azından istisna yönetim mekanizmalarından yararlanılmalı ve sistemin işleyişi çok fazla aksatılmamalıdır.
Şimdi dilerseniz ilk sorumuzun cevabını bulmak için kodlamaya yavaş yavaş geçelim. Bu noktada işlemleri daha kolay izleyebilmek adına bir önceki makalede geliştirdiğimiz CommonService isimli serviste küçük bir değişiklik yapıyoruz. Söz konusu değişiklikler ile en büyük amacımız, istemci tarafında CommonValue değerini takip ederek, ilk sorumuzu daha kolay analiz edebilmektir. Buna göre IncreaseValue metodunun, servis içerisindeki CommonValue alanının değerini geriye döndürmesini sağlıyoruz. Tabi bu değişikliğin hem servis sözleşmesinde hemde servis sözleşmesini uyarlayan tipin içerisinde yapılması gerekmektedir.
Servis tarafındaki sözleşmelerde değişiklik olması halinde(örneğin operasyonun dönüş tipi veya parametrelerinde değişiklik yapılması yada yeni operasyonların eklenmesi vb...) bu hizmeti kullanan istemci uygulamaların, servis referanslarını Update etmeleri mutlak suretle gereklidir.
Örneğimizde servis kütüphanesinde yapmış olduğu basit değişiklikler aşağıdaki gibidir.
ICommonService isimli servis sözleşmesinde yapılan değişiklik;
int IncreaseValue(int value);
Servis sözleşmesini uyarlayan CommonService sınıfında yapılan değişiklik;
public int IncreaseValue(int value)
Gelelim istemci tarafına. İstemci tarafında bu kez basit bir Windows uygulaması kullanıyor olacağız. (İstemci uygulamamız sadece test amaçlıdır bu nedenle çok fazla beklentimiz olmamalıdır :) İstemcimizin Form1 tasarımı aşağıdaki gibidir.
İstemci uygulamamızın kod içeriği ise şu şekildedir;
private ServiceCommonClient client=null;
// Proxy nesnesi örneklenir.
client = new ServiceCommonClient("WSHttpContextBinding_ServiceCommon");
lblStatus.Text = "Servis için proxy nesnesi örneklendi";
Text += " (" + DateTime.Now.ToLongTimeString() + ")";
// Start metodu çağırılır. Bu metod çağrısı ile servis tarafında InstanceData tablosuna kayıt atılır.
lblStatus.Text = "Start metodu çağırıldı";
txtInstanceId.Text = client.GetInstanceId().ToString();
private void btnIncreaseValue_Click(object sender, EventArgs e)
// Sembolik olarak değer arttırımı servis üzerinden yapılır
lblStatus.Text = "IncreaseValue metodu çağırıldı";
// Stop metodu çağırılır. Bu metod çağırıldığında servis tarafında InstanceData tablosunda saklanan satır silinir
lblStatus.Text = "Stop metodu çağırıldı";
// Daha önceden kayıt edilmiş bir instance' ı kullanmak için aşağıdaki kod parçası kullanılır
private void btnGetContext_Click(object sender, EventArgs e)
// IContextManager için System.WorkflowServices.dll assembly' ının referans edilmesi gerekir
IContextManager conMng = ((IContextChannel)client.InnerChannel).GetProperty<IContextManager>();
IDictionary<string, string> context = conMng.GetContext();
Bu uygulamada daha önceden depolanan bir servis örneğine ulaşılmak istendiğinden, System.ServiceModel.Channels isim alanındaki IContextManager isimli arayüzden yararlanılmaktadır. Bu arayüz System.WorkflowServices.dll assembly' ı içerisinde yer aldığından söz konusu .Net Framework 3.5 kütüphanesinin istemci uygulamaya referans edilmesi gerekmektedir. Örneğimiz tabiki geliştiriciler için yazılmıştır. Son kullanıcıya verildiği takdirde çalışma zamanında pek çok istisnanın oluşması muhtemel ve kaçınılmazdır.(Unutmayın şu anda bir test uygulaması geliştiriyoruz ve analiz yapmaya çalışıyoruz.)
İlk olarak servis kütüphanesini(Bir önceki makalemizden hatırlayacağınız gibi WCF kütüphaneleri otomatik olarak WcfSvcHost programı yardımıyla host edilebilmekte ve WcfTestClient ile test edilebilmektedir.) ve windows uygulamamızı çalıştıralım. Sonrasında ilk olarak Start düğmesine basacağız. Ardından IncreaseValue değerini arttırmak için bir kaç işlem yaptırılabiliriz. Ancak hiç bir şekilde Stop düğmesine basmayacağız. Bu en önemli test noktamız. Nitekim Stop düğmesine basılması sonrasında servis tarafında çağırılan operasyon, sahip olduğu DurableOperation niteliğinin CompleteInstace özelliğinin true değerine sahip olması nedeni ile instance' ın kaldırılmasına neden olacaktır. Testin bu aşamasında uygulamamızın ekran görüntüsüde aşağıdakine benzer olacaktır. Yine burada testin ikinci aşaması için önem arz eden konulardan biriside servis tarafından üretilen InstanceId değeridir. Bu değeri saklamamız gerekiyor.(Lütfen Form üzerindeki tarihe dikkat edin.)
Testimizin ikinci aşamasında uygulamamızı kapatıp çıktığımızı düşünelim. Eğer veritabanına gider ve InstanceData tablosuna bakacak olursak, servis örneğinin satır bazında eklenmiş olduğunu rahatlıkla görebiliriz.
XML verisinde ise servis tarafındaki CommonValue değişkeninin aşağıdaki şekilde görüldüğü gibi 41 olarak tutulduğu gözlemlenebilir.
Gel zaman git zaman aradan uzunca bir süre geçer ve biz aynı uygulamayı bir kere daha çalıştırırız. Ancak bu sefer, Start metodu yerine daha önceden sakladığımız servis InstanceId değerini TextBox kontrolüne ekleyip GetContext isimli metodu çağırmalıyız. Buna göre daha önceden kayıt edilmiş olan servis örneğin içeriğinin kullanılması için ilk adımı atmış oluruz ki uygulamanın çalışma zamanı ekran görüntüsüde aşağıdaki gibi olacaktır.
Dikkat edilecek olursa uygulamayı çalıştırdığımıda Form' un Text' i üzerinde saat bilgisi olarak 00:16:51 yazmaktaydı.(Gerçektende aynı günün akşamında örneği tekrardan çalıştırdığımı söyleyebilirim). Ancak şu anda saat 21:31:45 ve servis örneğinin veritabanında kayıtlı olan içeriğini kullanaraktan, CommonValue değişkeninin son değerini 51 olarak değiştirmiş bulunuyoruz. Dolayısıyla bir servisin nasıl dayanıklı hale getirilebileceğini ve bununla birlikte istemcilerin daha önceki instance' lara nasıl ulaşabileceklerini görmüş olduk.
Elbetteki gerçek hayat vakalarında söz konusu instanceId değerlerini istemci tarafında daha düzenli bir şekilde saklamak gerekebilir. Belikde uygulama kapatılırken instanceId değeri konfigurasyon düzeyinde saklanabilir ve bir sonraki açılışta değerlendirilerek istemcinin, servis içeriğinin son hali üzerinden otomatik olarak devam etmeside sağlanabilir. Tahmin edeceğiniz üzere dayanıklı WCF servisleri, Workflow tarzındaki uygulamalarda daha yaygın olarak kullanılmaktadır. Nitekim WF uygulamaları kendi içlerinde zaten var olan bir sürerlik yapısına sahiptir ve bir akışın anlık olarak uzun süreliğine saklanması mümkündür. Öyleki WCF servislerini dayanıklı hale getirmek içinde Workflow' un ana assembly' larından olan System.WorkflowServices.dll' dan yararlanılmaktadır.
Gelelim makalemize konu olan ikinci sorumuza. Özelleştirilmiş bir sürerlik sağlayıcısını(Custom Persistence Provider) nasıl geliştirebiliriz? Burada iş biraz daha zorlaşmaktadır. Bu konudaki en güzel örnek Microsoft tarafından geliştirilmiş olup MSDN den indirip inceleyebilirsiniz. Biz çok daha basit haliyle ele alacağız ve bu nedenle özellikle asenkron yaklaşımları göz ardı edeceğiz.
İlk olarak PersistenceProviders isimli bir sınıf kütüphanesi(Class Library) oluşturarak işe başlayabiliriz. Söz konusu kütüphane içerisinde özel bir sürerlik sağlayıcısı(Persistence Provider) kullanacağımız için System.ServiceModel.dll, System.WorkflowServices.dll assembly' larını referans etmemiz gerekiyor. Bunların haricinde örneğimizde kullanacağımız özel sağlayıcı, çalışma zamanındaki servis örneklerine ait verileri SOAP(SimpleObjectAccessProtocol) serileştirme yaparak saklayacaktır.
Elbetteki özel sürerlik sağlayıcısının hangi tipte veri kaynağı ile çalışacağı tamamen geliştiriceye ve ortam şartlarına bağlıdır. Sadece dosya sistemi değil Oracle gibi SQL Server harici veritabanları da kolaylıkla ele alınabilir.
Biz örneğimizde SOAP serileştirmesini kullanacağımızda System.Runtime.Serialization.Formatters.Soap.dll assembly' ınında sınıf kütüphanesine referans edilmesi gerekmektedir. Model temel olarak Abstract Factory tasarım desenine uygun olarak geliştirilmelidir.
Doğruyu söylemek gerekirse biraz karmaşık görünen bir yapı. Ancak incelendiği takdirde içerisinde asenkron operasyonların dahi yer aldığını(ki bunun uyarlamalarını eklemedik kodlarımıza), Abstract Factory deseninin uygulandığını rahatlıkla görebiliriz. Bir başka deyişle XmlPersistenceProviderFactory sınıfı, özel depolama işlemleri için XmlPersistenceProvider tipini örneklemektedir. Bu, WCF servislerinde özel sağlayıcıların üretimi sırasında kullanılan standart yoldur. Örnekte kullandığımız XmlPersistenceProviderFactory tipi, abstract PersistenceProviderFactory tipinden türemektedir. Bu sınıfın içerisinde ezilmek(override) zorunda olan CreateProvider isimli metod ise PersistenceProvider tipinden bir referans döndürmektedir.
Örneğimizde söz konusu dönüş referansı olarak PersistenceProvider sınıfından türetilen XmlPersistenceProvider tipi kullanılmaktadır. Buna ek olarak PersistenceProviderFactory tipi CommunicationObject' ten türemektedir ve bu sebeptende CreateProvider haricinde ek metodlarıda uyarlamaktadır. Söz gelimi OnBeginClose, OnEndClose gibi metodlar veya DefaulfCloseTimeout, DefaultOpenTimeout gibi özellikler CommunicationObject tipi içerisinde abstract olarak tanımlandıklarından otomatik olarak XmlPersistenceProviderFactory sınıfınada uygulanmaktadırlar. Ek olarak, XmlPersistenceProvider tipinin ortaklaşa kullanacağı Create,Load,Update,Delete gibi işlevselliklere ait metodlar, XmlPersistenceProviderFactory tipi içerisinde yer almaktadır.
İki sınıf arasındaki iletişimin sağlanması amacıyla XmlPersistenceProvider tipinin yapıcı metodu(Constructor Method) içerisine, XmlPersistenceProviderFactory sınıfının çalışma zamanı referansı aktarılmaktadır. Böylece XmlPersistenceProvider tipi içerisinden, factory sınıfında tanımlanan genel üyelere erişilebilir. (PersistenceProvider türevli sınıf istenirse ProviderPersistenceFactory türevli sınıf içerisinde dahili tip-inner type olarakta tasarlanabilir. Microsoft'un örneğinde bu tarz bir kullanım söz konusudur.) XmlPersistenceProviderFactory tipi ile ürettiği XmlPersistenceProvider sınıfının içerikleri ise aşağıdaki gibidir.
public class XmlPersistenceProviderFactory
#region Gerekli Sınıf Değişkenleri
// Servis örneklerinin saklanacağı varsayılan klasörü tutan değişken
private string fileStoragePath = Path.Combine(System.Environment.CurrentDirectory, "Instances");
// Soap serileştirmesini yapacak olan SoapFormatter değişkeni
SoapFormatter sFrmtr = null;
Logger lgr = null;
private string InstanceFileName()
// Dosya adı belirlenir. Ad belirlenirken ayırt edici olması için base sınıftan Id özelliğinin değeri kullanılır
string fileName = Path.Combine(fileStoragePath, InstanceId.ToString() + ".sxml");
#region XmlPersistenceProvider sınıfı için kullanılan ortak CRUD metodları
// Servis örneği için depolama kaynağının oluşturulması ve içeriğinin serileştirilmesi amacıyla kullanılır
public object CreateInstance(object instance, TimeSpan timeout)
string fileName = InstanceFileName();
// Soap formatter tipinden yararlanılarak servis örneğinin serileştirilmesi sağlanır
lgr.AddEventEntry(fileName + " için Create işlemi yapıldı, Serileştirme gerçekleştirildi.", System.Diagnostics.EventLogEntryType.Information);
// Servis örneğinin depolama kaynağının silinmesi amacıyla kullanılır
public void DeleteInstance(object instance, TimeSpan timeout)
// Dosya adı elde edilir
// Eğer dosya sistemde var ise
// Silme bilgsi loglanır
lgr.AddEventEntry(fileName + " için Delete işlemi yapıldı.", System.Diagnostics.EventLogEntryType.Information);
else // yok ise
// Dosya sistemde bulunamadıysa normal şartların aksine başka bir nedenle silinmiş olabilir. Bu durum loga aktarılır
lgr.AddEventEntry(fileName + " sistemde bulunamadığından Delete işlemi yapılamadı", System.Diagnostics.EventLogEntryType.Warning);
// Kaydedilmiş olan servis örneğinin kaynaktan alınıp kullanılabilir object haline getirilmesi için kullanılır
public object LoadInstance(TimeSpan timeout)
if (File.Exists(fileName)) // dosya mevcutsa
// loglama yap
lgr.AddEventEntry(fileName + " için Load işlemi yapıldı", System.Diagnostics.EventLogEntryType.Information);
// servis örneğinin dosya içerisinde tutulan serileştirilmiş örneğini deSerialize ederk geriye döndür
result = sFrmtr.Deserialize(stream);
else // dosya bulunamıyorsa
// uyarı mesajı niteliğinde loglama yap
lgr.AddEventEntry(fileName + " sistemde bulunamadığından Load işlemi gerçekleştirilemedi", System.Diagnostics.EventLogEntryType.Warning);
// Kaynakta tutulan servis örneğinin güncellenmesi işlemini gerçekleştirir.
public object UpdateInstance(object instance, TimeSpan timeout)
// Dosya adı alınır
// Güncellenen servis örneği tekrardan aynı dosya üzerine serileştirilir
lgr.AddEventEntry(fileName + " için Update işlemi yapıldı, Serileştirme gerçekleştirildi.", System.Diagnostics.EventLogEntryType.Information);
// Eğer servis örneklerinin saklanacağı varsayılan klasör yoksa oluştur
// Soap Formatter değişkeni oluşturulur
sFrmtr = new SoapFormatter();
// Loglama işlemini yapacak olan sınıf örneği oluşturulur
lgr = new Logger("CustomXmlPersistenceProvider", "Custom Xml Persistence Log(WCF)");
lgr.AddEventEntry("XmlPersistenceProviderFactory örneği oluşturuldu", System.Diagnostics.EventLogEntryType.SuccessAudit);
public override PersistenceProvider CreateProvider(Guid id)
return new XmlPersistenceProvider(id, this);
Görüldüğü gibi basit serileştirme(Serialization) ve ters-serileştirme(DeSerialization) işlemleri gerçekleştirilmektedir. Bununla birlikte örneğin bizim için(en azından benim için) kolay anlaşılabilir olması adına asenkron metodların içerikleri uyarlanmamıştır.
XmlPersistenceProvider sınıfının içeriği;
public class XmlPersistenceProvider
public XmlPersistenceProvider(Guid id, XmlPersistenceProviderFactory xmlFactory)
XmlPrsFactory = xmlFactory;
#region PersistenceProvider ve CommunicationObject Üyeleri
// Servis örneği depolama alanında ilk oluşturulduğunda çalışır.
public override IAsyncResult BeginCreate(object instance, TimeSpan timeout, AsyncCallback callback, object state)
// Silme işlemi başladığında devreye girer.
public override IAsyncResult BeginDelete(object instance, TimeSpan timeout, AsyncCallback callback, object state)
// Yükleme işlemi başladığından devreye girer.
// Servis örneği güncellemeye başladığında devreye girer
public override IAsyncResult BeginUpdate(object instance, TimeSpan timeout, AsyncCallback callback, object state)
// Depolama alanında servis örneğinin kayıt altına alınmasında kullanılır
public override object Create(object instance, TimeSpan timeout)
return XmlPrsFactory.CreateInstance(instance, timeout);
// Servis örneğinin depolama alanında silinmesinin ele alındığı metoddur
public override void Delete(object instance, TimeSpan timeout)
// Servis örneğinin veri içeriğinin depolama alanına ilk kayıt edildiği aşamanın sonunda devreye giren metoddur
public override object EndCreate(IAsyncResult result)
// Servis örneğine ait veri içeriğinin depolama alanında silindiği aşamının sonunda devreye giren metoddur
public override void EndDelete(IAsyncResult result)
// Servis örneği verisinin depolama alanında ortama yüklendiği aşamın sonunda devreye giren metoddur
public override object EndLoad(IAsyncResult result)
// Servis örneği verisinin depolama alanında güncelleştirilmesi sonrasında devreye giren metoddur
public override object EndUpdate(IAsyncResult result)
// Servis örneği verisinin depolama alanından yükenmesi aşamasının ele alındığı metoddur
public override object Load(TimeSpan timeout)
// Servis örneği verisinin depolama alanında güncelleştirilmesi aşamasının ele alındığı metoddur
public override object Update(object instance, TimeSpan timeout)
return XmlPrsFactory.UpdateInstance(instance, timeout);
// Kapatma operasyonun tamamlanması için gereken varsayılan sürenin belirlendiği özelliktir
// Açma operasyonunun tamamlanması için gereken varsayılan sürenin belirlendiği özelliktir
Dikkat edileceği üzere XmlPersistenceProvider tipi ağırlıklı olarak CRUD işlemleri için gerekli olan zorunlu metod çağrılarını PersistenceProvider abstract sınıfından devralmaktadır. Create, Update, Delete ve Load isimli zorunlu olarak ezilen metodlar, XmlPersistenceProviderFactory tipi içerisinde tanımlanmış olan CreateInstance, UpdateInstance, DeleteInstance ve LoadInstance fonksiyonelliklerini çağırmaktadır. Aslında Factory sınıfı kendisini kullanan birden fazla PersistenceProvider türevli tipide ele alabilir. Hepsi için ortak olabilecek pek çok fonksiyonellik fabrika tipi içerisinde toplanabileceği gibi, PersistenceProvider türevli tiplerede yayılabilir. (Bence bu konuyu araştırabilirsiniz. Tek bir PersistenceProviderFactory türevli tip üstünden birden fazla PersistenceProvider türevli tipi kullanabilmek ve bunları farklı vakalar için ele alabilmek.)
Son olarak, kütüphanemizde süreci, oluşabilecek çalışma zamanı hatalarını loglamak amacıyla Logger isimli birde yardımcı sınıfımız bulunmaktadır. Bu sınıf yardımıyla kodun gerekli yerlerinde ilgili bilgileri günlüklere işleme şansına sahip oluyoruzki, geliştirme(Development) ve özellikle test aşamalarında bu oldukça işimize yarayacak bir özelliktir. Söz gelimi WCF kütüphanesinin bir Windows Servisi içerisinde host edilmesi halinde, hataların ayıklanması için ekstra çaba sarfetmek gerekmektedir. Bu nedenle Windows Servisinin host olarak kullanıldığı bir senaryoda log kayıtları hayati bilgiler verebilir ve hataların tespit edilmesi çok daha kolaylaşabilir.
Logger sınıfımızın içeriği ise aşağıdaki gibidir;
/// Loglama işlemleri için yardımcı sınıftır
private string LogSource;
/// Event Viewer içerisinde ayrı bir log sekmesi oluşturur
/// <param name="logSource">Log verisinin kaynağı(Genellikle işlem adı yada program adı olarak kullanılabilir)</param>
/// <param name="logName">Event Viewer altındaki boğumun adıdır</param>
public Logger(string logSource, string logName)
LogSource = logSource;
/// Yapıcı metodda oluşturulan log kaynağına event yazdırır
/// <param name="mesaj">Event ile ilişkili bilgi</param>
/// <param name="entryType">Event in EventLogEntryType tipinden değeridir</param>
public void AddEventEntry(string mesaj, EventLogEntryType entryType)
EventLog.WriteEntry(LogSource, mesaj, entryType);
Örnek sürerlik sağlayıcımız, uygulamanın olduğu yerde(yada serivis host eden uygulamanın olduğu yerde) Instances adlı bir klasör açmaktadır.
Uygulamada dosyaların kaydedileceği klasör bilgisi konfigurasyon seviyesinde de tutulabilir. Hatırlayalım, SqlPersistenceProviderFactory sağlayıcısı kullanıldığında örneklerin saklanması için kullanılacak veritabanı bağlantısı, ConnectionString elementinin üzerinden alınmaktadır.
Çalışma zamanındaki servislere ait örnekler bu klasör altında sxml uzantılı olarak tutulmaktadır. İçerikleri tahmin edileceği üzere SOAP formatındadır. Dosya adları servis örneğine ait InstanceId değerleri olarak verilmektedir. Akış son derece basittir. DurableOperation niteliğinin CanCreateInstance özelliğinin değeri true olan operasyon çağrısı ile bu klasörde bir dosya oluşturulmakta ve servis örneğine ait içerik serileştirilmektedir. Sonrasında yapılan operasyon çağrılarında bu dosya içeriğinin ters serileştirilerek object olarak elde edilmesi ve içeriğinin kullanılması söz konusudur. Yine DurableOperation niteliğinin CompleteInstance özelliği true olan operasyon çağırıldığında söz konusu servis örneğine ait dosya, Instances klasöründen silinmektedir. Tabi istenirse söz konusu dosyanın silinmeyip yeniden isimlendirilerek örneğin belirli süreliğine sistemde tutulması gibi işlemlerde yapılabilir.
Bu teorik akışı elbette test ederek onaylamamız gerekmektedir. Bunun için basit bir istemci ve servis host uygulaması yazabilir yada özel sürerlik sağlayıcılarını kullanan servis kütüphanesini doğrudan Visual Studio 2008 ortamında çalıştırabiliriz. Hangisi kullanılırsa kullanılsın servis tarafındaki konfigurasyon içeriğinde bir önceki makalemizde yaptığımız gibi PersistenceProviderFactory türevli tipin belirtilmesi gerekmektedir. Örnek uygulamada bu amaçla geliştirilmiş olan CustomServiceLib isimli servis kütüphanesi bir önceki makalede geliştirdiğimiz ICommonService sözleşmesi ile CommonService tipini kullanmaktadır. App.config dosyasının içeriği ise aşağıdaki gibidir.
<persistenceProvider type="PersistenceProviders.XmlPersistenceProviderFactory,PersistenceProviders, Verison=1.0.0.0" />
<service behaviorConfiguration="CommonServiceBehavior" name="CustomServiceLib.CommonService">
<endpoint address="" binding="wsHttpContextBinding" bindingConfiguration="" name="CommonServiceWsHttpEndPoint" contract="CustomServiceLib.ICommonService" />
<endpoint binding="mexHttpBinding" bindingConfiguration="" name="MexEndPoint" contract="IMetadataExchange" address="Mex" />
<add baseAddress="http://localhost:40001/CommonServiceV2" />
Görüldüğü üzere, persistenceProvider elementi içerisinde özelleştirilmiş olan XmlPersistenceProviderFactory tipi belirtilmektedir. Söz konusu tipin bulunduğu kütüphane referans etme yoluyla kullanıldığından br önceki örnekte olduğu gibi Culture veya public key bilgileri bulunmamaktadır. Elbetteki özelleştirilmiş depolama sağlayıcısını bünyesinde barındıran kütüphaneyi Global Assembly Cache' e atabiliriz. Bu durumda konfigurasyon içerisinde Full Qualified Name' in belirtilmesi gerekmektedir.
CustomServiceLib kütüphanesini direkt olarak Visual Studio 2008 üzerinden çalıştırarak ilerleyebiliriz. WcfSvcHost ve paralelinde Wcf Test Client çalıştırıldıktan sonra ilk yapmamız gereken Start metodunu çağırmak olmalıdır. Bu noktada uygulamanın çalıştığı yerdeki Instances klasörüne baktığımızda aşağıdaki ekran görüntüsünde olduğu gibi sxml uzantılı ve GUID değeri ile adlandırılmış bir dosyanın oluşturulduğunu görürüz.
Bu adımdan sonra test olarak IncreaseValue metodu değişik değerler ile çağırlabilir. Operasyonun her çağırılmasında, servis örneğine ait içeriğin oluşturulan dosyadan yüklenmesi, ters serileştirildikten sonra içeride yer alan CommonValue değerinin arttırılması ve örneğin son halinin tekrardan aynı dosya üzerine serileştirilmesi adımları gerçekleşir. Örneğin şu noktada sxml isimli dosyanın içeriğine bakılırsa aşağıdaki XML bilgisinin tutulduğu görülebilir.
Bu andan itibaren Stop metodu çağırılmadan Wcf Test Client uygulamasından çıkılabilir. Hatta aynı örnek defalarca çağırılıp Stop operasyonu kullanılmadan test edilebilir. Her durumda Instances klasöründe birer dosya oluşturulacak ve herhangibir nedenle silinene kadar sahip oldukları InstanceId değerleri için servis tarafı istemcilere hizmet verebilecektir. Ancak Stop metodu çağırılırsa bu durumda Instances klasöründe bununla ilişkili olan dosyanın silindiği açık bir şekilde görülebilir. İşleyişi daha net bir şekilde anlayabilmek için kodu debug ederek çalıştırmanızı öneririm. Bu durumda Create, Update, Delete, Load metodları arasındaki geçişleri çok daha kolay bir şekilde analiz edebilirsiniz. Son olarakta servis kütüphanesini kullanan bir sunucu uygulama ve istemci geliştirerek yeni sağlayıcıyı test etmenizi ve sistemin düzgün yürüdüğünden emin olmanızı öneririm.
Böylece geldik bir makalemizin daha sonuna. Bu makalemizde dayanıklı WCF servisleri ilişkili iki önemli soruya cevap bulmaya çalışltık. İlk olarak istemcilerin var olan InstanceId' leri kullanarak servislerin, sunucu üzerinde uzun süreliğine saklanabilen hallerini nasıl kullanabileceklerine değinik. Sonrasında ise özel bir sürerlik sağlayıcısının(Custom Persistence Provider) nasıl yazılabileceğini gördük. Tabiki buradaki örnekleri geliştirmek, genişletmek tamamen sizin elinizdedir. Bir sonraki makalemizde görüşünceye dek hepinize mutlu günler dilerim.