Quantcast
Channel: Burak Selim ŞenyurtBurak Selim Şenyurt0.0000000.000000
Viewing all 513 articles
Browse latest View live

Asp.Net–Doğru async, await Kullanımı

$
0
0

Merhaba Arkadaşlar,

Bazen web sayfalarının yüklenmesi sırasında senkron olarak çalışan ve uzun süren işlemler gerçekleştiririz(ki aslında Web uygulamalarında bu tip yaklaşımları pek tercih etmeyiz) Sayfada ki kontrollerde gösterilmek üzere çeşitli kaynaklardan veri çekilmesi buna örnek olarak verilebilir. Bu tip veri yükleme işlemleri ağırlıklı olarak Page_Load olay metodu içerisinde gerçekleştirilir. Uzun süren işlemlerin kısa sürede tamamlanabilmesi için farklı teknikler mevcuttur. Bir tanesi de asenkron olarak çalışabilmelerini sağlamaktır(Örneğin zaman kaybettiren servis çağrılarının, veri çekme işlemlerinin eş zamanlı hale getirilmesi)

.Net dünyasında bu tip asenkron işleri kolaylaştıran async, await keyword’ leri artık yaygın olarak kullanılmakta. Lakin Web dünyasında biraz daha dikkatli olmak gerekir. Nitekim bir web sayfasının yaşam döngüsü(Page Life-Cycle),async geri bildirimlerini sorunsuz şekilde ele alan Windows UI Thread’ lerinden biraz daha farklı çalışmaktadır. Güvenilir ve stabil bir ortam söz konusu değildir. Dahası HTTP 503 hatasının alınmasına neden olabilecek vakalar vardır. İşte bu görsel dersimizde bir sayfanın yüklenmesi esnasında asenkron hale getirilmesi istenen işlemlerde uygulanabilecek doğru ve tavsiye edilen bir pratiği incelemeye çalışacağız.

Bir başka görsel dersimizde görüşmek dileğiyle.


Asp.Net–Farklı Ortam, Farklı Config, Farklı Deploy

$
0
0

Merhaba Arkadaşlar,

Bazen geliştirdiğimiz web uygulamaları farklı ortamlar için farklı parametrik değerler kullanır. Bu parametre değerleri çoğunlukla web.config dosyası içerisinde yer alır. Böyle bir durumda ortamlara göre Deployment yapmak zahmetli bir hal alabilir. Nitekim yaygın olarak kullanılan dört farklı ortam söz konusudur. Development, Test, PreProd ve Prod. Her bir ortam için parametreler farklı değerlere sahip olabilir/olması gerekir. Bu yüzden Publish adımlarında, ortamlara göre Profile hazırlanması tercih edilir. Peki bu farklı profiller, config dosyaları içerisindeki çeşitli değerleri (veritabanı bağlantıları, proxy veya servis adresleri, fiziki path bildirimleri vb) ortamlara göre nasıl değiştirebilir? İşte bu görsel dersimizde bu soruya cevap bulmaya çalışıyoruz.

Bir başka görsel dersimizde görüşünceye dek hepinize mutlu günler dilerim.

NLog için Oracle Database Kullanmak

$
0
0

marlin-doryMerhaba Arkadaşlar,

Animasyon film meraklısı olupta Pixar’ ın 2003 yapımı Nemo’ sunu izlemeyen sanırım yoktur. Kayıp oğlu Nemo’ yu bulmak isteyen Marlin, uzun ve zorlu yolculuğu sırasında oldukça unutkan olan ve aslında bu özelliği ile balık olduğunu adeta tüm izleyenlere ispat eden Dory ile seyahat etmektedir. Dory neredeyse bir saniye önce söylediğini hatırlamakta zorlanan bir balıktır.

Aslında geliştirmekte olduğumuz uygulamaların da buna benzer handikapları vardır. Bir şeyler hatırlamak zorundadırlar ve bu yüzden çeşitli depolama ortamlarını kullanırlar. Bu depolar ile olan iletişimlerinde çeşitli stratejiler uygularlar. Özellikle web tabanlı uygulamalar söz konusu olduğunda istemciler ile olan iletişimde de unutkanlık halleri baş gösterir. Web, doğası gereği çoğu zaman State tutmakta zorlanan bir ortamdır.

Unutkanlığın baş gösterdiği bir diğer yer ise uygulamalar üzerindeki işlem hareketliliklerdir. Öyleki genel performansın gözlemlenmesinde, erken uyarı sistemlerinin oluşturulmasında, hataların kodun içerisine girilmeden analiz edilmesinde, müşterilerin geçmişe yönelik hareketlerinin kayıt altına alınmasında ve benzer bazı senaryolarda uygulamanın o anki hafızasının kayıt altına alınması gerekebilir. Ne yazık ki uygulamalar bunu otomatik olarak yapmazlar. Bunu gerçekleştirebilmeleri için bir Log mekanizmasına sahip olmaları ve unutkanlıklarını kayıt altına alabilmeleri gerekir. Tabi ki geliştiricilerin yardımıyla.

Daha önceden Log4Netaracını incelemiş ve pek çok projede kullanmıştım ama hayat bizi farklı kaynaklarla çalışmaya da itebiliyor. Öyle ki yakın zamanda popüler loglama araçlarından olan NLog kütüphanesini kullanma fırsatı buldum. Ve bu sefer gerek kayıt altına alınacak bilgiler gerekse logun yazılacağı ortam biraz farklıydı. Log’ ların veritabanına, kurumun Audit mekanizmasına uygun kurallar dahilinde yazılması zorunluydu. Yıllarca alışkın olduğum SQL Server yerine bu kez karşımda Oracle vardı. Ve sonuçta bir vaka çalışması ortaya çıktı. Haydi gelin senaryomuz ile makalemize başlayalım.

Senaryo

Log içeriklerinin nereye yazılacaği önemlidir. Pek çok kurumsal projede veri kaynağı olarak SQLServer veya Oracle gibi RDBMS(Relational Database Management System) hizmetleri kullanılır. Bu çaptaki projelerde veri, hacimsel olarak büyüktür ve transaction sayıları oldukça yüksektir. Dolayısıyla sistem üzerindeki hareketliliklerin izlenmesi noktasında log bilgilerinin bir text dosyasına yazılması tercih edilmez. Bunun yerine söz konusu log içeriklerinin veri tabanı üzerinde konuşlandırılması gerekir. Nitekim oluşan log bilgileri gelecek zamanlarda hayati önem taşıyacağından kolayca sorgulanabilir olmalıdır.

Özellikle SOA dünyasında, servislerin birbirleriyle ve diğer farklı uygulamalarla çokça konuştukları ortamlarda, Log bilgilerinden yararlanılarak sistemin genel performansı ölçümlenebilir ve hatta önceden uyarı verecek alarm sistemleri tasarlanabilir. Söz gelimi bir servisin giderek daha yavaş cevap vermesi log’a yazılan bilgilere bakılarak önceden tahmin edilebilir. Dolayısıyla Log diyip geçmemek gerekir.

İşte bu felsefeden yola çıkarak örnek senaryomuzda basit bir Asp.Net Web Applicationüzerindeki hareketlilikleri, Oracle veritabanında oluşturulan bir tablo içerisine NLog paketi yardımıyla atmaya çalışacağız. Öncelikle web uygulamasını oluşturup gerekli kütüphane ve konfigurasyon içeriklerini dahil ederek işe başlamakta yarar var. 

Ön Hazırlıklar

Senaryoya göre NLog ile ilişkili kütüphanelerin ve gerekli konfigurasyon dosyalarının web uygulamasına referans edilmesi gerekmektedir. NLog' un Oracle veri tabanı yönünde log atabilmesi için bir Provider bildiriminin yapılması gerekmektedir. Özetle web uygulamasının referansları ve kullanacağı konfigurasyon içerikleri aşağıdaki gibidir.

  • NLog.dll (Çekirdek log kütüphanemiz)
  • NLog.config (Çekirdek log mekanizmasına ait konfigurasyon ayarlarını barındıran dosyadır. Copy to output Directory özelliğinin Copy If Newer yapılmasında fayda bulunmaktadır)
  • NLog.xsd (NLog.config dosyası için gerekli olan XML şema içeriğidir. Özellikle NLog.config içerisine yazılan XML içeriğinin denetlenmesinde rol almaktadır) 
  • NLog.Extended.dll (${aspnet-sessionid} gibi genişletilmiş Layout' ların kullanılmasını sağlamaktadır)

NLog ve NLog.Extended kütüphaneleri, NuGet paket yönetim aracı üzerinden uygulamaya kolayca yüklenebilirler.

lto_1lto_2

Oracle Database Hazırlıkları

Oracle tarafında aşağıdaki şema yapısına sahip bir tablo ve trigger kullanılabilir.

CREATE TABLE ApplicationLogs
    (TraceTime                  TIMESTAMP (7),
    LogLevel                    VARCHAR2(50 BYTE),
    Logger                      VARCHAR2(250 BYTE),
    Message                     VARCHAR2(1000 BYTE),
    MachineName                 VARCHAR2(20 BYTE),
    UserName                    VARCHAR2(30 BYTE),
    CallSite                    VARCHAR2(1000 BYTE),
    ThreadId                    VARCHAR2(10 BYTE),
    ExceptionMessage            VARCHAR2(1000 BYTE),
    StackTrace                  VARCHAR2(1000 BYTE),
    SessionID                   VARCHAR2(50 BYTE)
    );

CREATE OR REPLACE TRIGGER ApplicationLogsTrigger
BEFORE
  INSERT
ON ApplicationLogs
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
    :new.TraceTime:= SYSTIMESTAMP;
END;

ApplicationLogs tablosu içerisinde log için kullanılabilecek bir kaç alan bulunmaktadır. Message alanının içeriği tahmin edileceği üzere kod tarafında geliştirici tarafından üretilmektedir. Diğer yandan temel bir kaç çevre değişkeni de NLog mekanizması tarafından doldurulmaktadır. Örneğin Info, Warn, Exception gibi log seviyeleri için LogLevel alanı, Web uygulamasında oturum açan kullanıcıyı benzersiz şekilde tanımlayan Session bilgisi için SessionID alanı, bir çalışma zamanı istisnası oluşması halinde ele alınabilecek ExceptionMessage alanı vb. ApplicationLogsTrigger isimli Trigger'ın temel amacı ise log' un oluşturulma zamanının TraceTime alanına yazılmasını sağlamaktır. Burada SysTimeStamp fonksiyonundan yararlanılmıştır.

Özellikle Web uygulamalarının log’lanmasında güncel oturum bilgisi önemlidir. Nitekim SessionID, log yığını içerisinde oturumun benzersiz bir tanımlayıcısı şeklinde ele alınabilir ki bu sayede her oturumun log hikayesinin farklılaştırılması mümkün hale gelir.

NLog.Config İçeriği

Gelelim NLog konfigurasyon ayarlarına. Dosya içeriğinin aşağıdaki gibi oluşturulması gerekmektedir.

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      internalLogLevel="Debug"     
       internalLogFile="c:\InteralLogs.txt"
      >
  <extensions>
    <add assembly="NLog.Extended" />
  </extensions>
  <targets>   
    <targetname="database" xsi:type="Database" keepConnection="false" useTransactions="true"
          connectionStringName="ConStr"
          commandText="INSERT INTO APPLICATIONLOGS (LOGLEVEL,LOGGER,MESSAGE,MACHINENAME,USERNAME,CALLSITE, THREADID,EXCEPTIONMESSAGE,STACKTRACE,SESSIONID)
              VALUES (:pLEVEL,:pLOGGER,:pMESSAGE,:pMACHINENAME,:pUSERNAME, :pCALLSITE,:pTHREADID,:pEXCEPTIONMESSAGE,:pSTACKTRACE,:pSESSIONID)">
      <parameter name="pLEVEL" layout="${level}"/>
      <parameter name="pLOGGER" layout="${logger}"/>
      <parameter name="pMESSAGE" layout="${message}"/>
      <parameter name="pMACHINENAME" layout="${machinename}"/>
      <parameter name="pUSERNAME" layout="${windows-identity:domain=true}"/>
      <parameter name="pCALLSITE" layout="${callsite:filename=true}"/>
      <parameter name="pTHREADID" layout="${threadid}"/>
      <parameter name="pEXCEPTIONMESSAGE" layout="${exception}"/>
      <parameter name="pSTACKTRACE" layout="${stacktrace}"/>
      <parameter name="pSESSIONID" layout="${aspnet-sessionid}"/>
    </target>
  </targets>
  <rules>
    <logger name="*" minlevel="Trace" writeTo="database" />
  </rules>
</nlog>

Konfigurasyon içeriğinde dikkat edilmesi gereken bir kaç nokta bulunmaktadır. Bunları aşağıdaki maddeler ile özetleyebiliriz.

  • Her ihtimale karşı sistemi ayağa kaldırırken NLog bazı sorunlar yaşayabilir ve bu sebepten beklediğimiz şekilde log atmayabilir. Örneğin Oracle tarafından gelecek bir hata sonrası(geçersiz bir kolon adı kullanılması, eksik parametre yazılması vb)NLog kendi içinde exception' lar verebilir. Bu yüzden internalLogLevel ve internalLogFile isimli nitelikler belirlenmiştir. Sistemin çalışırlığından emin olduktan sonra bu değerler kaldırılabilir.
  • extensions elementi altında yapılan bildirim ile NLog.Extended kütüphanesi içerisindeki tiplerin kullanılabilmesi sağlanır. Örnekte Asp.Net SessionID değerinin alınması sırasında kullanılan layout için bu bildirimin yapılması gerekmektedir.
  • Elbette en can alıcı nokta target kısmıdır. Burada, hangi bağlantı üzerinden veri tabanına gildileceği ve çalıştırılacak olan SQL ifadesi belirtilmektedir.(Database Target’ da kullanılabilecek alternatiler için şu adrese bakabilirsiniz)
  • connectionStringNameözelliğine atanan değer, web.config dosyası içerisindeki connectionString elementini işaret etmektedir. Uygulamada kullanılan web.config içeriği aşağıdaki gibidir.

    <?xml version="1.0"?>
    <configuration>
      <connectionStrings>
        <add name="ConStr"
             connectionString="Data Source=CookShop;User Id=scoth;Password=tiger;"
             providerName="System.Data.OracleClient"/>
      </connectionStrings>
      <system.web>
        <compilation debug="true" targetFramework="4.5"/>
        <httpRuntime targetFramework="4.5"/>   
      </system.web>
    </configuration>

    Dikkat edilmesi gereken en önemli ayrıntı providerName bildirimidir. Bu bildirim yapılmadığı takdirde log atma işlemi gerçekleşmeyecektir. Durumu daha net kavramak adına providerName niteliğini kaldırıp InternalLog dosyasında oluşan içeriğe bakılabilir. Bu durumda aşağıdaki ekran görüntüsünde yer aldığı gibi Providerüretimi sırasında bir exception fırlatıldığı fark edilecektir. Bunun doğal sonucu uygulamanın log atamayacak olmasıdır.
    lto_4
  • commandText niteliğine atanan ifade de Insert parametreleri kullanılmıştır. Parametre bildirimleri için parameter elementinden yararlanılmaktadır. Her elementin layoutözelliğinde $ ile başlayan birer NLog anahtar kelimesi ele alınmaktadır.
  • loglama mekanizması için hedef hizmet rules elementi içerisinde bildirilmektedir. Burada writeTo niteliğine atanan değerin target elementinde bir karşılığı vardır.

Örnekte connectionString bilgisinin okunabilir şekilde yazıldığı görülmektedir. Pek tabi kurumsal çaptaki bir proje de bu bilginin şifrelenmiş şekilde durması tercih edilir. Nitekim ürünün dahil olduğu Audit mekanizmaları böyle bir kullanıma müsade edemez.

Peki şifrelenmiş bir bağlantıyı NLog mekanizması nasıl kullanabilir? Eğer özel bir şifreleme algoritması ile oluşturulmuş bir içerik söz konusu ise, Decryption işini üstlenen kütüphane NLog çalışma zamanına nasıl bildirilebilir? İşte size güzel bir araştırma konusu.

Başlangıç olarak, Logger tipinin üzerinden veritabanına ilişkin bağlantı bilgisine kod bazında ulaşılabildiğini belirtelim. Dolayısıyla config içerisinde Encrypt edilmiş bir bağlantı bilgisi, Logger tipinin hazırlanacağı bir fonksiyonellikte Decrypt edilip atanabilir. Söz gelimi aşağıdaki kod parçası bu anlamda size bir ip ucu verebilir.

Logger logger = LogManager.GetCurrentClassLogger();
var db = (DatabaseTarget)logger.Factory.Configuration.AllTargets
              .Where(t => t.Name == "database").FirstOrDefault();
db.ConnectionString = ConnectionInitializer.GetConnectionString();

Gelelim web uygulamasının içeriğine.

Web Uygulamasının Geliştirilmesi

Asp.Net uygulaması içerisinde tamamen senaryonun amacına hizmet eden sembolik bir işleyiş söz konusudur. Default.aspx içeriği bu anlamda aşağıdaki gibi oluşturulabilir.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CookShop.Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="lblEmail" runat="server" Text="Email" />
        <asp:TextBox ID="txtEmail" runat="server" />
        <br />
        <asp:Label ID="lblRecipeName" runat="server" Text="Recipe Name" />
        <asp:TextBox ID="txtRecipeName" runat="server" />
        <br />
        <asp:Button ID="btnGetRecipe" runat="server" Text="GetRecipe" OnClick="btnGetRecipe_Click" />
    </div>
    </form>
</body>
</html>

Kod içeriği ise aşağıdaki gibi yazılabilir.

using System;
using System.Diagnostics;
using System.Threading;
using NLog;

namespace CookShop
{
    public partial class Default
: System.Web.UI.Page
    {
        Logger nemo = LogManager.GetCurrentClassLogger();

        protected void btnGetRecipe_Click(object sender, EventArgs e)
        {
            try
            {
                nemo.Info("Yemek reçetisi tedarik süreci başladı");
                if (ValidateRecipe(txtRecipeName.Text))
                {
                    FindRecipe(txtRecipeName.Text);
                    SendRecipeToUser(txtEmail.Text);
                }

                // Sembolik olarak bir Exception fırlatıldı
                throw new OutOfMemoryException();
            }
            catch (Exception excp)
            {
               nemo.ErrorException("Reçetenin elde edilmesi sürecinde hata oluştu.", excp);      
            }

            nemo.Info("İşlemler tamamlandı");
        }

        private void SendRecipeToUser(string EmailAddress)
        {  
            nemo.Info(string.Format("{0} adresine yemek tarifi gönderilecek."
                , string.IsNullOrEmpty(EmailAddress)?"sendWithSMS" :EmailAddress));
        }

        private void FindRecipe(string RecipeName)
        {
            Stopwatch watcher = new Stopwatch();
            watcher.Start();
            Thread.Sleep(3250); // Sembolik olarak bir gecikme uygulattık
            watcher.Stop();
            nemo.Info(string.Format("{0}:{1} yemek reçetesinin bulunması için geçen toplam süre.",watcher.Elapsed.TotalSeconds,RecipeName));
        }

        private bool ValidateRecipe(string RecipeName)
        {
            // Sembolik olarak RecipeName' in boş geçilmesini
            if (string.IsNullOrEmpty(RecipeName))
            {
                nemo.Warn("Yemeğin adı boş geçilmiş");
                return false;
            }
            else
            {
                nemo.Info(string.Format("{0} doğrulandı.", RecipeName));
                return true;
            }
        }
    }
}

Log yazma operasyonunu nemo isimli Logger nesne örneği üstlenmektedir. Oluşturulması için LogManager sınıfının static GetCurrentClassLogger metodundan yararlanılmaktadır. Bu, varsayılan olarak NLog.config içerisindeki ayarları göz önüne alacak şekilde log hizmetini ayağa kaldıracaktır. Kodun çeşitli kısımlarında Info, Warn, ErrorException gibi fonksiyon çağrıları yapılarak örnek log bilgilerinin yazdırılması sağlanmaktadır.

Çalışma Zamanı

Uygulamanın çalışmasından ve butona basılarak bir takım işlemlerin icra edilmesinden ziyade Oracle tarafındaki ApplicationLogs içeriğinin dolması daha önemlidir. Eğer konfigurasyon ayarları sorunsuz yapıldıysa, Oracle ile uygulamanın konuşması ve Insert işlemlerini icra etmesi noktasında bir yetki problemi yoksa, çalışma zamanı için aşağıdakine benzer sonuçların elde edilmesi gerekmektedir.

lto_3

Sonuçlar

Görüldüğü üzere NLog mekanizmasında loglama yönünü Oracle tarafına çekmek oldukça kolaydır. Dikkat edilmesi gereken noktaların başında, doğru provider sürümünün kullanılması gelir. Şu unutulmamalıdır ki, geliştirici ortamı ile test, preprod, prod ortamları farklılıklar gösterebilir. Yerel makinede seçilen database provider tipi ile sorunsuz çalışılması bir production ortamında geçerli olmayabilir.

Diğer bir husus da, uygulamanın hangi noktasından, hangi seviyede ne çeşit bilgilerin Log olarak yazdırılacağıdır. Log’ un içeriğini belirlemek bu makalenin konusu olmamakla birlikte ileride devasa şekilde büyüyecek veri kümesinin araştırılabilir olarak tasarlanması gerektiği de akıldan çıkartılmamalıdır.

Böylece geldik bir makalemizin daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

Yazılımcı Empatisi

$
0
0

Merhaba Arkadaşlar

Pek çoğumuz nesne yönelimli bir dili öğrenmeye çalışırken eminim ki Inheritance, Polymorphism, Encapsulation gibi kavramları anlamakta zorlanmıştır. Hatta daha da temel olan sınıfın ne işe yaradığını ve hangi amaçlarla kullanıldığını kestirmek için uzun zaman boşluğa bakmıştır. Oysa ki, bu gibi aktörleri daha iyi bir şekilde öğrenebilmemin çok basit yolları vardır. Bunlardan birisi Empati yapmaktır. Yani kendimizi C#/Java gibi ilgili dilin yerine koymak ve “ben olsam şu gerçek hayat problemindeki x varlığını nasıl ifade ederdim?” sorusuna cevap bulabilmektir.

Bu sadece OOP(Object Oriented Programming) dünyası için değil, RDBMS(Relational Database Management System) ler için, W3C’ un standartlaştırdığı XML için, JSON için ve hatta SOA için de böyledir. Hatta enterprise bir ürünün geliştirilmesinde rol alan ürün yöneticisi, CRM ekibinde raporlama ile uğraşan bir uzman veya iş birimindeki bir analist gibi düşünmeye çalışmakta buna benzer bir yaklaşımdır.

Bu konu nereden çıktı derseniz? Herşey aşağıdaki fotoğrafta görülen taslak çizimlerle başladı.

WP_20140723_002

Sonrasında ise işte bu görsel video ortaya çıktı.

Bir başka görsel dersimizde görüşünceye dek hepinize mutlu günler dilerim.

Asp.Net’ ten HTTPS Tabanlı WCF Çağrısı Gerçekleştirmek

$
0
0

381eed443562d941546485cc9e2decf4_1316198153Merhaba Arkadaşlar,

Özel Ajan Oso! Son yılımda Disney Channel’ de sıklıkla maruz kaldığım bir çizgi karakter. Aslında bu sakar ve bir o kadar da maharetli ve sevimli çizgi dizi kahramının görevi son derece basit. Sadece 3 adımda çocuklara yol gösterici nitelikte yardımcı olmaya çalışmak.

Ajanımızın dizi de bir de yöneticisi var. Aynı Mission Impossible’ da olduğu gibi. Onun adı “Bay Dost”

Her bölüm Özel Ajan Oso’ ya bir görev veriliyor. Söz gelimi çocuklardan birisi yanlışlıkla kumbarasının bir ayağını kırmış olsun. Bunu nasıl tamir eder? 3 adımda. Dizi de olay şöyle ilerler.

Adım 1 : Kırılan kısma tutkal sür

Adım 2 : Kumbara ile kırılan kısmı birleştir

Adım 3 : 7 saniye boyunca birleşik şekilde tut

gibi

Bazen biz geliştiriciler de bir işi icra edebilmek adına bu şekilde basit ve az sayıda adıma ihtiyaç duyarız. İşte bugünkü makalemizde kendimizi Özel Ajan Oso yerine koyacak ve şu senaryoyu gerçekleştirmeye çalışıyor olacağız.

Adım 0 : Senaryo

Development ortamında geliştirme yapmaktayız. IISüzerinde host edilen bir WCF Servis uygulamamız var. Bu servis uygulaması WSSecurity standartlarında ve SSL tabanlı bir hizmet sunmakta. Bir başka deyişle servise httpsüzerinden talep gönderebiliyoruz. Servisin WSDL içeriğinin elde edilebildiği adres de aslında HTTPS tabanlı.

İstemci tarafı ise Asp.Net tipinden bir web uygulaması. Bu uygulamanın söz konusu servise HTTPS tabanlı olarak talep gönderebilmesi ve cevap alabilmesi isteniyor. Development ortamında çalışıldığından gerçek bir sertifika yerine, Microsoft’ un test amaçlı X509 sertifikasının kullanılması planlanıyor.

O halde ne duruyoruz! Haydi bilgisayar başına.

Adım 1 : WCF Servis Uygulamasını Oluştur

Öncelikli olarak bir WCF Service Application projesi oluşturarak işe başlayabiliriz. Test amaçlı kodlar içerecek olan servisin dahilindeki tiplerden ziyade konfigurasyon tarafında yapılan ayarlar senaryomuz gereği çok daha önemli. İlk olarak sınıf diagramımıza bakalım ve bizim için gerekli tipleri üretelim.

wcfhttps_8

Servis sözleşmesi;

using System.ServiceModel;

namespace EmployeeService
{
   [ServiceContract]
    public interface IEntryService
    {
        [OperationContract]
       Employee InsertEmployee(Employee newEmployee);
    }
}

Bir Employee tipinin sembolik olarak üretimi ve doldurulan Id değeri ile geri döndürülmesini üstlenen servis operasyonu söz konusudur.

Sözleşme uygulayıcısı;

using System;

namespace EmployeeService
{
    public class EntryService
       : IEntryService
    {
        public Employee InsertEmployee(Employee newEmployee)
        {
            newEmployee.EmployeeId = 1903;
            return newEmployee;
        }
    }
}

Employee tipi

namespace EmployeeService
{
    public class Employee
    {
        public int EmployeeId { get; set; }
        public string Title { get; set; }
        public string FirstName { get; set; }
        public string MiddleName { get; set; }
        public string LastName { get; set; }
        public int Level { get; set; }
    }
}

ve en önemli kısım, servis tarafındaki konfigurasyon içeriği Winking smile

<?xml version="1.0"?>
<configuration>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="TransportSecurity">
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="EntryServiceBehavior">
          <serviceMetadata httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service
        name="EmployeeService.EntryService"
        behaviorConfiguration="EntryServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="https://localhost/EmployeeService/"/>
          </baseAddresses>
        </host>
        <endpoint
          address="EntryService.svc"
          binding="wsHttpBinding"
          bindingConfiguration="TransportSecurity"
          contract="EmployeeService.IEntryService"
        />
        <endpoint
          address="mex"
          binding="mexHttpsBinding"
          contract="IMetadataExchange"
      />
      </service>
    </services>
  </system.serviceModel>
  <system.web>
    <compilation debug="true"/>
  </system.web>
</configuration>

Senaryomuza göre WCF servis uygulamamız https tabanlı olarak hizmet veriyor olacak. Bir başka deyişle WS-IHTTP standartlarını kullanan ve transport seviyesinde güvenlik sunan bir servis çalışması söz konusu. Bu nedenle wsHttpBinding tipinin kullanılması, servisin iletişim sırasında kullanacağı güvenlik çeşidinin transport olarak belirlenmesi gerekiyor. Ayrıca servisin metadata bilgisinin de httpsüzerinden elde edilmesini istediğimizden, mexHttpsBinding bağlayıcı tipinin kullanılması ve davranışsal özelliklerde httpsGetEnabled niteliğinin true olarak set edilmesi gerekiyor.

Bu arada servis uygulamasının IIS üzerinde host edilecek şekilde oluşturulduğunu ifade edelim. İlk etapta base adresin https olarak belirtilmesine rağmen IIS tarafındaki Virtual Directory’ nin HTTP tabanlı çalıştığını düşünüyoruz. Bu haliyle servisi çalıştırmaya kalkarsak, sertifikasyon ile ilişkili hatalar almamız muhtemeldir. Dolayısıyla çözüm olarak bir test sertfikası üretecek, web uygulamasının http için oluşturulan Virtual Directory’ sini IIS Manager yardımıyla ele alacak ve SSL kullanımını etkinleştireceğiz.

Adım 2 : Makecert ile Test Sertifikasının Oluşturulması

Windows SDK ile birlikte gelen makecert aracını kullanarak X509 tabanlı test sertifikalarının oluşturulması mümkündür. Özellikle development süreçlerinde bu aracı kullanarak SSL tabanlı senaryoların ele alınması son derece kolaydır. Senaryomuz için örnek bir test sertifikasını aşağıdaki ekran görüntüsünde olduğu gibi basitçe üretebiliriz.

makecert –r –pe –n “CN=makineadı" –b 01/01/2000 –e 01/01/2100 –eku 1.3.6.1.5.5.7.3.1 –ss my –sr localmachine –sky exchange –sp “Microsoft RSA SChannel Cryptographic Provider” –sy 12

wcfhttps_1

Adım 3 : IIS Tarafında SSL Kullanımını Etkinleştirmek

Https tabanlı iletişimi IISüzerinde oluşturulan web sayfalarında uygulayabilmemiz için öncelikli olarak site seviyesinde SSL kullanımının etkinleştirilmiş olması gerekmektedir. Bunun için https tipinin Bindings olarak ilave edilmesi yeterli olacaktır. SSL Settings-> Bindings –> Add kısmından aşağıdaki ekran görüntüsünde yer alan adımları takip ederek işlemlerimize devam edelim.

wcfhttps_3

https tipinin eklenmesi sırasında bir de SSL sertfikası sorulacaktır. Bu sertifikaların seçimi sırasında 2nci adımda üretilen sertifikanın da listelendiğini görebiliriz.

wcfhttps_5

Adım 4 : Publish Edilmiş Virtual Directory için SSL Etkinleştirilmesi

Artık http adresi temelli oluşturulan VirtualDirectory için SSL kullanımını etkinleştirebiliriz. Bunun için WCF uygulamasının SSLSettings kısmında aşağıdaki işaretlemeleri yapmamız yeterli olacaktır. Require SSL ve istemci tarafı sertifika için Accept.

wcfhttps_4

Özellikle Require SSL seçeneğinin aktif olabilmesi bir önceki adımlarda yaptığımız IIS tabanlı SSL etkinleştirilmesine bağlıdır.

Örnekte kullandığımız IIS(Internet Information Services) versiyonu 7.5 dir uygulama Windows 7 Enterprise işletim sistemi üzerinde geliştirilmektedir.

Adım 5 : WCF Servis Uygulamasında HTTPS Adres Bilgisinin Kullanılması

Önceden de belirttiğimiz üzere eğer IIS tarafında SSL tabanlı bir sertifikasyon söz konusu değilse WCF uygulamasının da https tabanlı bir proje adresini kullanabilmesi söz konusu değildir. Ancak önceki adımlar ile bu sorunu aşmış bulunuyoruz. Dolayısıyla aşağıdaki ekran görüntüsündeki gibi Project Url kısmında https protokolünü kullanacağımızı belirtebiliriz.

wcfhttps_2

Adım 5.5 : Test

Ara adımda EntryService.svc sayfasını bir tarayıcı üzerinden açmayı deneyebiliriz. Bu durumda aşağıdaki ekran görüntüsüne ulaşmamız gerekmektedir. Dikkat edileceği üzere https tabanlı bir açılış söz konusu olmuştur.

wcfhttps_6

Ayrıca WSDL(Web Service Description Language) erişim adreslerinin de https tabanlı olduğu rahatlıkla gözlemlenebilir Winking smile

Adım 6 : İstemci Uygulamaya Servis Referanasının Eklenmesi

Senaryomuza göre istemci uygulamamız ASP.Net tabanlı bir web uygulamasıdır. Visual Studio ile bir Asp.Net Empty Web Application oluşturarak bu adıma başlayabiliriz. Bundan sonraki en önemli kısım ise servis referansının projeye dahil edilmesidir. AddServiceReference kısmında https tabanlı WSDL adresine talepte bulunursak, aşağıdaki ekran görüntüsünde yer alan uyarı mesajı ile karşılaşırız.

wcfhttps_7

Bu iletişim penceresinde Yes seçeneğini işaretleyerek ilerleyelim. Sonuç olarak servis tarafına ait proxy tipinin istemci tarafında üretildiğini görürüz. Client için söz konusu olan konfigurasyon içeriği ise aşağıdaki gibi olacaktır.

<?xml version="1.0"?>
<configuration>
    <system.web>
      <compilation debug="true" targetFramework="4.5" />
      <httpRuntime targetFramework="4.5" />
    </system.web>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IEntryService">
                    <security mode="Transport">
                        <transport clientCredentialType="None" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint
                address=https://domainName.com.tr/EmployeeService/EntryService.svc/EntryService.svc
                binding="wsHttpBinding"
                bindingConfiguration="WSHttpBinding_IEntryService"
                contract="EmployeeRef.IEntryService"
                name="WSHttpBinding_IEntryService"
                />
        </client>
    </system.serviceModel>
</configuration>

Dikkat edileceği üzere wsHttpBinding tipine ait güvenlik modu Transport olarak gelmiştir. Ayrıca sunucu tarafında belirttiğimiz gibi clientCredentialType niteliği None olarak atanmıştır.

Adım 7 : Test Sayfasının Oluşturulması ve Kodlanması

Bu amaçla aşağıdaki basit Web formunu hazırladığımızı düşünelim.

wcfhttps_10

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="NewEmployee.aspx.cs" Inherits="ClientApp.NewEmployee" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <table>
        <tr>
            <td>
                Title :
            </td>
            <td>
                <asp:TextBox ID="txtTitle" runat="server" />
            </td>
        </tr>
                <tr>
            <td>
                First Name :
            </td>
            <td>
                <asp:TextBox ID="txtFirstName" runat="server" />
            </td>
        </tr>
                <tr>
            <td>
                Middle Name :
            </td>
            <td>
                <asp:TextBox ID="txtMiddleName" runat="server" />
            </td>
        </tr>
                <tr>
            <td>
                Last Name :
            </td>
            <td>
                <asp:TextBox ID="txtLastName" runat="server" />
            </td>
        </tr>
                <tr>
            <td>
                Level :
            </td>
            <td>
                <asp:TextBox ID="txtLevel" runat="server" />
            </td>
        </tr>
                <tr>
            <td>
               
            </td>
            <td>
                <asp:Button ID="btnSend" runat="server" Text="Insert new Employee" OnClick="btnSend_Click" />
            </td>
        </tr>
                <tr>
            <td colspan="2">
                <asp:Label ID="lblResult" runat="server" />
            </td>
        </tr>
    </table>
    </div>
    </form>
</body>
</html>

Web sayfasından Employee tipine ait Title, FirstName, MiddleName, LastName ve Level bilgileri alınmaktadır. Bu bilgiler aşağıdaki kod parçası ile de sunucuya gönderilmekte ve sonuç olarak sunucu tarafından üretilen EmployeeId değeri Label bileşenine basılmaktadır.

using ClientApp.EmployeeRef;
using System;
using System.Net;
using System.Net.Security;

using System.Security.Cryptography.X509Certificates;

namespace ClientApp
{
    public partial class NewEmployee
        : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
           
        }

        protected void btnSend_Click(object sender, EventArgs e)
        {
            ServicePointManager.ServerCertificateValidationCallback =
                new RemoteCertificateValidationCallback(IgnoreCertificationError);

            EntryServiceClient proxy = new EntryServiceClient("WSHttpBinding_IEntryService");
            Employee newEmployee=proxy.InsertEmployee(new Employee
            {
                FirstName = txtFirstName.Text,
                MiddleName = txtMiddleName.Text,
                LastName = txtLastName.Text,
                Level = Convert.ToInt32(txtLevel.Text),
                Title = txtTitle.Text
            }
            );
            lblResult.Text = newEmployee.EmployeeId.ToString();
            proxy.Close();
        }

        public static bool IgnoreCertificationError(object sender,
      X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }
    }
}

Kodun belki de en önemli kısmı ServerCertificateValidationCallback tipinin kullanılması ve IgnoreCertificationError isimli metod içerisinden daima true değeri döndürülmesidir. Bunun sebebi aslında bir test sertifikası kullanmamız ve söz konusu sertifikanın validasyona tabi tutulması halinde çalışma zamanı hatası alacak olmamızdır.

wcfhttps_11

Bir başka deyişle oluşacak olan hata sürkülase edilmiştir. Malum development ortamında geliştirme yaptığımızdan bu tip görmezden gelmeleri çözümümüze katabiliriz. Birazcık hile yaptık anlayacağınız.

Artık uygulamayı çalıştırıp test edebiliriz. Eğer adımlarımızda bir sorun yoksa aşağıdaki ekran görüntüsünde olduğu gibi 1903 sonucunu alıyor olmamız gerekmektedir.

wcfhttps_9

WCF tarafında HTTPS tabanlı geliştirme ortamının hazırlanması daha önceki yıllarda biraz daha zorluydu. Ancak yeni nesil ortamlarımızda bu işlemleri gerçekleştirmek, adımlarımızda da görüldüğü üzere daha kolay. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

HowTo_WCFandHTTPS.zip (63,74 kb)

Data Binding İşlemlerinde Converter Kullanımı

$
0
0

Fuel levelMerhaba Arkadaşlar,

Daha önceki yazılarımızdan birisinde(Data-Binding Retro Bakış Açısı)özellikle WPF(Windows Presentation Foundation), Windows Phone, WF(Workflow Foundation) gibi XAML tabanlı ara birimlerin sıklıkla kullanıldığı noktalarda veri bağlama(Data Binding) işlemlerinin temellerini kavramaya çalışmış ve çok basit bir örnek ile konuyu irdelemiştik.

Bu yazımızda ise, veri bağlama işlemleri sırasında dönüştürücü tiplerden(Converters) nasıl yararlanabileceğimizi incelemeye çalışacağız. Bu güzel kabiliyet sayesinde aslında var olan çalışma zamanı veri bağlama işlemlerine müdahale edebilmekteyiz ki bu, geliştirici açısından oldukça önem arz eden bir konudur. Öyleki, geliştiricinin standart basma kalıpların dışına çıkarak hareket edebilmesine olanak sağlamakta.

Standart olarak kullanılan veri bağlama tekniklerinde bilindiği üzere kontrolün bir özelliğinin, bağlanılan veri tipinin bir özelliğine eşleştirilmesi işlemi söz konusudur ve bu noktada genellikle içeriğin string tipli olarak ele alındığına şahit oluruz. Bir başka deyişle veriyi göstermek amacıyla geliştirdiğimiz senaryolarda ağırlıklı olarak Context, Text gibi nitelikler veri sunumu için kullanılmaktadır.

Ancak bazı senaryolarda(ki edindiğim tecrübelere göre özellikle WF tarafında) gelen veri tipinin string tabanlı bir kontrol niteliği yerine farklı tipten olan bir kontrol niteliğine bağlanması istenebilir. Örneğin bir kontrolün visibility niteliğinin gelen verinin durumuna göre etkinleştirilmesi veya arka plan renginin veriye göre farklılaştırılması vb. İşte bu gibi gereksinimlerde, Converter tipler devreye girerek, bağlanan veri değerinin, kontrol niteliğinin istediği asıl tipe dönüştürülmesinde rol oynamaktadır.

Dilerseniz konuyu biraz daha iyi kavrayabilmek adına basit bir senaryo üzerinden ilerlemeye çalışalım. WPF tabanlı olarak geliştireceğimiz örnek uygulamamızda ilk etapta aşağıdaki gibi bir POCO(Plain Old CLR Objects) tipinin söz konusu olduğunu düşünelim.

dbcvrtr_1

namespace UsingConverters
{
    public class Vehicle
    {
        public int VehicleId { get; set; }
        public string Name { get; set; }
        public int FuelLevel { get; set; }       
    }
}

Örnekte Converter tiplerinin kullanımını ele almak istediğimizden basitlik adına söz konusu tipe INotifyPropertyChanged arayüzü(Interface) uygulanmamıştır.

Vehicle sınıfı içerisinde int tipinden VehicleId, FuelLevel ve string tipinden Nameözellikleri(Property) bulunmakta. Bu sınıfa ait nesne örneklerinden oluşan bir koleksiyona ait verilerin ise, aşağıdaki XAML içeriğine sahip WPF penceresinde gösterilmek istendiğini düşünelim.

<Window x:Class="UsingConverters.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="600" Loaded="Window_Loaded">
    <Grid>
        <ListBox x:Name="lstVehicles"  ItemsSource="{Binding}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical">
                        <Label 
                               Content="{Binding Path=VehicleId}"
                               FontWeight="Bold" FontSize="16"/>
                        <Label  
                               Content="{Binding Path=Name}"/>
                        <Label Height="24"
                               HorizontalAlignment="Left"
                               Background="CadetBlue"
                               Width="{Binding Path=FuelLevel}"                            
                               Content="{Binding Path=FuelLevel}"
                               />
                        <Separator Width="500"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

ListBox bileşeni için standart bir Data Binding işlemi gerçekleştirilmektedir. Bu nedenle ItemsSourceözelliği {Binding} olarak işaretlenmiştir. DataTemplate bileşenine baktığımızda ise VehicleId, Name ve FuelLevelözellikleri için çeşitli kontrol niteliklerine(Control Attribute) atamalar yapıldığı görülmektedir. Örneğin aracın adı Label kontrolünün Content niteliğine bağlanmıştır. Benzer durum VehicleId ve FuelLevelözellikleri için de geçerlidir.

FuelLevelözellik değerinin bağlanmasında ise iki niteliğe atama yapıldığı görülmektedir. Bunlardan birisi Content niteliğidir ve aslında benzin değerinin sayısal karşılığını göstermektedir. Diğer yandan Width niteliği de bu sayısal değere bağlanmış ve benzin miktarının görsel olarak boyutu değişen bir yatay bar şeklinde ifade edilmesi sağlanmıştır.

Örnek veriyi doldurabilmek için Window kontrolünün Loaded olay metodunda gerekli bazı düzenlemeler yapılmıştır. Bu kod içeriği aşağıdaki gibidir.

using System.Collections.Generic;
using System.Windows;

namespace UsingConverters
{
    public partial class MainWindow
        : Window
    {
        List<Vehicle> vehicles = null;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            vehicles = new List<Vehicle>
           {
                new Vehicle{ VehicleId=1, Name="Su Todoroki", FuelLevel=75},
                new Vehicle{ VehicleId=2, Name="Migel Kamino", FuelLevel=50},
                new Vehicle{ VehicleId=3, Name="Francesco Bernulli", FuelLevel=45},
                new Vehicle{ VehicleId=4, Name="Meytır", FuelLevel=60},
                new Vehicle{ VehicleId=5, Name="Naycıl", FuelLevel=90},
                new Vehicle{ VehicleId=6, Name="Şimşek", FuelLevel=23},
                new Vehicle{ VehicleId=7, Name="Şolet", FuelLevel=85}
            };

            lstVehicles.DataContext = vehicles;
        }
    }
}

vehicles isimli List<T> koleksiyonu bir kaç Vehicle nesne örneğine sahiptir ve Window_Loaded olay metodu içerisinde bu koleksiyon içeriği DataContextözelliğine set edilmektedir. Uygulamayı bu haliyle çalıştırdığımızda aşağıdak ekran görüntüsündekine benzer bir sonuç ile karşılaşırız.

dbcvrtr_2

Aslında pek de fena bir görüntü değil? En azından benim açımdam. Yine de daha iyisi yapılabilir. Örneğin benzin seviylerini göz önüne alalım. Kutuların uzunluklarına bakıldığında araçların benzin oranlarını görsel olarak daha iyi anlayabiliyoruz. Üstelik içerisinde yazan sayısal değerlerde bize iyi bir istatistik sunmakta.

Peki ya bu yatay bara benzer kontrollerin renklerini benzin değerlerine göre değiştirmek istesek. Söz gelimi yakıt miktarı 25 birimin altına düştüğünde rengi kırmızı olsa veya 75 ile 100 birim arasında iken elverişli anlamına gelebilecek Yeşil renkte olsa.

Bunun için Label kontrolünün Backgroundözelliğine uygun bir değeri vermemiz yeterli olacaktır. Ancak bu değeri verirken benzin miktarına göre uygun rengin seçilmesi ve atanması gerekmektedir. Bir başka deyişle bir aracının devreye girmesi ve sayısal olarak tutulan FuelLevel değerini, görsel kontrolün niteliğinin istediği tipe dönüştürmesi işlemi icra edilmelidir. Normal şartlar da örneğin bir Label kontrolünün arka plan rengini değiştirmek istediğimizde, kod tarafında aşağıdaki tarzda bir yaklaşımı uygularız.

lbl.Background = new SolidColorBrush(Colors.Red);

Bu kod ifadesinde görüldüğü üzere Background niteliği bir SolidColorBrush ile zenginleştirilmiş ve kırmızı renkte belirlenmiştir. Converter tipi bu tarz bir yaklaşımı uygulamak durumundadır. Yani sayısal tipin aslında arka planın istediği bir Brush türevine dönüştürülmesi gerekmektedir. Şimdi aşağıdaki Converter tipini projeye ekleyereki işlemlerimize devam edelim.

dbcvrtr_3

ve kod içeriği,

using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;

namespace UsingConverters
{
    public class FuelLevelToSolidColorConverter
        :IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            int fuelLevel = (int)value;
            SolidColorBrush brush = null;

            if (fuelLevel <= 25)
                brush = new SolidColorBrush(Colors.Red);
            else if (fuelLevel > 25
                && fuelLevel <= 50)
                brush = new SolidColorBrush(Colors.Orange);
            else if (fuelLevel > 50
                && fuelLevel <= 75)
                brush = new SolidColorBrush(Colors.LightBlue);
            else if (fuelLevel > 75
                && fuelLevel <= 100)
                brush = new SolidColorBrush(Colors.DarkGreen);
            else
                brush = new SolidColorBrush(Colors.White);

            return brush;
        }
       
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }
    }
}

IValueConverter arayüzünü implemente etmiş olduğumuz FuelLevelToSolidColorConverter sınıfı iki metodu ezmek zorundadır. Bunlardan birisi veriden kontrole doğru olan yönde devreye giren Convert fonksiyonudur. Söz konusu örnekte bu fonksiyon daha ön plandadır. Nitekim sayısal int tipinden olan FuelLevelözelliğinin çalışma zamanındaki değerinin, uygun olan bir SolidColorBrush tipine dönüştürülmesi sırasında devreye girmektedir.

.Net Framework içerisinde yer alan pek çok ve sayısız arayüz(Interface) sayesinde, var olan davranışları değiştirmek, ortamı genişletmek ve çalışma zamanına ekstra kabiliyetler kazandırmak pekala mümkündür. IValueConverter ve IPropertyNotifyChanged gibi arayüzler bunlardan sadece ikisidir.

Convert metodu devreye girdiğinde metoda gelen değer object tipinden olan value isimli parametre ile yakalanmaktadır. Bu değer Convert metodu içerisinde ele alındıktan sonra uygun bir SolidColorBrushüretilmiş ve geriye döndürülmüştür. Convert metodunun dönüş tipi yine object’ tir.

ConvertBack metodu ise tahmin edileceği üzere tam ters yönde çalışmaktadır. Yani kontrol içerisindeki ilgili nitelik değerinin, bağlanan veri içeriğine dönüştürülmesi noktasında rol oynamaktadır. Genel olarak çok fazla başvurulan bir metod değildir ve örneğimizde de aslında bir etkinliği bulunmamaktadır. Bu sebepten metoda value ismiyle gelen kontrol değişkeni doğrudan object tipinden geriye döndürülmektedir.

Tabi bazı kritik senaryolarda ConvertBack metodunun içeriğinin de yazılması ve kontrol içeriğinin ilgili değerinin bir dönüşüm işlemine tabi tutularak veri kaynağına gönderilmesi söz konusu olabilir. Bu, özellikle Workflow Foundation tarafında tasarlanan Custom Designer kontrolleri için söz konusudur.

Nitekim WF tarafında geliştirilen bu tip kontrollerde, Workflow Designer’ ın üzerinde yapılan kontrol bazlı değişikliklerin arka plandaki bazı tiplerin özelliklerine yansıtılması da gerekebilir.

Kod tarafında gerekli düzenlemeleri yaptıktan sonra artık yeni Converter tipini arayüz tarafında kullanabiliriz. Bunun için WindowXAML içeriğini aşağıdaki gibi modifiye etmemiz yeterli olacaktır.

<Window x:Class="UsingConverters.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="600" Loaded="Window_Loaded"
        xmlns:local="clr-namespace:UsingConverters"
        >
    <Window.Resources>
        <local:FuelLevelToSolidColorConverter x:Key="FuelLevelToSolidColor"/>
    </Window.Resources>
    <Grid>     

        <ListBox x:Name="lstVehicles"  ItemsSource="{Binding}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical">
                        <Label
                               Content="{Binding Path=VehicleId}"
                               FontWeight="Bold" FontSize="16"/>
                        <Label
                               Content="{Binding Path=Name}"/>
                        <Label Height="24"
                               HorizontalAlignment="Left"
                               Background="{Binding Path=FuelLevel, Converter={StaticResource FuelLevelToSolidColor}}"
                               Width="{Binding Path=FuelLevel}"                            
                               Content="{Binding Path=FuelLevel}"
                               />
                        <Separator Width="500"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

Dikkat edilmesi gereken ilk nokta FuelLevelToSolidColorConverter tipinin XAML içeriğinde bir Resource olarak belirtilmiş olmasıdır. localön eki ile başlayan takıya dikkat edildiğinde, bu Resource’ un ilerleyen elementlerde de kullanılabilmesini sağlamak amacıyla bir de Key değeri verildiği görülmektedir. local takma adı ise Converter tipinin bulunduğu isim alanını(Namespace) işaret etmektedir.

Resource, Window elementine bağlandığından, Window içerisindeki her alt element tarafından kullanılabilir.

Converter tipinin devreye alındığı yer ise Label kontrolünün Backgroundözelliğidir.

{Binding Path=FuelLevel, Converter={StaticResource FuelLevelToSolidColor}}

kullanılan ifadede yer alan Converterözelliğine atanan değer ile, ilgili Label kontrolünün Backgroundözelliğine yapılan veri bağlama operasyonlarında, FuelLevelToSolidColor takma adı ile belirtilen Resource’ un işaret ettiği IValueConverter türevinin devreye gireceği ifade edilmektedir.

Buraya kadar anlatıklarımızdan yola çıkarsak Converter tipinin ve ilgili fonksiyonlarının çalışma şekli aşağıdaki şekilde görüldüğü gibidir.

dbcvrtr_5

.Net Framework tarafında IValueConverter arayüzünü uygulayan Built-In converter tipler de bulunmaktadır. Visual Studio arabirimindeki Object Browser yardımıyla bu tipler incelenebilir.

dbcvrtr_6 

Yapılan son değişikliklere göre uygulamanın yeni çalışma zamanı çıktısına ait sonuçlar şu şekilde olacaktır.

dbcvrtr_4

Sanırım Şimşeğin en kısa sürede pit alanına girmesi ve benzin alması grerekiyor. Gaza fena yüklenmiş belli ki.

Görüldüğü üzere söz konusu kontroller yakıt seviyesine göre arka plan renklerini değiştirmiş ve görsel açıdan kullanıcı deneyimi biraz daha iyi olan bir sonuç ortaya çıkmıştır. Siz de farklı senaryolarda farklı Converter tiplerini geliştirmeyi deneyerek antrenmanlar yapabilirsiniz. IValueConverter arayüzü sayesinde epey bir esnekliğimiz olduğunu fark etmişsinizdir. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

UsingConverters.zip (65,26 kb)

TFİ 109 - IIS Üzerindeki Uygulamaları Kod Yoluyla Öğrenmek

$
0
0

Merhaba Arkadaşlar,

Diyelim ki sunucudaki IIS üzerinde konuşlandırdığınız Web uygulamalarının bir listesini almak istiyorsunuz. Bunun elbette pek çok yolu olduğunu biliyorsunuz. Bir Powershell script' i belki de işinizi görür. Ancak belki de siz bunu kendi geliştireceğiniz windows forms uygulamasında bu listeyi kullanmak istiyorsunuz. Ne yaparsınız? Kod yardımıyla IIS üzerindeki Application' ları, Site' ları öğrenebilir misiniz?

Aslında hep elinizin altında olan (Windows\System32\inetsrv\Microsoft.Web.Administration.dll) ve hatta isterseniz NuGet Package Manager ile de indirebileceğiniz Microsoft.Web.Administration kütüphanesini kullanarak bu işi gerçekleştirmeniz oldukça kolay. Nasıl mı? İşte böyle.

Başka neler mi yapabilirsiniz? Örneğin bir Application Pool' u Recylce edebilirsiniz. Ya da bir Web Site' ı Stop-Start. Hatta yeni bir Web Site bile açabilirsiniz. Araştırmaya değer değil mi?

Başka bir ipucunda görüşmek dileğiyle.

WCF WebHttp Service, JSON, jQuery, Ajax ve CORS ile Yeni Bir Macera

$
0
0

browser_warsMerhaba Arkadaşlar,

Bir süredir şirket içerisinde kullanılacak olan web tabanlı bir .Net uygulamasının geliştirilmesinde görev almaktayım. Uygulama, yürütülen süreç gereği her iterasyon sonunda yeni özellikler eklenmiş ve hataları giderilmiş biçimde Üretim(Production) ortamına taşınmakta.

Projede kaynak sıkıntısı nedeniyle uzun bir süre servis katmanı haricinde kalan arayüz tarafı ile de ilgilenmek zorunda kaldım. Arayüz tarafı ile uğraşırken iş biriminden gelen isteklere göre CSS(Cascading Style Sheets) ve bol miktarda Javascript kodlamak benim gibi acemiler için epeyce zorlayıcıydı. Lakin en çok zaman kaybettiğim vaka, şirket içinde kullanılmakta olan eski,yeni ve çeşitli tipteki tarayıcıların uyumlu çalışmasının sağlanabilmesiydi. Kimi lokasyonda Internet Explorer 8,kimi yerlede Google Chrome’ un en güncel sürümü bulunmakta. Hatta global çevrimde Firefox standart olarak her bilgisasyarda yüklü geliyor.

Şunu fark ettim ki, tarayıcı savaşları makalelerde okuduğumuzdan çok daha ciddi boyutta. Havada uçuşan standartları farklı farklı yorumlama biçimleri nedeniyle her tarayıcıya uygun standart çözümler üretmek gerçekten zormuş. En azından benim için Smile Basit bir CSS' in Internet Explorer' da sorunsuz çalışırken Chrome' da problem çıkarttığına, Chrome' da dertsiz işleyen bir Ajax Control Toolkit kontrolünün, Firefox’ un eski bir sürümünde hiç çalışmadığına şahit oldum. Hal böyle olunca çalışma zamanında, tarayıcıların debug kabiliyetleri ile de haşır neşir olmak zorunda kaldım. Sıkıcı mıydı? Hayır Winking smile Aksine benim için farklı ve değer katan deneyimlerdi. İşte bu düşünceler geçtiğimiz günlerde yine internet üzerinde bir şeyler araştırıp öğrenmeye çalışırken kendimi farklı bir macera içerisinde buldum. Sonunda bunu kaleme almanın yararlı olacağını düşündüm ve işte buradayım.

Senaryo

Bu yazımızda bir kavram ve terim cümbüşü içerisinde yer alacağımızı söyleyebilirim. Yazacağımız basit bir WCF servisini öncelikle REST tabanlı çalışır hale getireceğiz. Ardından söz konusu servise jQuery kütüphanesinden yararlanarak bir Ajaxçağrısı gerçekleştireceğiz. Temel hedefimiz ise HTTPPost metoduna göre bir içeriği tarayıcı üzerinden servise göndermek olacak. Lakin JSON(JavaScript Object Notation) tipinden bir nesne kullanacağız. Kabaca aşağıdaki çizelge de görülen durumun söz konusu olduğunu söyleyebiliriz.

restJquery_7

Bu toplu senaryo aslına bakılırsa günümüzün popüler pek çok web tabanlı uygulamasında kullanılabilecek türden. Haydi gelin parmaklarımızı sıvayalım...

Servis Tarafının Geliştirilmesi

İlk olarak aşağıdaki servis sözleşmesini içeren bir WCF Service Application projesi açarak yola çıkabiliriz. Söz konusu projede IProductService isimli bir sözleşme(Service Contract) yer alacak.

using System.ServiceModel;
using System.ServiceModel.Web;

namespace AzonServices
{
    [ServiceContract]
    public interface IProductService
    {
        [OperationContract]
        [WebInvoke(Method = "POST",
                   RequestFormat = WebMessageFormat.Json,
                   ResponseFormat = WebMessageFormat.Json,
                   UriTemplate = "AddProduct")]
        string PostProduct(Product NewProduct);
    }
}

Senaryomuzda sadece HTTPPost metodunu ele almak istediğimizden basit bir operasyon söz konusu. Önemli olan servis operasyonunun WebInvoke niteliği(attribute) ile işaretlenmiş olmasıdır. WebInvoke niteliği bu operasyonun HTTP tabanlı taleplere cevap verecek şekilde kullanılabileceğini ifade etmektedir.

Niteliğin içerisinde dikkat edileceği üzere bir kaç özelliğin set edildiği görülmektedir. Methodözelliğine atanan değer ile, operasyonun HTTPPost taleplerine cevap vereceği belirtilmektedir. RequestFormat ve ResponseFormatözellikleri ile operasyona gelen ve istemcilere cevap olarak dönen içeriklerin JSON formatında serileştirileceği ifade edilir. Son olarak bir Urişablonu atanmıştır. UriTemplate' e atanan AddProduct ifadesi, istemci tarafının göndereceği HTTP Post talebinde kullanılacaktır.

Servis metodu Product tipinden bir nesne örneğini alıp geriye string tipte içerik döndürecek şekilde tasarlanmıştır. Product tipi oldukça basit bir içeriğe sahiptir.

namespace AzonServices
{
    public class Product
    {
        public int ProductId { get; set; }
        public string Title { get; set; }
        public decimal ListPrice { get; set; }
    }
}

Gelelim ProductService.svcöğesinin kodlarına.

using System;
using System.Collections.Generic;

namespace AzonServices
{
    public class ProductService
        : IProductService
    {
        List<Product> productList = new List<Product>();

        public string PostProduct(Product NewProduct)
        {
            productList.Add(NewProduct);
            return Guid.NewGuid().ToString();
        }
    }
}

ProductService.svc içerisinde çok özel bir kod parçası yoktur. Sadece generic bir List<Product>örneğine, PostProduct metoduna gelen Productörneğinin eklenmesi işlemi icra edilmektedir. Test sırasında istemcinin doğru bir cevap aldığını kolayca tespit etmek adına metod geriye benzersiz bir Guid değeri döndürmektedir.

EndPoint Bildirimi

Servis tarafı için önem arz eden konulardan birisi de EndPoint tanımlamasıdır. Servis, REST tabanlı olacak şekilde çalışabilmelidir. WCF bu noktada WebHttpBinding isimli Binding tipini sağlamaktadır. Bu sebepten web.config içerisinde gerekli tanımlamaların yapılması gerekmektedir. Aynen aşağıda görüldüğü gibi.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <services>
            <service name="AzonServices.ProductService">
                <endpoint address=""
                               binding="webHttpBinding"
                               contract="AzonServices.IProductService"behaviorConfiguration="webBehavior"></endpoint>
            </service>
        </services>
        <behaviors>
            <endpointBehaviors>
                <behavior name="webBehavior">
                    <webHttp/>
                </behavior>
            </endpointBehaviors>
        </behaviors>
        <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
    </system.serviceModel>
</configuration>

endpoint elementi içerisinde yer alan binding niteliğine webHttpBinding atanması haricinde bir de HTTP davranışının verilmesi söz konusudur. Bunun için dikkat edileceği üzere bir endPointBehavior tanımlaması yapılmış ve webHttp değeri eklenmiştir. Eğer bir problem yoksa ProductService.svc dosyasının tarayıcı üzerinde aşağıdaki gibi açılması gerekir.

restJquery_1

Servisin Metadata Publishing özelliği kapalıdır. Bilindiği üzere REST tabanlı servislere HTTP protokolü ve metodları ile erişilmektedir. Bu yüzden istemci tarafında bir Proxy nesnesi kullanılmasına gerek yoktur.

İstemci Tarafı

Gelelim istemci uygulamanın geliştirilmesi. Servis tüketicisi bir Web uygulaması olarak inşa edilecektir. Detayları bir kenara bırakıp asıl konuya odaklanmak istediğimizden Asp.Net Empty Web Application projesi bizim için biçilmiş kaftandır. Web uygulamamızda jQuery kullanacağımızdan en azından ilgili javascript kütüphanesinin eklenmesi gerekir.

Bunun için http://jquery.com/download/ adresine giderek istediğiniz bir sürümü seçebilirsiniz. Sürüm seçiminde bu sayfada yazılan notlara dikkat etmenizi öneririm. Eğer kurumunuzun tarayıcılar ile ilişkili bazı kuralları varsa ve özellikle eski tarayıcılar ile çalışıyorlarsa uygun jQuery kütüphanesinin seçilmesi doğru olacaktır.

Ben örnek projemizde jQuery-2.1.0.min.js sürümünü kullanmayı tercih ettim. İlgili Script dosyasını projeye ekledikten sonra Default.aspx sayfasını aşağıdaki gibi geliştirebiliriz.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ClientApp.Default" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>REST Service Test</title>
    <meta http-equiv="X-UA-Compatible" content="IE=10" />
</head>
<body>
    <script type="text/javascript" src="Scripts/jquery-2.1.0.min.js">

    </script>
    <script type="text/javascript">
        function AddNewProduct() {

            var product = {
                "ProductId": 1220,
                "Title": "ElCiii 4580 Laptop",
                "ListPrice": "1499"
            };
                       
            $.ajax({
                type: "POST",
                url: "http://localhost:61954/ProductService.svc/AddProduct",
                data: JSON.stringify(product),
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (data, status, xmlRequest) {
                    alert("JSON içeriği "+JSON.stringify(product)+". "+data + " numaralı ürün eklenmiştir"); 
                },
                error: function (xmlRequest,status,errorThrown) {
                    alert(xmlRequest.responseText);
                }
            });
        }
    </script>
    <form id="form1" runat="server">
    <div>
        <input type="button" value="Add New Product" onclick="AddNewProduct()" />
    </div>
    </form>
</body>
</html>

Çalışma Şekli

Şimdi web sayfasını biraz inceleyelim. Burada koşullara ve şartlara göre uygulanmış bazı hileler de bulunmaktadır. İlk olarak projeye ilave edilmiş jQuery kütüphanesinin kullanılacağı belirtilmiştir. Formüzerinde button tipinden bir input elementi yer almaktadır. İstemci tarafında bu buton tıklandığında ise AddNewProduct isimli javascript fonksiyonu çalıştırılmaktadır. Peki fonksiyon içerisinde neler olmaktadır?

İlk olarak product isimli bir tip oluşturulduğunu ve ProductId, Title, ListPriceözelliklerine bir takım değerler atandığını görebiliriz. Bu tanımlamayı takip eden satırda ise ajax fonksiyon çağrısı gerçekleştirilmektedir. ajax fonksiyonun pek çok parametresi bulunmaktadır. Örnekte HTTP Postçağrısı gerçekleştirileceğinden typeözelliğine POST değeri atanmıştır. urlözelliği tahmin edileceği üzere HTTPPost talebinin gönderileceği WCF Servis adresini işaret etmektedir. Bu adres tanımında yer alan AddProduct son ekine ayrıca dikkat edilmelidir.

Hatırlanacağı üzere bu bilgi servis operasyonunun WebInvoke niteliğinde belirtilmiştir. data kısmında gerçekleştirilen stringifyçağrısı, parametre olarak aldığı product nesne örneğini JSON formatına çevirmek üzere kullanılır. Böylece servise gönderilecek olan JSON içeriği oluşturulur. contentTypeözelliğine atanan değer ile içerik tipinin JSON olacağı ve karakter seti olarak utf-8 standardının kullanılacağı belirtilmektedir. dataTypeözelliği POST işlemi sırasında kullanılan veri tipinin JSON olduğunu işaret eder. success ve error değişkenleri tahmin edileceği üzere çağrının başarılı veya hata olması durumlarnda devreye giren fonksiyonları taşımaktadır. Her iki fonksiyon da standart olarak XmlHttpRequest tipini kullanır.

Biz örneğimizde bu fonksiyonellikler içerisinde önemli bir iş yapmıyoruz. Sadece çağrının başarılı olması halinde gönderilen JSON içeriğini ve servisden gelen GUID değerini bir mesaj kutusu içerisine gösteriyoruz. Pek tabi gelen içeriğin sayfa üzerinde yer alan bir takım kontrollere basılması da düşünülebilir.

Web sayfasında dikkat edilmesi gereken noktalardan birisi de title elementinin hemen altında kullanılan meta tag' dir.

<meta http-equiv="X-UA-Compatible" content="IE=10" />

Bunu şöyle ifade etmeye çalışalım. Örneği gerçekleştirdiğimiz sistemde Internet Explorer' ın 10 sürümü bulunmakta ve web sayfasının aslında IE Compatibility Mode' da çalıştığı görülmektedir. Nitekim bu bildirimin meta tag olarak bildirilmemesi halinde istemci tarafında bir script hatası ile karşılaşılmaktadır.

restJquery_2

Bu sorun IE 11' de kendini göstermeyebilir. Ya da jQuery kütüphanesinin daha eski bir sürümü böyle bir hatayı oluşturmayabilir. Hatta bu meta tag açık olduğunda Document Mode' un IE 9, 8 ve 7 olduğu durumlarda kütüphanenin aynı hatayı vermeye devam ettiği de tespit edilmiştir. Bu tarayıcıları anlamak hakikaten zor Confused smile

Uyumluluk Sonrası Chrome Öncesi ve CORS

Örneğimizi Internet Explorer ile(en azından sistemde var olan sürümü ile) uyumlu hale getirdik diyebiliriz. Default.aspx sayfasında Add New Product başlıklı butona bastığımızda aşağıdakine benzer bir mesaj kutusu ile karşılaşmamız gerekmektedir.

restJquery_3

Görüldüğü üzere başarılı bir şekilde servis çağrısı yapılmıştır. JSON içeriği üretilmiş ve servisden benzersiz bir GUID değeri elde edilmiştir. Ne var ki örnek Chrome' da çalışmamaktadır Surprised smile (Yine örneğin geliştirildiği makinedeki tarayıcı sürüm için böyle bir durum oluştuğunu ifade edelim)

restJquery_4

Pek de sevimli olmayan bir hata mesajı Sad smile Eğer Chrome tarafında debug işlemi uygulanırsa aşağıdaki gibi bazı hataların oluştuğuna şahit olunur. İşte buton tıklandıktan sonraki durum.

restJquery_5

3 hata mesajı söz konusudur. Hata mesajlarının ikisi jQuery kütüphanesinden gelmektedir ama ana fikir söz konusu metod çağrısına izin verilmemiş olmasıdır. Aslında dikkatli gözler şunu hemen fark edecektir. Web uygulamasının host ediliği port ile WCF Service uygulamasının host edildiği port birbirinden farklıdır. Bu Cross Domainçağrı Chorme tarafından işlenmemiştir. Çözüm olarak(ki burada istediğimiz sadece servisin Chrome üzerinden IE' de olduğu gibi çağırılabildiğini görmektir) ilgili servisin ve web uygulamasının aynı domain' de host edilmesi sağlanabilir. Yani IIS altına atılmaları halinde her hangibir sorun olmadan çağırılabildikleri görülecektir.

Modern tarayıcıların bu tip Cross Domain referans çağrılarına izin vermediği bilinmektedir. Servislerin bu noktada çözüm olarak istemciden gelecek olan bu tip Header' ları kabul edecek şekilde tesis edilmesi gerekmektedir. Bu sıkıntı CROS olarak isimlendirilmiştir. Dolayısıyla servis tarafı CORS (Cross-Origin Resource Sharing)özelliğini desteklemelidir. Bir başka deyişle servisin istemciden gelen Header bilgisine göre POST talebini kabul edecek şekilde ayarlanması sorunu çözecektir.

Sorunu Büyüttük

Görüldüğü üzere yeni bir mücade ile karşı karşıyayız. WCF servisini CORS destekli hale getirmek çözümlerden bir tanesi. Ancak oldukça zahmetli olan bu yola yazımızda değinmeyeceğiz. Yine de ilgilenler http://enable-cors.org/server_wcf.html adresine uğrayabilirler. Daha basit bir çözüm olarak WCF Service Application'ın aslında bir Web uygulaması gibi davranış gösterdiğini düşünerek hareket edeceğiz. Dolayısıyla bir global.asax dosyası ve gelen uygulamaya gelen taleplerin yakalandığı olay metodları söz konusudur. Bu noktada Application_BeginRequest metodu içeriğini aşağıdaki kod parçasında görüldüğü gibi yazmamız yeterli olacaktır.

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    {
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type");
        HttpContext.Current.Response.End();
    }
}

BeginRequest metodu tahmin edileceği üzere WCF servisini host ettiğimiz uygulamaya gelecek her talep için devreye girecektir. jQuery ile gerçekleştirdiğimiz ajaxçağrısında ContentTypeHeader bilgisi kullanılmış ve POST metoduna göre talep de bulunulmuştur. BeginRequest metodunun yaptığı pratikte bu şekilde gelen istekleri geri çevirmemek ve istemci tarafına da uygun olan Header bilgisini göndermektir. Söz konusu değişiklik sonrası uygulamanın Chromeüzerinde de sorunsuz bir şekilde çalışabildiği görülecektir.

restJquery_6

Eksikler

Elbette senaryomuzda önemli eksiklikler bulunmaktadır. Örneğin,

  • Servis tarafının bir sertifika ile çağırılabileceği durumlarda CORS için nasıl aksiyonlar almak gerekir?
  • Son uygulanan pratik, tüm tarayıcılar da çalışmakta mıdır? Örneğin Firefox' ta. Peki ya mobil cihazda bulunan Native kodla geliştirilmiş bir Browser bileşeninde?
  • Acaba Windows Forms içerisinde kullanına WebBrowser gibi kontrollerde sonuç nasıl olacaktır?
  • Peki HTTPGet ile bir JSON veri kümesi istemci tarafına nasıl çekilebilir?
  • Ya veri göndermek için sayfa üzerine konacak kontrollerden nasıl yararlanılabilir?

Bu soruların çözümünü, araştırmasını ve uygulanmasını siz değerli okurlarıma bırakıyorum. Bu mücadeleler inanın size önemli saha tecrübeleri kazandıracaktır. Böylece geldik bir makalemizin daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

HowTo_RestPostJQuery.rar (111,76 kb)


Simetrik Şifreleme(Symmetric Cryptography) Hızları

$
0
0

62374Merhaba Arkadaşlar,

Crptograpy

Hayır hayır şöyleydi.

Crpytograyph

Yok yok böyle değil. Belki de…

Cyrptograhy

Offf! Bir türlü beceremiyorum şunu yazmayı. Hah sanırım buydu.

Cryptography.

Nihayet! Smile 

Şifreleme teknikleri sizlerin de bildiği üzere karmaşık matematiksel algoritmaları baz alacak şekilde tesis edilmeye çalışılırlar. Algoritma ne kadar karmaşık veya basit olursa olsun önemli olan çıkan sonuçların zor kırılacak cinsten olmalarıdır. Bu noktada kullanılan anahtarların ve bit değerlerinin de büyük önemi vardır. E tabi doğal olarak zaman içerisinde pek çok şifreleme algoritması ortaya çıkmıştır. Bunları temel de simetrik ve asimetrik olanlar gibi iki ana kategoriye ayırabiliriz. Ama diğer yandan hash algoritmaları veya veri bütünlüğünü korumaya yönelik algoritmalar da mevcuttur.(Ve belki de bizim bilmediğimiz ama üst düzey veri güvenliğinde kullanılan çok gizli olanları Sarcastic smile )

.Net Framework gibi pek çok programlama geliştirme ortamı, bu tip şifreleme algoritmalarının kod içerisinde kolayca ele alınmasını sağlayacak cinsten tipler/sınıflar içermektedirler. Genellikle hangisinin kullanılacağını tercih ederken simetrik veya asimetrik şifreleme yapılıp yapılmayacağına ya da ne kadar zor kırılacağına bakılır(AES’ in bu konuda bazı yarışmaları var. İnceleyin derim). Bunlardan herhangibiri seçilirse bu durumda farklı faktörleri de göz önüna almamız gerekebilir. Örneğin algoritmanın şifreleme ve çözümleme işlemlerini ne kadar sürede yaptığı da önemli bir kriter olabilir.

Küçük içerikler söz konusu olduğunda bu çok da önem arz eden bir konu değildir, lakin elimizde n sayıda büyük boyutlu, şifrelenmesi gereken ve yeri geldiğinde de çözülecek olan veri kümeleri varsa bu durumda hız önemli bir faktör olabilir.

Biz bu yazımızda, simetrik şifreleme algoritmalarından olan AES(Advenced Encryption Standard’ in Cryptographic Application Programming Interfaces uyarlaması), TripleDES, DES, RC2 ve Rijndael tekniklerini ele alıp, büyük boyutlu bir veri içeriği için şifreleme ve çözümleme zamanlarını ölçümlemeye çalışacağız. (Hem bu vesile ile söz konusu sağlayıcıları pratik olarak nasıl kullanabileceğimizi de göreceğiz)

Temel Cryptography Tipleri

Başlamadan önce Visual Studio – Object Browser yardımıyla söz konusu şifreleme algoritmalarının tip hiyerarşisine bakmamızda yarar olduğu kanısındayım. Temel olarak simetrik şifreleme işlemlerini üstlenen sağlayıcılar(Providers)SymmetricAlgorithm tipinden türeyen alt tiplerden üretilirler.

scs_4

Aslında şifreleme için kullanılacak tipler Provider veya Managed son eki ile biten sınıflardır. Tüm bu sınıflar tepede yer alan SymmetricAlgorithm isimli abstract sınıftan türemektedir.

Bu açıdan bakıldığında, Dependency Injection kullanılarak şifreleme işlemleri için bir üst provider yazılması ve konfigurasyon bazlı olarak ele alınması sağlanabilir. Bu sayede hangi şifreleme sağlayıcısını kullanmak istiyorsak, uygulama ortamına kolayca enjekte edebiliriz.

Tüm bu şifreleme algoritmalarının ortak özelliklerinden birisi de paylaşımlı anahtar(Shared Key) kullanıyor olmalarıdır. Yani, şifreleme için kullanılan anahtar(Key) ve ilklendirme vektör değeri(IV-Initialization vector) ortaktır. Dolayısıyla şifreleme yapılırken kullanılan bu değerler, çözümleme sırasında da devreye girmektedir. Bu zaten, simetrik ve asimetrik şifrelemeler arasındaki en büyük farklardan birisidir.

Test Senaryosu

Artık söz fazla uzatmayalım ve kodlarımızı yazarak testimizi gerçekleştirmeye çalışalım. Test senaryomuzdaki amacımız, 5 şifreleme algoritmasının aynı veri üzerindeki şifreleme ve çözümleme sürelerini ölçmek olacaktır. Bunun için yaklaşık olarak 50 mb büyüklüğünde bir text dosyasını kullandığımı ifade etmek isterim. Text dosyası içeriği ise bildiğimiz Lorem Ipsum paragraflarından oluşmaktadır. (Lorem Ipsum üretimleri için bu adresten yararlanabilirsiniz)

scs_6

Uygulama Kodları

Gelelim kod tarafına. Console uygulaması olarak geliştireceğimiz projemizde aşağıdaki sınıf diagramında yer alan kod içeriğini kullanıyor olacağız.

scs_3

using System;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;

namespace HowTo_Cryptography
{
    public static class Utility
    {
        #region Fields(Alanlar)

        private static string _targetContent = String.Empty;
        private static Stopwatch _watcher = null;

        private static AesCryptoServiceProvider _aesProvider = null;
        private static byte[] _aes_Key = null;
        private static byte[] _aes_IV = null;

        private static TripleDESCryptoServiceProvider _tdesProvider = null;
        private static byte[] _tdes_Key = null;
        private static byte[] _tdes_IV = null;

        private static RijndaelManaged _rijndaelProvider = null;
        private static byte[] _rijndael_Key = null;
        private static byte[] _rijndael_IV = null;

        private static RC2CryptoServiceProvider _rc2Provider = null;
        private static byte[] _rc2_Key = null;
        private static byte[] _rc2_IV = null;

        private static DESCryptoServiceProvider _desProvider = null;
        private static byte[] _des_Key = null;
        private static byte[] _des_IV = null;

        #endregion

        static Utility()
        {
            _aesProvider = new AesCryptoServiceProvider();
            _aes_Key = _aesProvider.Key;
            _aes_IV = _aesProvider.IV;

            _tdesProvider = new TripleDESCryptoServiceProvider();
            _tdes_Key = _tdesProvider.Key;
            _tdes_IV = _tdesProvider.IV;

            _rijndaelProvider = new RijndaelManaged();
            _rijndael_Key = _rijndaelProvider.Key;
            _rijndael_IV = _rijndaelProvider.IV;

            _rc2Provider = new RC2CryptoServiceProvider();
            _rc2_Key = _rc2Provider.Key;
            _rc2_IV = _rc2Provider.IV;

            _desProvider = new DESCryptoServiceProvider();
            _des_Key = _desProvider.Key;
            _des_IV = _desProvider.IV;

            _targetContent = ReadContent();
            _watcher = new Stopwatch();
        }

        #region AES(Advanced Encyption Standard)

        // Şifreleme metodu
        public static byte[] AES_Encrypt()
        {
            return Encypt<AesCryptoServiceProvider>(_aesProvider, _aes_Key, _aes_IV);
        }

        // Çözümleme metodu
        public static string AES_Decrypt(byte[] source)
        {
            return Decrypt<AesCryptoServiceProvider>(_aesProvider, source, _aes_Key, _aes_IV);
        }

        #endregion

        #region TripleDES

        // Şifreleme metodu
        public static byte[] TripleDES_Encrypt()
        {
            return Encypt<TripleDESCryptoServiceProvider>(_tdesProvider, _tdes_Key, _tdes_IV);
        }

        // Çözümleme metodu
        public static string TripleDES_Decrypt(byte[] source)
        {
            return Decrypt<TripleDESCryptoServiceProvider>(_tdesProvider, source, _tdes_Key, _tdes_IV);
        }

        #endregion

        #region Rijndael

        // Şifreleme metodu
        public static byte[] Rijndael_Encrypt()
        {
            return Encypt<RijndaelManaged>(_rijndaelProvider, _rijndael_Key, _rijndael_IV);
        }

        // Çözümleme metodu
        public static string Rijndael_Decrypt(byte[] source)
        {
            return Decrypt<RijndaelManaged>(_rijndaelProvider, source, _rijndael_Key, _rijndael_IV);
        }

        #endregion

        #region RC2

        // Şifreleme metodu
        public static byte[] RC2_Encrypt()
        {
            return Encypt<RC2CryptoServiceProvider>(_rc2Provider, _rc2_Key, _rc2_IV);
        }

        // Çözümleme metodu
        public static string RC2_Decrypt(byte[] source)
        {
            return Decrypt<RC2CryptoServiceProvider>(_rc2Provider, source, _rc2_Key, _rc2_IV);
        }

        #endregion

        #region DES

        // Şifreleme metodu
        public static byte[] DES_Encrypt()
        {
            return Encypt<DESCryptoServiceProvider>(_desProvider, _des_Key, _des_IV);
        }

        // Çözümleme metodu
        public static string DES_Decrypt(byte[] source)
        {
            return Decrypt<DESCryptoServiceProvider>(_desProvider, source, _des_Key, _des_IV);
        }

        #endregion

        #region Generic şifreleme ve çözümleme metodları

        static byte[] Encypt<T>(T provider,byte[] key,byte[] iv)
            where T:SymmetricAlgorithm
        {
            byte[] result = null;
            ICryptoTransform encryptor = provider.CreateEncryptor(key, iv);

            _watcher.Restart();

            using (MemoryStream ms = new MemoryStream())
           {
                using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
               {
                    using (StreamWriter sWriter = new StreamWriter(cs))
                    {
                        sWriter.Write(_targetContent);
                    }
                    result=ms.ToArray();
               }
           }

            _watcher.Stop();
            Console.WriteLine("Encrypt\t{0}\n{1}",provider.ToString(),_watcher.ElapsedMilliseconds.ToString());

            return result;
       }

        static string Decrypt<T>(T provider,byte[] source, byte[] key, byte[] iv)
            where T : SymmetricAlgorithm
        {
            string result = String.Empty;

            ICryptoTransform decryptor = provider.CreateDecryptor(key, iv);

            _watcher.Restart();

            using (MemoryStream ms = new MemoryStream(source))
            {
               using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader sReader = new StreamReader(cs))
                   {
                        result=sReader.ReadToEnd();
                    }
                }
           }

            _watcher.Stop();
            Console.WriteLine("Encrypt\t{0}\n{1}\n", provider.ToString(), _watcher.ElapsedMilliseconds.ToString());

            return result;
        }

        #endregion

        #region Yardımcı metodlar

        static string ReadContent()
        {
            return File.ReadAllText(Path.Combine(Environment.CurrentDirectory, "SampleDocument.txt"));
        }

        #endregion
    }
}

Aslında kod içeriği her ne kadar karmaşık görünse de region’ ları kapatıp büyük resme baktığımızda iskelet daha rahat anlaşılabilir. (Hafiften bir BLOB veya GOD Object AntiPattern’ ine kaymış gibiyiz ama bunu önemsemesekte olur)

scs_5

Kodun Çalışma Şekli

Şimdi neler yaptığımızı kısaca özetleyelim.

Öncelikli olarak staticUtility sınıfı içerisinde bazı temel alanlar(Fields) yer aldığı görülmektedir. Her bir şifreleme sağlayıcısı(Provider) ve bunlara ait Key, IV değerleri için birer alan tanımlanmıştır. Söz konusu alanlar staticyapıcı metod(Constructor) içerisinde initialize edilirler. Ayrıca yapıcı metodumuz, 50 mb’ lık text dosyasını da içerdeki string değişkene alacak şekilde tesis edilmiştir.

5 farklı simetrik şifreleme algoritması için ayrı ayrı metodlar yazıldığı görülmektedir ancak her biri generic olarak tasarlanmış Encypt(ki an itibariyle yanlış yazdığımı fark ettim Open-mouthed smile Sanırım enkıyipt olarak telafüz edebiliriz) ve Decrypt fonksiyonlarını kullanmaktadır. Bu fonksiyonlarda dikkat edilmesi gereken en önemli nokta ise generic T tipi için bir kısıtlama(Constraint) belirtilmiş olmasıdır ki bu kısıtlamaya göre T tipi mutlaka SymmetricAlgorithm türevli olmak zorundadır.

Yazıya konu olan kod parçasının en önemli kısımı, generic metod kullanılması ve T tipi için constraint uygulanarak, sadece simetrik algoritma sağlayacılarına destek verecek ortak fonksiyonelliklerin üretilmiş olmasıdır. Bu kodun yeniden kullanılabilirliği(re-usable), okunabilirliği(readable) ve bakımı(maintable) açısından önemlidir.

Aslında Provider’ ların kullanımları da aynıdır. Şifreleme işlemleri için ICryptoTransform arayüzü(Interface) tipi üzerinde taşınabilecek şekilde bir tip üretilmektedir. Bunun için kullanılan CreateEncryptor metodu parametre olarak Key ve IV değerlerini alır. Bu değerler tahmin edeceğiniz üzere kullanılan şifreleme algoritmasına özeldir. Benzer şekilde çözümleyici için de CreateDecryptor metodlarından yararlanılmaktadır ki bu fonksiyon da parametre olarak Key ve IV değerlerini alır.

Gerek şifreleme gerek çözümleme operasyonları olsun, her ikisinde de CryptoStream tipinden yararlanılmaktadır. Eğer bir şifreleme işlemi söz konusu ise doğal olarak CryptoStreamMode.Write modu, tersine bir çözümleme operasyonu yapılacaksa CryptoStreamMode.Read modu kullanılır.

Şifreleme işlemleri sonucunda provider’ lar içerikleri bir byte[] dizisine yazarlar. Bu noktada StreamWriter sınıfından yararlanılmaktadır. Çözümleme işlemlerinde ise byte[] tipinden olan içeriğin genellikle bir string katarına alınması söz konusudur. Bu noktada da, StreamReader tipinden yararlanılmaktadır.

Aslında yazma/şifreleme operasyonundaki yapı şu şekilde özetlenebilir.

using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter sWriter = new StreamWriter(cs))
                    {
                        sWriter.Write(_targetContent);
                    }
                    result=ms.ToArray();
                }
            }

StreamWriter, CryptoStream’ e yazar. CryptoStream ise MemoryStream’ e. Son olarak MemoryStreamörneği üzerine yazılan içerik bir byte[] array’ e atanır.

Okuma/çözümleme işleminde ise,

using (MemoryStream ms = new MemoryStream(source))
{
    using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
    {
        using (StreamReader sReader = new StreamReader(cs))
        {
            result=sReader.ReadToEnd();
        }
    }
}

bu kez StreamReader, CryptoStream’ e yazar. CryptoStream’ de yine MemoryStream’e. Son olarak StreamReader nesne örneği üzerinden ReadToEnd ile çözümlenen içeriğin string karşılığı alınır.

Örnekte süre ölçümlemesi için Stopwatch tipinden yararlanılmıştır. Bu tipin Restart ve Stop edildiği aralıktaki işlemlerin süresi hesaplanmaktadır. Bu hesaplamalar şifreleme ve çözümleme işlemlerinde devreye giren generic metodlarda yapılmaktadır.

Main metodunun bulunduğu Program sınıfının içeriğini ise aşağıdaki gibi kodlayabiliriz.

using System;

namespace HowTo_Cryptography
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("***Test Case Start***");
                TestMethod();
                Console.WriteLine("***Test Case End***");
            }
        }

        private static void TestMethod()
        {
            string content = String.Empty;

            byte[] aesEncrypted = Utility.AES_Encrypt();
           content = Utility.AES_Decrypt(aesEncrypted);

            byte[] tdesEncrypted = Utility.TripleDES_Encrypt();
            content = Utility.TripleDES_Decrypt(tdesEncrypted);

            byte[] rijndaelEncrypted = Utility.Rijndael_Encrypt();
            content = Utility.Rijndael_Decrypt(rijndaelEncrypted);

            byte[] rc2Encrypted = Utility.RC2_Encrypt();
            content = Utility.RC2_Decrypt(rc2Encrypted);

            byte[] desEncrypted = Utility.DES_Encrypt();
            content = Utility.DES_Decrypt(desEncrypted);
        }
    }
}

Test Sonuçları

Test olması açısından 5 simetrik algoritmanın şifreleme ve çözümleme metodlarını arka arkaya 5 kez çağıran bir yapı kurgulanmıştır. Örneği bu şekilde çalıştırdığımızda aşağıdakine benzer sonuçlar ile karşılaşırız. Tabi buradaki ölçüm değerleri donanımsal faktörlere de bağlıdır.

Testler, 4 çekirdekli ve 4Gb Ram kullanan intel İ5 chipset’ li bir desktop PC üzerinden, Windows 7 işletim sistemi ortamında Visual Studio 2012 ve .Net Framework 4.5 kullanılarak geliştirilmiş bir uygulama tarafından yapılmıştır.

scs_1

Sonuçları daha iyi irdelemek adına süreleri bir Excel grafiğinde birleştirebiliriz. Aynen aşağıda görüldüğü gibi Winking smile

scs_2

Elde edilen sonuçlara göre en hızlı şifreleme ve çözümleme AES sağlayıcısı tarafından gerçekleştirilmiştir. En yavaş olan algoritma ise TripleDES’ tir. Genel olarak tüm algoritmaların şifreleme süreleri çözümleme sürelerine oranla daha düşüktür. Ancak RC2 algoritması tabanlı sağlaycının verdiği sonuçlar ilginçtir. Yapılan 5 test göz önüne alındığında, RC2 için çözümleme hızı, şifreleme hızına göre daha yüksek çıkmıştır.

Bu test örneğinde simetrik şifreleme algoritmalarının Encryption ve Decryption operasyonlarına ait süre ölçümlemelerine bakılmıştır. Hızın öne çıktığı bir durum söz konusu ise AES’ in tercih edilmesi elbette olasıdır ama yine de iyi düşünmek gerekir. Nitekim hangi algoritmanın kaç bitlik şifreleme yaptığına göre de karar verilmesi önemlidir. Bu açıdan bakıldığında, en sağlam algoritmalarından birisi olarak TripleDes ve Rijndael daha fazla öne çıkmaktadır.

Tabi key boyutları dışında farklı etkenler de söz konusudur. Bilindiği üzere 2000li yılların başında şifreleme anahtarlarının en az 128bit destekli olması standart olarak kabul edilmiştir. Çok hassas verilerin korunmasında ise 192bit ile 256bit şifrelemeler önerilmektedir.

National Institute of Standards and Technology(NIST) in ifade ettiğine göre, 80bit anahtar kullanan algoritmalar 2015 yılından itibaren geçerliliğini yitireceklerdir. Dolayısıyla bu ve altında(örneğin 56bit gibi) anahtarlar ile hizmet veren şifreleme algoritmalarının kullanılmaması doğru bir hareket olacaktır.

Bu yazımızda en bilinen ve popüler olan simetrik şifreleme algoritmalarının .Net Framework tarafındaki süre bazlı performanslarını incelemeye çalışıp, nasıl kullanıldıklarını gördük. Konu veri güvenliği olunca en sağlam sistemi de kursanız %1 ihtimalle kırılma olasılığını her zaman için göz önünde bulundurmak gerekir. Bu sebepten yazdıklarıma güvenmeyip daha derin bir araştırma ile hangi simetrik şifreleme algoritmasını seçeceğinize karar vermeniz yerinde bir hareket olacaktır. Böylece geldik bir makalemizin daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

HowTo_Cryptography.zip (45,00 kb)

[Text dosyasının içeriği bilinçli olarak boşaltılıp 1Kb seviyesine indirilmiştir. Test etmeden önce dosya içeriğini copy-paste ile çoğaltarak boyut arttırımı yapmanızı ve 50mb seviyelerine getirdikten sonra kodu çalıştırmanızı öneririm]

Nedirtv?com Yazılım Teknolojileri Seminerleri

$
0
0

Merhaba Arkadaşlar,

18 Ekim Cumartesi günü İstanbul Şehir Üniversitesinde, Nedirtv?com topluluğumuzun bir etkinliği olacak. Bu yıl ikinci kez bir araya gelerek topluluk olarak da kişisel bir rekora imza atmış olabiliriz. Malum iş yoğunluğumuzdan, Türkiye' de ki yazılım topluluklarının son yıllarda yakanlandığı "Durağanlık Hastalığından" olsa gerek pek sık gerçekleştirebildiğimiz bir durum değil.

Etkinliğe kayıt olmak için şu adresi kullanabilirsiniz.(Kayıt formu 1 Ekim saat 12:00 da etkinleşecek)

Bu etkinliğimizde de sizelere faydalı olacağını düşündüğümüz konularımız var. Hatta sponsorlarımız bile var. (Portya, Kariyer Mimarı, Microsoft Türkiye ve Dikeyeksen Yayıncılık)

  • Karşılıksız Dünya Düzeni isimli kitabın yazarı ve Portya Bilişim Hizmetlerinin kurucusu sevgili Nezih Tınas canlı canlı bir "Ön Analiz Toplantısı" yapacak önreğin. Esprili ve enerjik anlatımı ile bizleri bu toplantıların gizemli dünyasına çekecek ve tavşan deliğinin ne kadar derinde olduğunu gösterecek.
  • Son yıllarda özellikle üzerinde durulan, Thoughtworks radarlarına yakalanan, Martin Fowler tarafından sıklıkla kaleme alınan önemli konulardan birisi de Mikro Servis mimarisi. Yaz döneminde Deniz Kuvvetleri Kupası Yat Yarışına katılarak beni kendisine bir kere daha hayran bırakan sevgili Arda Çetinkaya bu oturumda bizlerle yer alacak. Benim de bir süredir üzeriden araştırmala yaptığım bir konu. Keyifle dinleyeceğiz.
  • Fonksiyonel paradigma konusu ile Muhammed Cuma Tahiroğlu' da sahnede alacak. Bizlere her zamanki gibi aynen bloğundaki yazılarına benzer farklı bakış açıları sunacak ve programlamanın farklı dünyalarının kapılarını gösterecek.
  • Ercan Bozkurt hocamız yine ilgi çekici bir konu ile bizlerle olacak. Sanal Gerçeklik...Bakalım bize ne gibi sürprizler hazırlamış. Daha önceden Kinect ile yaptığı etkileşimli sunumları düşününce bizi sanal bir dünyaya götürmesini beklemek en doğal hakkımız olsa gerek.
  • Ben de bu etkinlikte kurumsal uygulamalar hakkında bilgiler vermeye çalışacağım. Gerçi bir Martin Fowler kadar doyurucu olamam ama en azından sektördeki izlenimlerimi aktarabileceğimi düşünüyorum. Hatta bir iki madde katkım bile olabilir.

Bu etkinlik de her zaman ki gibi Uğur Umutluoğlu hocamızın katkıları ile gerçekleşmiş durumda. Banner' lardan afişe, Üniversite ile iletişime geçilip salonun ayarlanmasından konuşmacıların kooridnasyonuna kadar pek çok konuda yardımlarını esirgemedi. Etkinliğin görünmeyen kahramanı. Keşke bir oturumla da kendisi katılsaydı ne de iyi olurdu. Kendisine ne kadar teşekkür etsek azdır.

Heyecanla hazırlandığımız bu etkinliğimize ilgilenen herkesi bekliyoruz. 300 kişilik bir seminer salonumuz var. Doldurabilir miyiz bilemiyorum ama dolup taşarsa da itiraz etmeyiz. Uğur hocamızın dediğine göre büyük bir aksilik olmazsa kahve, çay, kurabiye gibi servisler de olacak.

Görüşmek dileğiyle.

TFİ 110 - Control Tipini Extend Edip Basit Bind İşlemleri Gerçekleştirmek

$
0
0

Merhaba Arkadaşlar,

Diyelim ki, geliştirdiğiniz Windows Forms tabanlı ekranlarınızdaki kontrollerin içerikleri farklı bir veri kaynağından(Strongly Typed özellikte) geliyor ve siz bunları kontrollerin ilgili özelliklerine bağlayacak generic özellikte bir metod geliştirmek istiyorsunuz. Doğrudan özelliklere değer set etmek ile uğraşabilirsiniz de ama, veri kaynağından okuma yapan kod parçasının içerisinde bu işlemi merkezileştirmeyi de düşünüyorsunuz. Ancak ilk adım olarak Control türevli tipler için bir Extension metod üzerinden özelliklere değer bağlama işlemlerini yapmak istiyorsunuz. Nasıl bir yol izlersiniz? Aşağıdaki ip ucu işinize yarayabilir mi?

Peki aynı felsefeyi Web uygulamalarınız için tasarlayabilir misiniz? Hatta reflection hamleleri kokan bu kod parçasında dynmaic kullanabilir miyiz?

Bir başka ip ucunda görüşmek dileğiyle.

Entity Framework–İki Entity Bir Table ile Lazy ve Eager Loading

$
0
0

eftsplit_0Merhaba Arkadaşlar,

Yandaki görüntü 1988 yılında Mevlüt Dinç(nam-ı diğer Mev Dinc) tarafından kurulan Vivid Image firmasının geliştirdiği oyunlardan birisine ait. The First Samurai. Mev Dincülkemizin yetiştirdiği en önemli değerlerden birisidir. Kendisi ile NedirTv topluluğunda yapılmış güzel bir röportaj da bulunmaktadır. Pek çoğumuz onu, SOBEEfirması ile de tanımıştır. Ben ise uzun zaman önce Microsoft’ un Darphane’ deki binasında katıldığım bir söyleşiden…

MVP olduğum o dönemlerde Microsoft Türkiye düzenlediği bir etkinlik ile onu karşımıza çıkartmıştı. Kendisini büyük bir keyifle dinlemiştik. Nasıl bu günlere geldiğinden, geliştirdiği oyunlardan, kurduğu SOBEE firması' nın projelerinden bahsetmişti. Hatta akılda kalan önemli ifadelerden birisi de, yeni geliştirmekte oldukları oyunlarda C++ yerine C# programlama dilini tercih etmeleriydi. (Sene 2007 olabilir) Ancak benim daha çok aklımda kalan tam olarak hatırlayamasam da aşağıda yazan ifadeleriydi.

Bir savaş oyununda binanın tamamını düşünmeye gerek yoktur. O an için sadece aktörlerin bulunduğu odayı düşünmek yeterlidir.

Şimdi nereden geldik bu sözlere. Geçtiğimiz günlerde Entity Frameworküzerine bir takım araştırmalar yaparken Lazy ve Eager Loading işlemlerinin hangi noktalarda kullanılabileceğine dair bazı yararlı bilgiler edindim. Bununla birlikte bir senaryo gerçekten dikkatimi çekti.

Temel Gereksinim

Özellikle içerisinde CLOB veya BLOB benzeri alanlar barındıran tabloların Entity Framework tarafındaki kullanımlarında network yükünü hafifletmek adına bir tablonun iki Entity ile ifade edilebilmesi düşünülebilir. Böyle bir durumda Lazy Loading’ i tablo içerisindeki alanlar bazında uygulama şansına sahip oluruz. Bu, özellikle LINQ(Language INtegrated Query) sorgularını işlettiğimiz yerlerde performans açısından faydalı bir iyileştirmedir. Kısacası bir tablonun kendi alanları içerisinde ilişki kurup bunu Entity seviyesinde ifade etmemiz gerekmektedir. Gelin basit bir örnek üzerinden ilerleyerek konuyu incelemeye çalışalım.

Console Application formundaki örnekte yazının yazıldığı tarih itibariyle Entity Framework 6.1.1 sürümü kullanılmıştır. Entity Framework, NuGet paket yönetim aracı ile projeye dahil edilebilir.

Model

Code First yaklaşımını baz alarak geliştireceğimiz örnekte ilk olarak aşağıdaki sınıf çizelgesinde(Class Diagram) yer alan tipleri inşa ederek işe başlayabiliriz. Temel olarak PDF,WMV gibi yüksek boyutlarda ve binary formatta ifade edilebilen veri içeriklerini taşıyacak bir SQL tablosunun iki farklı Entity ile ifade edildiğini söyleyebiliriz. (Ki bu sebepten DocumentContent isimli sınıf içerisinde yer alan Content ve FrontCover özellikleri byte[] tipinden tanımlanmışlardır)

eftsplit_2

using System.ComponentModel.DataAnnotations;
using System.Data.Entity;

namespace HowTo_EFTable
{
    public class CompanyBookContext
        :DbContext
    {
        public DbSet<Document> Document { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Document>().ToTable("Documents");
            modelBuilder.Entity<DocumentContent>().ToTable("Documents");

            modelBuilder.Entity<Document>()
                .HasRequired(c => c.Content)
                .WithRequiredPrincipal();
        }
    }

    public class Document
    {  
        [Key]
        public int DocumentID { get; set; }
        public string Title { get; set; }
        public int PageCount { get; set; }
        public string Language { get; set; }
        public string Genre { get; internal set; }
        public string Publisher { get; set; }
        public virtual DocumentContent Content { get; set; }
        public string ISBN { get; internal set; }
    }

    public class DocumentContent
    {
        [Key]
        public int DocumentID { get; set; }
        public byte[] Content { get; set; }
        public byte[] FrontCover { get;set; }
    }
}

Pek tabi dikkat edilmesi gereken önemli noktalar bulunmaktadır. Söz gelimi Document ve aslında büyük boyutlu içerikleri barındıran DocumentContent sınıfları arasında bir ilişki vardır. Öyleki her ikisi de aslında aynı tabloyu işaret etmelidir. Bunun için her iki sınıfın DocumentID isimli özellikleri Key nitelikleri(attribute) ile işaretlenmiştir.

Entity tiplerinde Key niteliği kullanılmazsa kuvvetle muhtemel aşağıdaki gibi bir çalışma zamanı hatası alınacaktır.eftsplit_1

Document tipi içerisinde yer alan Content isimli özellik aslında DocumentContent tipindedir ve bir Navigation Property şeklinde düşünülebilinir.(Sanki iki farklı tablo arasında one-to-one Relation kuruyoruz gibi düşünebiliriz)

Çok doğal olarak Code First yaklaşımının kullanıldığı bu örnekte modelin inşası sırasında da bazı özel işlemler yapılması gerekmektedir. Nitekim veri tabanı üzerinde tek bir tablo olması planlanmaktadır ve model’in içinde yer alan iki Entity’ nin aslında aynı tabloyu işaret edeceği veri tabanı nesnelerinin üretimi sırasında söylenebilmelidir.

Bu işlem ezilen(Override)OnModelCreating metodu içerisinde yapılmaktadır. Dikkat edileceği üzere Document ve DocumentContent isimli Entity tiplerinin aynı tablo’ yu(ki örnekte Documents) işaret ettikleri belirtilmektedir.

Bu arada Config

Uygulamada SQL Server kullanılmaktadır. Bu nedenle config dosyası içerisindeki connectionStrings elementi içeriği önemlidir. Hatırlanacağı üzere DbContext türevli sınıf adı ile aynı isimde bir connectionString elementinin bulunması gerekmektedir. Örnekte aşağıdaki bağlantı bilgisi kullanılmıştır.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <connectionStrings>
    <add name="CompanyBookContext" connectionString="data source=.;database=Azon;integrated security=SSPI;" providerName="System.Data.SqlClient"/>
  </connectionStrings>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
</configuration>

Ana Uygulama Kodları

Örnekte temel olarak Lazy ve Eager Loading operasyonlarının özellikle SQL Script' ler bazında nasıl olduğu üzerinde durulmaktadır. Bu nedenle aşağıdaki anlamsız kod içeriği ele alınabilir. Bizim için önemli olan arka planda yürütülen SQL betikleridir.

Program.cs

using System;
using System.IO;
using System.Linq;

namespace HowTo_EFTable
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] samplePDF = File.ReadAllBytes(@"c:\DomainDrivenDesignQuicklyOnline.pdf");
            byte[] sampleCover= File.ReadAllBytes(@"c:\SampleCover.png");

            using (CompanyBookContext context=new CompanyBookContext())
            {
                context.Database.Log = Console.Write; // SQL Script' lerini izlemek için Log çıktısını Console olarak set ettik.

                Document someBook = new Document
                {
                    Language = "TR",
                    ISBN="1234-3456-BOOK-1202",
                    Title = "Domain Driven Design Quickly - Online Edition",
                    Genre="Computer Books",
                    PageCount = 348,
                    Publisher="Your Best Publisher",
                    Content = new DocumentContent
                    {
                        FrontCover =sampleCover,
                        Content =samplePDF
                    }
                };

                context.Document.Add(someBook);
                context.SaveChanges();

                #region Lazy Loading

                var dcmnt = (from d in context.Document
                             where d.ISBN == "1234-3456-BOOK-1202"
                             select d).FirstOrDefault();
                if (dcmnt != null)
                {
                    byte[] bookContent = dcmnt.Content.Content;
                    Console.WriteLine(bookContent.Length.ToString()); // bookContent' i her hangibir şekilde kullanmassak ikinci Select işlemi gerçekleşmez.
                }

                #endregion

                #region Eager Loading

                dcmnt = (from d in context.Document.Include("Content")
                         where d.ISBN == "1234-3456-BOOK-1202"
                         select d).FirstOrDefault();
                if (dcmnt != null)
                {
                    byte[] bookContent = dcmnt.Content.Content;
                }

                #endregion
            }
        }
    }   
}

Örnekte Document tipinden bir nesne örneği üretilmektedir. Dikkat edilmesi gereken nokta Content özelliğine de DocumentContent tipinden bir örneğin atanmış olmasıdır. Document nesne örneğinin DbSet’ e eklenmesi işlemi veri tabanı tarafından Documents isimli tabloya bir insert işlemi olarak algılanmaktadır.

Kodun sonraki kısmında ise oluşturulan Document içeriğinin tablodan iki farklı şekilde çekilme işlemi söz konusudur. İlkinde Lazy Loading tekniğinin uygulaması söz konusudur. Document içeriği çekilirken DocumentContent tipinin taşıyacağı alanlar ilk etapta alınmazlar. Ta ki Content özelliği kodda kullanılana dek(byte[] array' in boyutunun Console penceresine yazıldığı yer)

Eager Loading tekniğinin uygulandığı durumda ise DocumentContent tipinin işaret ettiği Content ve FrontCover alanlarının, LINQ ifadesindeki Include terimi nedeniyle Select işlemi sırasında çekilmesi söz konusudur. Yani tüm tablo içeriği, Lazy Loading’ in aksine Select ifadesi ile birlikte gelmektedir.

Çalışma Zamanı Analizi

Aslında durumu daha iyi analiz etmek adına çalışma zamanı çıktılarına bakabiliriz. Log içeriğini Console penceresine yansıttığımızdan, arka planda çalıştırılan SQL Script' lerini kolayca görebiliriz.

eftsplit_5

Şimdi ilk LINQ ifadesini  ele alalım(Lazy Loading region’ lı kısım)

Arka planda ilk olarak Content ve FrontCover alanlarını içermeyen bir Select cümleciği çalıştırıldığı görülmektedir.

SELECT TOP(1)
[Extent1].[DocumentID] as [DocumentID],
[Extent1].[Title] as [Title],
[Extent1].[PageCount] as [PageCount],
[Extent1].[Language] as [Language],
[Extent1].[Genre] as [Genre],
[Extent1].[Publisher] as [Publisher],
[Extent1].[ISBN] as [ISBN]
FROM [dbo].[Documents] as [Extent1]
WHERE N'1234-3456-BOOK-1202'=[Extent1].[ISBN]

Bu son derece doğaldır nitekim gelmeyen alanlar kodun o anki satırına kadar talep edilmemiştir. Ancak elde edilen Document nesne örneğinin Content özelliği üzerinden hareketle uzunluk bilgisi ekrana yazılmak istendiğinde, SQL tarafında ikinci bir Select ifadesinin çalıştırıldığı görülmektedir.

SELECT TOP(1)
[Extent1].[DocumentID] as [DocumentID],
[Extent1].[Content] as [Content],
[Extent1].[FrontCover] as [FrontCover]
FROM [dbo].[Documents] as [Extent1]
WHERE [Extent1].[DocumentID]=@EntityKeyValue1

Aynı Where koşulu için çalıştırılan bu ifade de Documents tablosundan sadece FrontCover ve Content alanlarının çekildiğine dikkat edilmelidir. İşte bu, “ihtiyaç duyduğum yerde verileri yükle” felsefesi olarak yorumlanabilir. Kısaca Lazy Loading

Gelelim ikinci ifadeye; Bu kez LINQ sorgusunda Include metodunun çağırıldığı ve parametre olarak Content isimli Navigation Property değerinin verildiği görülmektedir. Buna göre Documents tablosundaki tüm alanlar(Document ve DocumentContent Entity tiplerine ait özelliklerin işaret ettikleri)Select cümleciğine dahil edilmiştir.

SELECT TOP(1)
[Extent1].[DocumentID] as [DocumentID],
[Extent1].[Title] as [Title],
[Extent1].[PageCount] as [PageCount],
[Extent1].[Language] as [Language],
[Extent1].[Genre] as [Genre],
[Extent1].[Publisher] as [Publisher],
[Extent1].[ISBN] as [ISBN],
[Extent1].[Content] as [Content],
[Extent1].[FrontCover] as [FrontCover]
FROM [dbo].[Documents] as [Extent1]
WHERE N'1234-3456-BOOK-1202'=[Extent1].[ISBN]

İşte bu da,“o an kullanmayacak olsam da tüm içeriği şu anda bana ver.” felsefesidir. Yani Eager Loading

Tabi program çalıştırıldığında modelin inşasının sonuçlarına da bakılmalıdır. SQL Management Studio ile ilgili veritabanına gidilirse Documents isimli tek bir tablonun aşağıdaki şekildeki gibi oluşturulduğu görülebilir.

eftsplit_3

Ayrıca kod tarafında gerçekleştirilen Insert işlemi sonrasında, iki farklı Entity örneğindeki özellik değerlerinin tek bir satır içerisine yerleştirildiği fark edilebilir.

eftsplit_4

Sonuç

Aslında yazımızın başında da belirttiğimiz ve Mev Dinc’ in ifade ettiği üzere, bazen olay anında ve yerinde tüm detayın olmasına gerek yoktur. O anda sadece bulunması gereken verilere ihtiyaç vardır. Bu felsefe görüldüğü üzere sadece oyun programlama tekniklerinde değil farklı konularda da karşımıza çıkmaktadır.Tabi bu felsefe kurgunun çok iyi yapılmasını gerektirir. Lazy ve Eager Loading teknikleri, diğer ORM araçlarında olduğu gibi Entity Framework’ ün de olmassa olmazlarıdır. Makalemizde ele aldığımız konu ince bir performans ayarını işaret etmektedir. Bir tabloyu kod  tarafında parçalı şekilde ifade edebilmek, gerekli parçalarının Lazy Loading ile yüklenmesinin yolunu açmaktadır. Konu hakkında daha detaylı bilgiye Peter Vogel' in MSDN Magazine' de yayınlanan makalesinden ulaşabilirsiniz. 

Böylece geldik bir makalemizin daha sonuna. Bir bakşa makalemizde görüşünceye dek hepinize mutlu günler dilerim.

ObservableCollection’ ı Anlamak

$
0
0

Formspring.meMerhaba Arkadaşlar,

31 Mart 2013 deki kapanma kararına kadar Formspring‘in sadık kullanıcılarından birisiydim. Her ne kadar anlık bir soru-cevap ortamı olmasa da, takip edenler açısından faydalı bir sosyal ağ idi. Özellikle Facebook, Twitter gibi eklentileri de, cevapların farklı sosyal ağlara bağlanmasında önemli rol oynuyordu. Bu sayede verilen cevapların daha fazla kitleye ulaşması mümkündü. Ama maya bir şekilde tutmadı, kullanıcı sayısı git gide azaldı ve sonunda kapatılma kararı verildi.(Şu anda o adrese girmek isterseniz aslında şu adrese yönleniyor ve yeni bir oluşumla karşılaşıyorsunuz)

İşte o dönemlerde WCF tarafında Interceptor' ların nasıl kullanıldığına dair bir makale talebi almıştım Formspringüzerinden. Onu geçtiğimiz zamanlarda cevaplamayı başardım. Derken bunun ardından benzer bir soru daha gelmişti. Someone' dan gelen soru şöyleydi ve henüz cevaplamayı başaramamıştım…

burak abi merhaba senden wpf de sık kullanılan observablecollection konusunu anlatmanı rica ediyorum mmalesef bu konuda derinlemesine anlatım yapan türkçe kaynak yok saygılarımla başarılar hayırlı işler

İşte bu makalemizde söz konusu soruya cevap bulmaya çalışıyor olacağız.

WPF(Windows Presentation Foundation)bilindiği üzere Microsoft .Net Framework 3.0 ile birlikte tanıtılmış bir alt yapı(Infrastructure). Windows tabanlı masaüstü uygulamalarına(ve hatta Browser tabanlı da çalışabiliyorlar) yeni bir soluk getiren yapının XAML(eXtensible Application Markup Language) ile olan sıkı bir ilişkisi de bulunmakta. Dolayısıyla anlatacağımız konu aslında çok uzun zamandır var olan bir mevzu, lakin WPF tarafına yeni başlayan birisi için de epey yabancı sayılabilir. İşe ilk olarak bu koleksiyona olan ihtiyacı ortaya koyarak başlamakta yarar var.

Gereksinim

Günümüz yazılım ürünlerinin pek çoğu ister web tabanlı olsunlar, ister mobil cihaz üzerinde koşsunlar vb, genellikleData-Centric(Veri odaklı) olarak geliştirilmekteler. İçerik bir veritabanı sunucusundan(hatta NoSQL tabanlı bir kaynak bile olabilir) gelebileceği gibi, bellek üzerinde oluşturulmuş bir koleksiyon veya basit bir POCO(Plain Old CLR Object) tipi dahi olabilir.

Bu açıdan bakıldığında veri odaklı uygulamaların ön yüzlerinin(User Interface) veri ile olan iletişiminde karşılıklı olarak bir bilgi transferi söz konusudur. Yani arayüzler, veride meydana gelecek en ufak bir değişiklikten haberdar olmak isterlerken, arayüzde meydana gelen değişikliklerin de veri tarafına yansıtılması gibi bir ihtiyaç ortaya çıkmış durumdadır.

Şimdi burada durup biraz daha derin düşünmemiz gerekiyor. Tarafların birisinde meydana gelen değişiklikler sonucu başka bir tarafın/tarafların uyarılması(Notify edilmesi diyelim) yazılım dünyasında çok sık rastlanan bir durum olsa gerek. İşte bu sebepten zaten bir tasarım kalıbı bile ortaya çıkmış. Observer Design Pattern. Bu konuda daha önceden yazdığım bir makaleye şu adresten ulaşabilirsiniz

Observer Tasarım Kalıbı ile Olan İlişki

Peki bu desenin konumuzla ilgisi nedir? Sadece kök kelime isim benzerliği olabilir mi? Aslında pek değil. ObservableCollection' un iç yapısına bakıldığında ObserverTasarımKalıbını uyguladığını fark edebiliriz. Çünkü bu koleksiyonun en büyük özelliği, veri bağlı kontroller ile ilişkilendirildiğinde ekleme, çıkartma ve tazeleme gibi işlemlerde uyarı verilmesine zemin hazırlıyor olmasıdır. Bu uyarı genellikle bir arayüz kontrolünün durum değişikliğinden haberdar olması olarak algılanır. Örneğin koleksiyona bir veri eklendiğinde, bu koleksiyon ile ilişkili kontrolün ilgili öğeyi otomatik olarak göstermesi gibi.

ObservableCollection<T> sınıfı ve çevresinde etkileşimde olduğu tiplerin genel bir fotoğrafı şekilde görüldüğü gibidir.

htoc_1

System.Collections.ObjectModel isim alanı(namespace) içerisinde yer alan genericObservableCollection<T> sınıf, iki arayüzü(Interface) implemente etmektedir. Bunlardan birisi özellik bazlı(Property Based) değişiklikler sonrası tetiklenecek olayı(Event) uygulatan INotifyPropertyChanged iken, diğeri de koleksiyon değişimleri sonrası tetiklenmesi gereken olayı uygulatan INotifyCollectionChanged arayüzüdür(Interface) Zaten bu iki arayüzün belirttiği olaylar ObservableCollection içerisinde uygulanırken, bağlandıkları kontrolleri uyaracak şekilde tasarlanmışlardır.

Öyleyse ObservableCollection<T>ın temel amacı bellidir; Generic T tipi için söz konusu olan ekleme(Add), silme(Remove) veya yeniden tazeleme(Refresh) gibi işlemlerde bir uyarı(Notify) yayınlamak.

Örnek

Konuyu daha net anlamak adına basit bir örnek üzerinden ilerlemeye çalışalım. Öncelikli olarak aşağıdaki sınıf çizelgesinde(Class Diagram) görünen tipleri içeren bir WPF uygulaması oluşturduğumuzu düşünelim.

htoc_2

namespace HowTo_ObservableCollection
{
    public classBook
    {
        public int BookId { get; set; }
        public string Title { get; set; }
        public int PageSize { get; set; }
        public string Producer { get; set; }
    }
}

using System.Collections.ObjectModel;

namespace HowTo_ObservableCollection
{
    public class BookList
        :ObservableCollection<Book>
    {
        public BookList()
           :base()
        {
            Add(
                new Book
                {
                    BookId = 1
                    , Title = "Advanced WCF Programming"
                    , Producer = "Maybe Me"
                    , PageSize = 1280
                }
                );
            Add(
                new Book
                {
                    BookId = 2,
                    Title = "SOA: from a Developer Vision",
                    Producer = "Maybe Me",
                    PageSize = 740
                }
                );
       }
    }
}

BookList, ObservableCollection<Book> türevli olacak şekilde tanımlanmış olup içerisindeki yapıcı metod(Constructor) ile bir kaç Book nesne örneğinin yüklenmesini sağlamaktadır. Book tipi içerisinde çok basit bir kaç özelliğe yer verilmiştir.

BookList bir ObservableCollection<T>örneğidir. Bu nedenle veri bağlı kontroller ile ilişkilendirildiğinde ekleme, silme gibi operasyonlar sonrasında bildirimlerde bulunabilir. Bu durumu test etmek ve hangi hallerde koleksiyonun nasıl çalıştığını görmek adına geliştirmekte olduğumuz WPF uygulamasının arayüzünü aşağıdaki gibi tasarlayarak devam edelim.

htoc_3

Window seviyesinde bir Resource tanımlanmış ve BookList koleksiyonu işaret edilmiştir. BookList koleksiyonunun kendisi, ListBox kontrolüne bu staticresource yardımıyla ItemsSourceözelliği üzerinden bağlanmaktadır.

Book tipine ait özelliklerin, DataTemplate içerisindeki kontrollerin Textözelliklerine nasıl bağlandığına dikkat etmekte yarar vardır. Bir başka kayda değer nokta ise şudur; Visual Studio' nun WPF Designer' ı üzerinde çalışılmakta olup, uygulama çalışmadığı halde koleksiyon içerisinde yer alan kitap bilgileri, kontrollerin Textözelliklerine bağlanmış ve içerikleri gösterilmiştir.

Lakin burada ayrı bir nokta daha vardır. Eğer BookList sınıfını ObservableCollection<Book> yerine List<Book> tipinden türetirsek de, az önce belirttiğimiz davranış sergilenecektir. Yani Visual Studio tasarım zamanı yine kitap bilgilerini bağlanan kontrollerde gösterecektir. O zaman ObservableCollection<T> nin henüz kullanım amacı tam olarak tespit edilebilmiş değildir. Eğer List<T> tipi de yukarıdaki senaryoda aynı davranışı gösterdiyse, neden ObservableCollection<T> kullanalım ki.

Neden ObservableCollection<T> Kullanırız ki?

Gelin örneğimizi biraz daha değiştirelim ve aşağıdaki hale getirelim.

htoc_4

using System.Windows;

namespace HowTo_ObservableCollection
{
    public partial class MainWindow
        : Window
    {
        BookList sourceList = null;

        public MainWindow()
        {
            InitializeComponent();

            sourceList = ListBoxBooks.ItemsSource as BookList;
        }

        private void ButtonAdd_Click_1(object sender, RoutedEventArgs e)
        {
           sourceList.Add(new Book
            {
                BookId=94,
                 Title="Starwars Clone Wars",
                  Producer="Lucas Arts",
                   PageSize=180
            }
            );
        }

        private void ButtonRemove_Click_1(object sender, RoutedEventArgs e)
        {
           sourceList.RemoveAt(0);
        }

        private void ButtonChange_Click_1(object sender, RoutedEventArgs e)
        {
            sourceList[1].Title = "Changed...";
        }
    }
}

İlk olarak ListBox kontrolünün çalıştığı veri kaynağını ItemsSourceözelliğini BookList tipine dönüştürerek(cast) elde etmekteyiz. Nitekim ekleme, çıkartma ve özellik değiştirme işlemlerini bu kaynak üzerinden gerçekleştireceğiz.

3 farklı Button kontrolümüz ile Add, Remove ve Property Change senaryolarını ele almaya çalışıyoruz(Burada özellikle kodun ilgili yerlerine Breakpoint' ler koyup Debug ederek ilerlememiz de yarar var)

İlk olarak Add işlemine bakalım. BookList' e yeni bir Book nesne örneği eklendiğinde veriye bağlanmış olan kontrollerde otomatik olarak bu değişim için haberdar edilecek ve aşağıdaki çalışma zamanı durumu söz konusu olacaktır.

htoc_5

Görüldüğü gibi 94 numaralı Book nesne örneği koleksiyona eklendikten sonra yeni içerik ListBox kontrolüne de anında yansımıştır. Eğer Remove işlemini gerçekleştirirsek de benzer bir durum ortaya çıkacak ve ListBox kontrolü güncellendiği gibi, kaynak koleksiyondan da söz konusu kitap çıkartılacaktır.

htoc_6

Add sonrası Remove işlemi icra edildiğinde ise koleksiyondaki eleman sayısının 1 eksildiği görülecektir. Ayrıca ListBox kontrolünden de ilgili Book örneği kaldırılacaktır.

htoc_7

Peki Change işlemine gelirsek. Aslında Change vakasında, koleksiyondaki bir Book nesne örneğinin Titleözelliğinde yapılan değişiklik söz konusudur. Bu durumda aşağıdaki sonuçlar ile karşılaşılır.

htoc_8

Dikkat edileceği üzere koleksiyondaki Bookörneğinin Titleözelliğinin içeriği Changed olarak değişmiştir. Ne varki kullanıcı arayüzüne baktığımızda aynı etkinin oluşmadığı görülür. Title ilk ve orjinal hali ile kalmıştır.

htoc_9

Bu davranış doğaldır. Nitekim INotifyPropertyChanged aslında ObservableCollection<T> tipi için(ki örneğimizde bundan türeyen BookList) için geçerlidir. Ancak yaptığımız değişiklik, aslında bir Book nesne örneğinin özelliği üzerinde meydana gelmektedir. Bir başka deyişle Book sınıfına INotifyPropertyChanged arayüzü implemente edilmediği takdirde, User Interface' in de durum değişikliğinden haberdar olması pek mümkün değildir.

Şimdi gelelim önemli bir noktaya; Eğer BookList' i List<Book> tipinden türetirsek, Add ve Remove işlemleri sonrasında, User Interface kontrollerine ait içeriklerin güncellenmediğini görürüz. Şu anda ObservableCollection<T> ile List<T> arasında oluşan önemli bir davranış farkını da yakalamış bulunuyoruz.

Sanıyorum ObservableCollection<T> tipinin kullanım amacını ve şeklini biraz daha net anlayabilmişizdir. ObservableCollection<T> bildirim yapmak için gerekli arayüz tanımlamalarını uyguladığından, özellikle veri bağlı kontrollerin söz konusu olduğu senaryolarada, hem bileşenlerin hem de kaynak koleksiyonun Add,Remove,Refresh işlemleri sonrası uyarılmasında hazır bir alt yapı sunmaktadır. Örneği test ederken Debug ederek ilerlemenizi ve özellikle BookList koleksiyonunu ListBox<Book> türevli ele alarak analiz etmenizi öneririm.

Tabi bakabileceğiniz/araştırabileceğiniz başka vakalar da var. Örneğin,

  • ListBox kontrolünün Itemsözelliği ile görsel içerik de değişiklikler yaparsak ne olur? Bundan asıl koleksiyon(uygulamada BookList tipinin çalışma zamanı örneği) içeriği etkilenir mi?
  • Ya da bu senaryo aslında WorkflowFoundation tarafında özellikle görsel içeriğe sahip olan Custom Component’ ler de göz önüne alınabilir mi? Söz gelimi, kullanılabilir WCF servislerin listesini bir kaynaktan çekip, Workflow Component’ inin içerisinde yer alan bir ListBox bileşeninde gösterebilir miyiz? vb…

Bu tip soruları da ben size sormuş olayım.

Böylece geldik bir yazımızın daha sonuna. Bir sonraki makalemizde görüşmek dileğiyle hepinize mutlu günler dilerim.

HowTo_ObservableCollection.zip (71,13 kb)

Asp.Net–Custom Configuration Section Geliştirmek

$
0
0

ConfigurationMerhaba Arkadaşlar,

Konfigurasyon tabanlı geliştirme modeli, uygulama kodlarına girilmeden çalışma zamanına yönelik değişiklikler yapabilmemizi sağlar. Bu sayede pek çok programın kodsal müdahale yapmadan davranışları değiştirilebilir. .Net dünyasında baktığımızda da, App.Config, Web.config gibi dosyalar içerisinde Framework’ün geneline yönelik pek çok konfigurasyon ayarı bulunduğu görülür. appSettings, connectionStrings, httpHandler vb…

Eskilerden : Asp.Net 2.0 ile Configuration Management (Konfigurasyon Yönetimi)

Söz konusu konfigurasyon içerikleri aslında XML tabanlı bir dosya şemasının parçalarıdır ve doğal olarak element ile attribute’ lardan oluşmaktadır. Konfigurasyon dosyalarının daha iyi yönetilebilmesi için Asp.Net 2.0 ile birlikte Configuration API alt yapısı geliştirilmiştir. Bu kütüphane sayesinde konfigurasyon içerisindeki elementleresınıfbazında erişmek ve yönetebilmek mümkündür. Pek tabi XML elementlerinin sahip oldukları nitelikler, sınıfların özellikleri(Property) olarak ele alınmaktadır.

Peki konfigurasyon dosyası içerisine kendi özel kısımlarımızı(section) ilave etmek ve hatta bunları çalışma zamanında(Runtime) kullanmak istersek, nasıl bir yol izlememiz gerekir? Thinking smile 

Konfigurasyon Yapısı

Aklımıza ilk gelen belirli tip türetmeleri veya arayüz(interface) uyarlamaları ile bu işin halledilebilecek olmasıdır. Aslında olayı çözümlemek için var olan konfigurasyon parçalarının örnek tip yapısını incelemek yerinde bir hareket olacaktır. Söz gelimi system.web kısımı içerisindeki compilation ve pages elementlerini incelediğimizi düşünelim.

ccs_1

Eğer objectbrowserüzerinden ilgili elementlerin karşılık geldiği sınıfları incelersek şu sonuçlara varırız.

  • system.web elementi SystemWebSectionGroup isimli bir sınıf ile işaret edilmekte olup ConfigurationSectionGroup türevlidir.
  • compilation elementi system.web in alt elementidir ve CompilationSection sınıfı ile işaret edilmektedir. Bu sınıf ise ConfigurationSection’ dan türemiştir.
  • Benzer şekilde pages elementi de ConfigurationSection türevli PageSection sınıfı ile temsil edilmektedir.
  • namespaces elementine baktığımızda aynı tipten birden fazla elementi içerecek şekilde kullanılabildiği görülmektedir. Bunun için NamespaceColletion sınıfı ConfigurationElementCollection türevli olarak tasarlanmıştır.
  • namespaces segmenti içerisinde yer alan add elementleri aslında NamespaceInfo tipini işaret etmekte olup yine ConfigurationElement türevlidir.
  • alt elementler genellikle üst elementlerin birer özelliği(sınıf bazında düşünüldüğünde) olarak karşımıza çıkmaktadır.

Dolayısıyla kendi geliştireceğimiz özel Section elementleri için de bu tip bir yol izlememiz ve ilgili türetmeleri yapmamız yeterli olacaktır.

Örnek Senaryo

Basit bir senaryo üzerinden ilerleyebiliriz. Örneğin bir web uygulamasının web.config dosyası içerisinde aşağıdaki gibi bir konfigurasyon kısmı oluşturmak istediğimizi düşünelim.

ccs_3

serviceConnection ve altında yer alan definition elementlerinin işlevselliği çok önemli değildir aslında. Sadece bir kaç nitelik ve alt element içeren bir XML yapısı söz konusu. Bizim yapacağımız basit olarak bu konfigurasyon içeriğini türlendirmek ve çalışma zamanında yönetebilir hale getirmek. Öyleyse işe koyulalım ne duruyoruz.

Sınıfların İnşa Edilmesi

Boş bir Web uygulamasında aşağıdaki sınıf çizelgesinde yer alan tipleri ürettiğimizi düşünelim.

ccs_2

ServiceConnectionSection tipi dikkat edileceği üzere ConfigurationSection türevlidir ve içerisinde Type isimli ServiceTypeenum sabiti tipinden bir özellik ile ConfigurationElement türevli olan DefinitionSection tipinden başka bir özellik yer almaktadır.

ServiceConnectionSection sınıfı;

using System;
using System.Configuration;

namespace HowTo_WritingCustomConfigSection
{
    public class ServiceConnectionSection
       :ConfigurationSection
    {
        // Save işlemine izin vermesi için false döndürecek şekilde ezdik
        public override bool IsReadOnly()
        {
            return false;
        }

        [ConfigurationProperty("type", DefaultValue = ServiceType.WCF, IsRequired = false)]
        public ServiceType Type
        {
            get
            {
                return (ServiceType)Enum.Parse(typeof(ServiceType), this["type"].ToString());
            }
            set
            {
                this["type"] = value.ToString();
            }
        }

        [ConfigurationProperty("definition",IsRequired=true)]
        public DefinitionSection Definition
        {
            get
            {
                return (DefinitionSection)this["definition"];
            }
            set
            {
                this["definition"]=value;
            }
        }
    }
}

Konfigurasyon içeriğinde çalışma zamanında da değişiklik yapılması mümkündür ama konfigurasyon yöneticisinin Save metoduna tepki verebilmesi için IsReadOnly özelliğinin ezilmesi(override) ve false döndürmesi gerekmektedir.

Dikkat edileceği üzere Definition ve Type isimli özelliklere ConfigurationProperty niteliği(Attribute) uygulanmıştır. Bu niteliğe ait özelliklerden yararlanılarak element adı(konfigurasyon dosyasında görünecek olan isim) ve gereklilik(IsRequired) gibi değerler belirtilebilir.

Özelliklerin get ve set bloklarında fark edileceği gibi this anahtar kelimesinden yararlanılmakta ve üst tipin indeksleyicisine(Indexer) gidilerek değer ataması veya okunması işlemi gerçekleştirilmektedir.

F12 ile ConfigurationSection elementine gidiliğinde bu indexleyici görülebilir.

ccs_6

DefinitionSection sınıfı;

using System.Configuration;

namespace HowTo_WritingCustomConfigSection
{
    public class DefinitionSection
        :ConfigurationElement
    {
        // Save işlemine izin vermesi için false döndürecek şekilde ezdik
        public override bool IsReadOnly()
        {
            return false;
        }

        [ConfigurationProperty("name", IsRequired = true)]
        [StringValidator(InvalidCharacters = "~!@#$%^&*()[]{}/;'\"|\\,çşöğüı")]
        public string Name
        {
            get
            {
                return this["name"].ToString();
            }
            set
            {
                this["name"] = value;
            }
        }

        [ConfigurationProperty("wsdlAddress", IsRequired = true)]
        public string WsdlAddress
        {
            get
            {
                return this["wsdlAddress"].ToString();
            }
            set
            {
                this["wsdlAddress"] = value;
            }
        }
    }
}

DefinitionSection sınıfı aslında bir alt elementtir ve bu sebepten ConfigurationElement sınıfından türetilmiştir. Save operasyonuna cevap verebilmesi için IsReadOnlyözelliği ezilmiştir. Nameözelliğinde ConfigurationProperty dışında StringValidator niteliği de kullanılmış ve kullanılması istenmeyen bir karakter seti belirtilmiştir.

Web.config Bildirimleri

Artık konfigurasyon dosyası içerisinde kullanacağımız serviceConnection section için gerekli tip desteğine sahip bulunmaktayız. Peki web.config dosyası içerisinde bu bildirimleri nasıl gerçekleştirebiliriz?

Bazen 3ncü parti araçları sisteme dahil ettiğimizde, konfigurasyon dosyası içerisine koyacakları elementler için ekstra bildirimler eklediklerine şahit olmuşuzdur. Yani bir şekilde çalışma zamanına, “izleyen config içeriğinde şu tipe ait elementler kullanılabilir” denilebilmelidir. Bunun için configSections elementi içerisinde sectionGroup ve section tanımlamalarını yapmamız yeterli olacaktır. Aşağıda görüldüğü gibi.

<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="serviceConnectionGroup">
    <section
       name="serviceConnection"
       type="HowTo_WritingCustomConfigSection.ServiceConnectionSection"
       allowLocation="true"
       allowDefinition="Everywhere"
     />
    </sectionGroup>
  </configSections>
 <serviceConnectionGroup>
    <serviceConnection type="MSMQ">
      <definition
        name="Some MSMQ Service"
        wsdlAddress="msmq://www.azon.com/someservicequeue/inbox" />
    </serviceConnection>
  </serviceConnectionGroup>
  <system.web>
      <compilation debug="true" targetFramework="4.5" />
      <httpRuntime targetFramework="4.5" />   
    </system.web>
</configuration>

Kural son derece basittir. serviceConnectionGroup, serviceConnection ve definition elementlerinin kullanılabilmesi için bir sectionGroup tanımlaması yapılması yeterlidir. Bu tanımlama içerisinde ki type kısmı ise ConfigurationSection veya ConfigurationSectionGroup türevli tipi işaret etmektedir.

Test Uygulaması

Şimdi dilerseniz basit bir aspx sayfası hazırlayıp section içeriğini ekrana bastıralım ve hatta üzerinde değişiklik yapıp web.config dosyasına kayıt edelim. Senaryonun bu kısmını gerçekleştirmek için aşağıdaki basit aspx sayfasını tasarlayabiliriz.

ccs_4

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="HowTo_WritingCustomConfigSection.Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
   
        <table>
            <tr>
                <td><strong>Service Type</strong></td>
                <td>
                    <asp:DropDownList ID="ddlServiceType" runat="server">
                    </asp:DropDownList>
                </td>
            </tr>
            <tr>
                <td><strong>Definition</strong> <strong>Name</strong></td>
                <td>
                    <asp:TextBox ID="txtName" runat="server" Width="220px"></asp:TextBox>
                </td>
            </tr>
            <tr>
                <td><strong>Definition WSDL</strong> <strong>Address</strong></td>
                <td>
                    <asp:TextBox ID="txtAddress" runat="server" Width="300px"></asp:TextBox>
&nbsp; </td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>
                    <asp:Button ID="btnSave" runat="server"
                        OnClick="btnSave_Click" Text="Save" Width="60px" />
                </td>
            </tr>
            </table>
   
    </div>
    </form>
</body>
</html>

Gelelim kod tarafına. Sayfa yüklenirken serviceSection içeriğini göstermek arzusundayız. Ayrıca Save düğmesine basıldığında, yaptığımız değişiklikleri kaydetmek ve web.config dosyasını güncellemek istiyoruz.

Senaryomuzda exceptional durumları göz ardı ettiğimizi ifade etmek isterim. Örneğin, boş değer geçilmesi, geçeriz bir url bildirimi yapılması vb. Ancak gerçek hayat senaryolarında bu tip veri doğrulama opsiyonlarını da işin içerisine katmanız önem arz etmektedir.

using System;
using System.Configuration;
using System.Web.Configuration;
using System.Web.UI;

namespace HowTo_WritingCustomConfigSection
{
    public partial class Default
        : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                ddlServiceType.DataSource = Enum.GetNames(typeof(ServiceType));
                ddlServiceType.DataBind();

                ServiceConnectionSection scs =
                    ConfigurationManager.GetSection("serviceConnectionGroup/serviceConnection")
                    as ServiceConnectionSection;

                if (scs != null)
                {
                    txtAddress.Text = scs.Definition.WsdlAddress;
                    txtName.Text = scs.Definition.Name;
                    ddlServiceType.SelectedValue = scs.Type.ToString();
                }
            }
        }

        protected void btnSave_Click(object sender, EventArgs e)
        {
            Configuration manager = WebConfigurationManager.OpenWebConfiguration("/");

            ServiceConnectionSection scs =
                manager.GetSection("serviceConnectionGroup/serviceConnection")
                as ServiceConnectionSection;
            if (scs != null)
            {
                scs.Type = (ServiceType)Enum.Parse(typeof(ServiceType),  ddlServiceType.SelectedValue.ToString());
                scs.Definition.Name = txtName.Text;
                scs.Definition.WsdlAddress = txtAddress.Text;

                manager.Save();
            }
        }
    }
}

serviceSection elementinin managed karşılığını elde edebilmek için Configuration veya ConfigurationManager tipinin GetSection metodundan yararlanılmaktadır. Söz konusu metodun dönüşü ServiceConnectionSection tipine dönüştürüldükten sonra ise Type ve Definition gibi özelliklere erişilebilinir. Hatta Definitionözelliği üzerinden Name ve WsdlAddress değerleri de yakalanabilir. Pek tabi Save işleminin gerçekleştirilebilmesi için WebConfigurationManager ile açılan web.config dosyasını işaret eden manager isimli Configuration tipinden yararlanılmaktadır.

Çalışma Zamanı Sonuçları

Artık çalışma zamanına geçebilir ve sonuçları irdeleyebiliriz. Uygulamayı ilk olarak başlattığımızda Page_Laod içerisindeki kodlar devreye girecektir.

ccs_5

Görüldüğü gibi varsayılan olarak belirtilen değerler çekilebilmiştir. Eğer bu noktada tip, ad ve adres bilgilerinde değişiklik yapıp Save düğmesine basılırsa, kod web.config dosyasında da gerekli etkiyi yapacaktır.

Tabi bu senaryoda çok basit bir section içeriği ele alınmış ve yönetilmiştir. Size tavsiyem connectionStrings gibi bir den fazla aynı tipden element içerebilen bir bölüm geliştirmeye çalışmanız olacaktır. Böyle bir senaryoda devreye ConfigurationElementCollection tipi girecektir. Bu örnek senaryoyu geliştirmeye çalışarak kendinizi bu yönde daha ileri bir noktaya taşıyabilirsiniz. Böylece geldik bir makalemizin daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

HowTo_WritingCustomConfigSection.zip (25,98 kb)

WPF Üzerinde Data Binding– Retro Bakış Açısı

$
0
0

retro_car-1590Merhaba Arkadaşlar,

Bizim dünyamızda zaman hızla akar ve eskiler eskide kalıp, yerini yeniler almaya başlar. Her ne kadar uzun ömürlü kavramlar söz konusu olsa da genel itibariyle yazılım dünyası böyledir. Bazen durup geriye bakar, eskiden nasıl yaptığımızı hatırlar, sonra yenisine dönerek bir kıyaslama yaparız. İşte bu yazımızda yıllarca eski stilde geliştirme yapmış klasik bir .Net yazılımcısının gözünden, yenilikçi bir konuya(ki çıkalı da çok çok çoook zaman olmuştur) bakmaya çalışacağız. Buyrun bakalım.

XAML doğduğundan beri gerek WPF(Windows Presentation Foundation), gerek Silverlight, gerek Windows Phone tarafı olsun pek çok yeniliği ve farklı geliştirme bakış açılarını da beraberinde getirmiş oldu. Bu alanlardan birisi de özellikle kontrol odaklı veri bağlama(Data Binding) stratejileri üzerinedir. Bu anlamda pek çok ve farklı veri bağlama tekniğini bulmak mümkün.

Doğruyu söylemek gerekirse yeni nesil veri bağlama işlemleri daha kolay olmasına karşın klasik stilde programlama yapanlara biraz yabancı gelebilmektedir. İşte bu yazımızda klasik bir WindowsForms geliştiricisi olarak, anladığımız kadarı ile DataBinding kavramına giriş yapmaya çalışıyor olacağız. Olayı ilk önce klasik yaklaşım modeline göre ele almakta fayda var. Aslında klasik yaklaşım modelinden yenilikçi XAML modeline geçiş yaparak artıları görmeye çalışacağımızı ifade edebiliriz.

Senaryo

Senaryomuzda WPF tabanlı bir Windows uygulaması söz konusudur. Window kontrolü üzerinde yer alan TextBox, TextBlock gibi kontroller, Product tipinden bir nesne örneğinin özellikleri ile ilişkilendirileceklerdir. Product tipi ilk etapta basit bir POCO(Plain Old CLR Object) sınıfı olarak tasarlanmalıdır. Buna göre çalışma zamanında Product nesne örneklerinde veya kontrol üzerinden yapılan değişikliklerin, karşı tarafa da yansıtılması istenmektedir. Yani senkronizasyon çift taraflı olarak başarılı bir şekilde sağlanabilmelidir.

İlk Tasarım

WPF tabanlı uygulamamızda yer alan MainWindow  nesnesinin ilk hali aşağıdaki gibidir.

<Window x:Class="HowTo_FundementalsOfBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="175" Width="400" Loaded="Window_Loaded_1">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="25"/>
            <RowDefinition Height="25"/>
            <RowDefinition Height="25"/>
            <RowDefinition Height="25"/>
            <RowDefinition Height="25"/>
            <RowDefinition Height="25"/>
        </Grid.RowDefinitions>
        <TextBlock Text="Product ID" Grid.Column="0" Grid.Row="0"/>
        <TextBlock Text="Product Name" Grid.Column="0" Grid.Row="1"/>
        <TextBlock Text="Product Price" Grid.Column="0" Grid.Row="2"/>
        <TextBlockx:Name="txtProductId" Grid.Column="1" Grid.Row="0"/>
        <TextBox x:Name="txtName" Grid.Column="1" Grid.Row="1" Margin="2,2,2,2"/>
        <TextBox x:Name="txtListPrice" Grid.Column="1" Grid.Row="2" Margin="2,2,2,2"/>
        <Buttonx:Name="btnChange" Grid.Column="1" Grid.Row="3" Content="Change" Width="60"
                HorizontalAlignment="Right" Margin="2,2,2,2" Click="btnChange_Click_1"/>
    </Grid>
</Window>

ub_1

Tasarıma göre pencere üzerinde yer alan kontroller görsel olarak konumlandırılmış ve özellikle kod tarafında erişilebilirlikleri için x:Namenitelikleri(attribute) ile zenginleştirilmişlerdir.

Binding Olmadan Önce

Eğer XAML tarafındaki zengin veri bağlama seçeneklerinin olmadığını düşünürsek, bu durumda büyük ihtimalle aşağıdaki gibi bir kodlama gerçekleştiririz.

using System.Windows;

namespace HowTo_FundementalsOfBinding
{
    public partial class MainWindow
        : Window
    {
        Product computer = null;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnChange_Click_1(object sender, RoutedEventArgs e)
        {
            // örnek olarak computer nesne örneğinin ListPrice özelliğinin değeri arttırılır
            computer.ListPrice += 10;
            // computer nesne örneğinin ListPrice özelliğinin yeni değeri, yapılan değişiklik üzerine ilgili kontrolün text özelliğine YENİDEN atanır
           txtListPrice.Text = computer.ListPrice.ToString();
        }

        private void Window_Loaded_1(object sender, RoutedEventArgs e)
        {
            // computer isminde Product tipine ait nesne örneklenir
            computer = new Product
            {
                ProductId = 10934
                , Name = "HP Compaq 1024X"
               , ListPrice = 999
            };

            // örneğe ait özelliklerin değerleri Window üzerindeki kontrollerin Text özelliklerine set edilir.
            txtProductId.Text = computer.ProductId.ToString();
            txtName.Text = computer.Name;
            txtListPrice.Text = computer.ListPrice.ToString();
        }
    }
}

Uygulamanın çalışma zamanına ait örnek bir ekran görüntüsü aşağıdaki gibidir. Düğmeye her basışta ListPrice değeri artacak ve sonuç ilgili TextBox kontrolü içerisine yazılacaktır.

ub_2

Çalışma zamanında üretilen Product nesne örneği değerleri, kontrollerin ilgili özelliklerine kod yardımıyla basitçe atanmaktadır. Aslında ortada bir sorun yoktur. Olmayacaktır da. Bu şekilde de programlamaya devam edilebilir. Ancak XAML tarafında getirilmiş olan yeni nesil veri bağlama opsiyonlarının bazı artıları vardır. Örneğin MVVM(Model-View-ViewModel) gibi desenlere kolayca enjekte olabilirler. Peki ya diğerleri ne olabilir?

En Basit Haliyle Binding

XAML tarafındaki veri bağlama opsiyonlarını en basit haliyle ele aldığımızda aşağıdaki yeni içeriği üretmemiz yeterlidir.

<Window x:Class="HowTo_FundementalsOfBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="175" Width="400" Loaded="Window_Loaded_1">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="25"/>
            <RowDefinition Height="25"/>
            <RowDefinition Height="25"/>
            <RowDefinition Height="25"/>
            <RowDefinition Height="25"/>
            <RowDefinition Height="25"/>
        </Grid.RowDefinitions>
        <TextBlock Text="Product ID" Grid.Column="0" Grid.Row="0"/>
        <TextBlock Text="Product Name" Grid.Column="0" Grid.Row="1"/>
        <TextBlock Text="Product Price" Grid.Column="0" Grid.Row="2"/>
        <TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding Path=ProductId}"/>
        <TextBox Grid.Column="1" Grid.Row="1" Margin="2,2,2,2" Text="{Binding Path=Name}"/>
        <TextBox Grid.Column="1" Grid.Row="2" Margin="2,2,2,2" Text="{Binding Path=ListPrice}"/>
        <Button Grid.Column="1" Grid.Row="3" Content="Change" Width="60"
                HorizontalAlignment="Right" Margin="2,2,2,2" Click="btnChange_Click_1"               
                />
    </Grid>
</Window>

Dikkat edileceği üzere x:Name nitelikleri kaldırılmıştır. Bu bir gereklilik değildir ama senaryomuzun bu kısmında söz konusu değerlere de ihtiyacımız yoktur. Nitekim kod tarafında bu bileşenler erişilmesine ihtiyaç bulunmamaktadır. Önemli olan ProductId, Name, ListPrice isimli Product nesne örneğine ait özellik değerlerinin ilgili kontrollere nasıl bağlandığıdır. Syntax oldukça basittir.

Text = {Binding Path=Name}

Buna göre Text özelliğine X nesneörneğinin Nameözelliğinin değeri basılacaktır.

X Nesne Örneğinin Kim Olduğunu Çalışma Zamanı Nasıl Bilebilir?

Kod bazında bu işlemi gerçekeştirmek için üst XAML kontrollerinden birisinin DataContextözelliğine bir Product nesne örneğini(computer isimli değişken) atamamız yeterlidir.

Üst kontrolün set edilen DataContext özelliğinin işaret ettiği veri kümesi, alt kontroller tarafından da erişilir niteliktedir. Buna göre Grid veya StackPanel gibi bir container kontrolünün DataContext özelliğine veri içeren bir liste bağlanması, içeride yer alan alt bileşenlerin de (ve hatta onların altındakilerin) bu veri kümesi ile çalışabilmesi anlamına gelmektedir. Ve tüm bu bağlantı işlemleri XAML tarafında dekleratif olarak yapılabilmektedir.

using System.Windows;

namespace HowTo_FundementalsOfBinding
{
    public partial class MainWindow
        : Window
    {
        Product computer = null;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnChange_Click_1(object sender, RoutedEventArgs e)
        {
            // örnek olarak computer nesne örneğinin ListPrice özelliğinin değeri arttırılır
            computer.ListPrice += 10;
            // computer nesne örneğinin ListPrice özelliğinin yeni değeri, yapılan değişiklik üzerine ilgili kontrolün text özelliğine YENİDEN atanır
            //txtListPrice.Text = computer.ListPrice.ToString();
        }

        private void Window_Loaded_1(object sender, RoutedEventArgs e)
        {
            // computer isminde Product tipine ait nesne örneklenir
            computer = new Product
            {
                ProductId = 10934
                , Name = "HP Compaq 1024X"
                , ListPrice = 999
            };

            // Tüm Window içeriğindeki XAML kontrollerini computer isimli Product nesne örneğine bağlamış oluyoruz.
            this.DataContext = computer;

            // örneğe ait özelliklerin değerleri Window üzerindeki kontrollerin Text özelliklerine set edilir.
            //txtProductId.Text = computer.ProductId.ToString();
            //txtName.Text = computer.Name;
            //txtListPrice.Text = computer.ListPrice.ToString();
        }
    }
}

Görüldüğü üzere kontrollerin Text gibi alanlarına, computer isimli örneğe ait özellikler doğrudan kod yardımıyla bağlanılmamıştır. Sadece nesne örneğinin oluşturulması ve this ile Window' un DataContextözelliğine set edilmesi yeterli olmuştur.

ub_3

Özellik Değeri Değiştiğinde?

Bir önceki ekran görüntüsüne bakıldığında XAML tabanlı yapılan veri bağlama işleminin sorunsuz çalıştığı düşünülebilir. Ancak küçük bir problem vardır. Düğmeye basarak computer isimli değişkene ait ListPrice değerini arttırdığımızda TextBox kontrolü içerisindeki verinin güncellenmediğine şait oluruz.

ub_4

Demek ki TextBox bileşenini gerçek anlamda Product tipinden nesne örneğine bağlayabilmiş değiliz. Çözümsel yaklaşım olarak, Product nesne örneğinin ilgili özelliklerinde olabilecek değişiklikler sonucunda, ilgili görsel kontrollerin bir şekilde uyarılması ve içeriklerinin güncellenmesi gerektiği düşünülebilir. Yine klasik stilde olaya yaklaşırsak Product tipini şu hale getirmemiz işe yarayabilir.

namespace HowTo_FundementalsOfBinding
{
    public class Product
    {      
        public int ProductId { get; set; }

        private string _name;

        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                txtName.Text = value;
            }
        }
        public decimal ListPrice { get; set; }
    }
}

txtName Nereden Geliyor?

x:Name niteliklerini kaldırmıştık hatırlayacağınız gibi. Hadi bunu geçtik diyelim. Ya Product tipi bir ClassLibrary içerisinde ise ve aslında başka projelerde de kullanılacaksa ve o projelerde TextBox kontrolleri yoksa!? Name değerinin bir WindowsPhone uygulamasında TextBlock içerisine basılması söz konusu iken başka bir XAML bazlı uygulama da farklı bir kontrolde gösterilmesi istenirse...

Demek ki özelliklerde olan değişiklikleri, görsel ortama bildirirken kontrolden, adından, tipinden vs tamamen bağımsız olabilmeliyiz. İşte bu, INotifyPropertyChanged isimli arayüzün(Interface)neden var olduğunun açık bir ifadesidir. Dolayısıyla Product tipinin içeriğini şu hale getirdiğimizi düşünebiliriz.

ub_5

using System.ComponentModel;

namespace HowTo_FundementalsOfBinding
{
    public class Product
        : INotifyPropertyChanged
    {
        private int _productId;

        public int ProductId
        {
            get { return _productId; }
            set
            {
                if (_productId == value)
                    return;
                _productId = value;
                // Dış dünyayı ProductId özelliğinin değerinin değiştiğine dair bilgilendir
                OnPropertyChanged("ProductId");
            }
        }

        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name == value)
                    return;
                _name = value;
                // Dış dünyayı Name özelliğinin değerinin değiştiğine dair bilgilendir
               OnPropertyChanged("Name");
            }
        }

        private decimal _listPrice;
        public decimal ListPrice
        {
            get { return _listPrice; }
            set
            {
                if (_listPrice == value)
                    return;
                _listPrice = value;
                // Dış dünyayı ListPrice özelliğinin değerinin değiştiğine dair bilgilendir
               OnPropertyChanged("ListPrice");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            // Özellik değiştiğine dair bir Event yüklendiyse bunu tetikle
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Buna göre kod tarafında computerörneğine ait özelliklerde bir değişiklik olduğunda, Windowüzerinde veriye bağlanmış olan kontrollerin ilgili içeriklerinin de değiştiği gözlemlenecektir. Diğer yandan tam tersi durumda söz konusudur. Yani kontrol üzerindeki değerlerde bir değişiklik yapıp, odağı farklı bir bileşene kaydırırsak(tab tuşuna basmamız dahi bunun için yeterli olacaktır), bu durumda kontrollere bağlanan nesne örneğinin özellikleri de otomatik olarak güncellenir. Aşağıdaki ekran görüntüsünde bu durum irdelenmiştir.

ub_6

Biraz Revizyon

Product tipine ait özelliklerin set bloklarında değişikliğe neden olan özellik adlarının, OnPropertyChanged metoduna string olarak geçirildiği dikkatinizden kaçmamıştır. Aslında biraz daha güvenli bir yol tercih edilebilir. Yani string tipte özellik adlarını vermekten kurtulabiliriz. Nasıl mı? İşte aşağıdaki kod parçasında görüldüğü gibi.

using System;
using System.ComponentModel;
using System.Linq.Expressions;

namespace HowTo_FundementalsOfBinding
{
    public class Product
        : INotifyPropertyChanged
    {
        private int _productId;

        public int ProductId
        {
            get { return _productId; }
            set
            {
                if (_productId == value)
                    return;
                _productId = value;
                // Dış dünyayı ProductId özelliğinin değerinin değiştiğine dair bilgilendir
                OnPropertyChanged(()=>ProductId);
            }
        }

        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name == value)
                    return;
                _name = value;
                // Dış dünyayı Name özelliğinin değerinin değiştiğine dair bilgilendir
                OnPropertyChanged(()=>Name);
            }
        }

        private decimal _listPrice;
        public decimal ListPrice
        {
            get { return _listPrice; }
            set
            {
                if (_listPrice == value)
                    return;
                _listPrice = value;
                // Dış dünyayı ListPrice özelliğinin değerinin değiştiğine dair bilgilendir
                OnPropertyChanged(()=>ListPrice);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            // Özellik değiştiğine dair bir Event yüklendiyse bunu tetikle
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertySelector)
        {
            var memberExpression = propertySelector.Body as MemberExpression;
            if (memberExpression != null)
           {
                OnPropertyChanged(memberExpression.Member.Name);
           }
        }
    }
}

Dikkat edileceği üzere özelliklerin Set bloklarında yapılan OnPropertyChanged metod çağrılarında, lambda(=>) operatörü kullanılmış ve değer olarak string yerine tipe ait özellik adları gönderilmiştir(Bu noktada Development ortamında intelli-sense özelliğinin de çalıştığını ifade edebiliriz)

Size Düşen

Böylece geldik bir yazımızın daha sonuna. Bu yazımızda veri bağlama(Data Binding) operasyonlarının bir kaç temel noktasını daha iyi bir şekilde anlamaya çalıştık. Size tavsiyem XAML tarafındaki Binding ifadelerinde kullanılabilen diğer özelliklerin ne anlama geldiklerini ve hangi amaçla kullanıldıklarını öğrenmeye çalışmanız olacaktır. Söz gelimi Mode niteliğine OneWay değerinin atanması ile TwoWay verilmesi arasındaki fark nedir? Ya da OneWayToSource ne işe yaramaktadır. Hatta orada Converter’ lar ile ilişkili bir durum da söz konusudur. Converter’ lara neden ihtiyaç duyarız ki?

Bir sonraki yazımızda görüşünceye dek hepinize mutlu günler dilerim.

HowTo_FundementalsOfBinding.zip (77,96 kb)


WCF - Referans Güncellemeden Güncelleme Yapmak

$
0
0

wcfrf_8Merhaba Arkadaşlar,

Bildiğiniz üzere bir servis yazıldığında genellikle bunu tüketen(Consume) en az bir taraf bulunur. İstemci olarak düşündüğümüz bu taraflar her hangi bir uygulama olabilir. WCF(Windows Communication Foundation) ile geliştirdiğimiz bu servislerin, söz konusu istemciler tarafından kullanılması noktasında ise izlenebilecek bir kaç yol bulunmaktadır. Bunlardan en yaygını, servislerin projeye referans olarak eklenmesi ve üretilen Proxy sınıfının kullanılmasıdır(Add Service Reference). Visual Studio gibi gelişmiş IDE’ lerin ilgili arabirimleri, bu noktada büyük kolaylık sağlamaktadır.

İkinci bir yol ise Proxy üretimi zorunluluğu olmadan servislerin kullanılmasıdır. Ancak söz konusu servislerin Web programlama modeline uygun olacak şekilde, HTTP protokolünün ilgili metodlarına(GET,POST,PUT,DELETE…) destek vermek üzere geliştirilmesi gerekir. Daha çok veri odaklı(Data-Centric) servisler için geçerli olan bu senaryoda, içerik de çoğunlukla JSON(JavaScriptObjectNotation), BSON(Binary Javascript Object Notation) ve XML(eXensibleMarkupLanguage) gibiveri formatlarında sunulmaktadır. Bu tip servislerin tüketilmesinde istemcinin her hangi bir Proxyüretimine gereksinimi bulunmamaktadır. Yine de işleri kolaylaştırıcı tiplerden yararlanıldığı da görülmektedir.

Çok doğal olarak her iki kullanım şeklinin de bazı handikapları vardır. Özellikle Proxy bazlı servis kullanımında yaşanan sıkıntılardan birisi, servislerin çeşitli nedenler ile güncellenmeleri sonrasında, istemcilerin de sahip oldukları referansları güncelleme gerekliliği(Update Service Reference) ya da farklı versiyonların nasıl ele alınacağıdır.

Vaka

Ancak burada özel bir vaka vardır. Bazen servis tüketicileri ile servis tarafının geliştiricileri aynı projeye dahil edilmiş ve bir arada çalışan ekiplerdir ve hatta aynı Solutionüzerinde çalışmaktadırlar. Ayrıca buna bir de istemci ve servis tarafının IIS tabanlı birer WebSite projesi olması şartını da eklediğimiz de ortaya farklı bir bakış açısı çıkmaktadır. O da, istemci tarafının servis referansını güncellemeden(ve pek tabi proxy tipi ürettirmeden) çalışabilmesi ve güncellemeleri anında uygulama şansına sahip olabilmesidir.

Bu tip bir vaka genellikle istemci tarafının, servise yeni bir operasyon ilave edilmesini istediği durumlarda ortaya çıkmaktadır. İşte bu yazımızda söz konusu vakayı nasıl gerçekleştirebileceğimizi incelemeye çalışıyor olacağız.

Solution İçeriği

İlk olarak Solution içerisinde aşağıdaki proje iskeletinin söz konusu olduğunu düşünelim.

wcfrf_1

Dikkat edileceği üzere Client ve servisi Host eden uygulamalar aslında birer Asp.Net Web Site olarak yer almaktadır. Bununla birlikte servis sözleşmesi(Service Contract) ile sözleşme implementasyonu ayrı projeler olarak düşünülmüş ve birer WCF Service Libraryşeklinde tasarlanmışlardır.

Projelerin Oluşturulması

Pek tabi servisi host eden uygulama her iki kütüphaneyi de referans etmek durumundadır. İstemci tarafından olaya baktığımızda ise sadece sözleşmeyi(Contract) barındıran kütüphaneye bir referans içerdiği gözden kaçmamalıdır. Bu yaklaşıma göre Solution’ ımızı yavaş yavaş oluşturmaya çalışalım. Bunun için aşağıdaki şekilde görülen ağaç yapısını inşa etmemiz yeterlidir.

wcfrf_2

Karma.HostApp, WCF Service tipinden bir Asp.Net Web Site’ dır(Add New Web Site ile eklerken WCF Service tipi seçilmelidir). Karma.ClientApp, Empty tipinden bir Asp.Net Web Site’ dır(Add New Web Site ile eklerken Asp.Net Empty Web Site seçilmelidir). Her iki Web Site’ de IISüzerinde host edilecek şekilde üretilmişlerdir(Eklerken HTTP tipini seçip IIS altında Application klasörü oluşturulması yeterlidir)  Karma.Contract ve Karma.Implementation’ da birer WCF Service Library’ dir.

Burada senaryo için anahtar nokta Web Site proje şablonunun kullanılmış olmasıdır. Dikkat edilirse, Solution içerisinde yapılacak Build işlemlerinin ardından Web Site projelerinin Bin klasörlerinde, referans edilen dll’ lerin otomatik olarak eklendiği görülecektir.

Servis Tarafının Kodlanması

Şimdi örnek senaryo içerisinde ele alacağımız diğer tipleri de ilave etmeye başlayalım. Karma.Contract kütüphanesinde aşağıdaki servis sözleşmesinin yazıldığını düşünelim.

wcfrf_3

using System.ServiceModel;

namespace Karma.Contract
{
   [ServiceContract]
    public interface IAlgebra
    {
        [OperationContract]
        double Sum(double x, double y);
    }
}

Şu aşamada servis sözleşmesinin içeriği çok da önemli değil. Sadece bir sözleşme olması yeterli senaryo gereği. Bu sözleşmeyi implemente edecek sınıfın konuşlandırılacağı yer ise, Karma.Implementation isimli WCF kütüphanesidir. Bu kütüphane içeriğini de aşağıdaki gibi geliştirdiğimizi düşünelim.

wcfrf_4

using Karma.Contract;

namespace Karma.Implementation
{
    public class Algebra
       :IAlgebra
    {
        public double Sum(double x, double y)
        {
            return x + y;
        }
    }
}

Servis tarafı için gerekli sözleşme(Contract) ve uygulayıcı tip hazır. Çok doğal olarak bu sözleşmeyi bir yerde host ederek sunmalıyız. Bu amaçla projemizde yer alan Karma.HostApp uygulamasına bir WCF Service ekleyeceğiz, ancak sadece svc uzantılı dosyanın durmasına izin vermeliyiz. Bir başka deyişle App_Code klasörü içerisine atılan dosyaları(büyük ihtimalle IService1.cs ve Service1.cs) silmeliyiz. Diğer yandan AlgebraService.svc dosyasının içeriğinin de aşağıdaki şekilde düzenlenmesi gerekmektedir.

<%@ ServiceHost Language="C#" Debug="true" Service="Karma.Implementation.Algebra" %>

Dikkat edileceği üzere ServiceHost elementindeki Service niteliğinin değeri, Karma.Implementation kütüphanesinde yer alan Algebra tipi olarak işaret edilmiştir.

Buraya kadar ki işlemleri tamamladıktan sonra servisin çalışıp çalışmadığının kontrol edilmesinde yarar vardır. Eğer aşağıdaki çıktıyı alabiliyorsak ve WSDLçıktılarına da sorunsuz ulaşabiliyorsa süper!

wcfrf_5

İstemci Tarafının Kodlanması

Gelelim istemci tarafına. Yani Karma.ClientApp uygulamasına. Senaryomuzda belirttiğimiz üzere bu projede her hangi bir şekilde Add Service Reference ile Proxyüretimi söz konusu olmamalıdır/olmayacaktır. Nitekim kurtulmak istediğimiz nokta referans güncelleme işlemleridir. Ancak diğer taraftan istemcinin uygun servis sözleşmesini kullanarak bir şekilde ilgili host uygulamaya talepte bulunabilmesi de gerekmektedir. Bu nedenle istemci tarafında bu kullanımı sağlayabilecek ilave bir sınıfa daha ihtiyacımız vardır. Aşağıdaki kod parçasında olduğu gibi.

using System.ServiceModel;

public classAlgebraServiceClient<T>
    : ClientBase<T>
    where T : class
{
    private bool _disposed = false;
    public AlgebraServiceClient()
        : base(typeof(T).FullName)
    {
    }
    public AlgebraServiceClient(string endpointConfigurationName)
        : base(endpointConfigurationName)
    {
    }
    public T Proxy
    {
        get { return this.Channel; }
    }
    protectedvirtual void Dispose(bool disposing)
    {
        if (!this._disposed)
        {
            if (disposing)
            {
                if (this.State == CommunicationState.Faulted)
                {
                    base.Abort();
                }
                else
                {
                    try
                    {
                        base.Close();
                    }
                    catch
                    {
                        base.Abort();
                    }
                }
                _disposed = true;
            }
        }
    }
}

Aslında ClientBase<T> türevli olarak geliştirilen AlgebraServiceClient, Add Service Reference işlemi sonrası üretilen Proxy sınıfı içerisinde yer alan tiplere benzemektedir. Ancak daha generic bir tip söz konusudur. Özellikle bu tipin konfigurasyon bazlı çalışabilmesi için yapıcı metodlardan birisinde, üst sınıftaki yapıcıya(Constructor) paramete gönderildiğine dikkat edilmelidir.

Tabi istemci tarafında ilgili WCF servisinin kullanılabilmesi için web.config dosyasında da bazı düzenlemelerin yapılması gerekmektedir. Bu sebeple web.config içeriğini aşağıdaki gibi değiştirmeliyiz.

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5"/>
    <httpRuntime targetFramework="4.5"/>
  </system.web>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IAlgebra" closeTimeout="00:01:00"
            openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
            allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
            maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
            messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
            useDefaultWebProxy="true">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
              maxBytesPerRead="4096" maxNameTableCharCount="16384" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost/Karma.HostApp/AlgebraService.svc"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IAlgebra"
          contract="Karma.Contract.IAlgebra" name="BasicHttpBinding_AlgebraService" />
    </client>
  </system.serviceModel>
</configuration>

Bu içeriği oluştururken var olan bir client web.config içeriğinden yararlanmanızı önerebilirim.

Örneğimizde servis tarafında WCF’ in varsayılan konfigurasyon ayarları kullanıldığından, iletişim BasicHttpBindingüzerinden yapılmaktadır. Ancak servis tarafında seçilen EndPoint içeriğine göre istemci tarafındaki tanımlamaların da(Binding gibi) değiştirilmesi gerekebilir.

Test Kodları

Artık servisi kullanmayı deneyebiliriz. Bu amaçla Karma.ClientApp içerisindeki Default.aspx sayfasına basit bir Button ekleyelim ve Click olay metodunda aşağıdaki kodları yazarak geliştirmemize devam edelim.

using Karma.Contract;
using System;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    protected void btnProcess_Click(object sender, EventArgs e)
    {
        using (AlgebraServiceClient<IAlgebra> dynamicProxy =
            new AlgebraServiceClient<IAlgebra>("BasicHttpBinding_AlgebraService"))
        {
            double result=dynamicProxy.Proxy.Sum(3.2, 4.5);
            Response.Write(result.ToString());
        }
    }
}

İstemci uygulama servis sözleşmesini içeren Karma.Contract kütüphanesini referans ettiğinden, IAlgebraüzerinde tanımlı olan Sum metoduna erişebilmektedir. Ancak çalışma zamanında bunun servis tarafına yönlendirilebilmesi, ClientBase<T> türevli AlgebraServiceClient nesne örneğinin kullanılmasına bağlıdır.

Servise Yeni Bir Operasyon Dahil Edilmesi

Peki buraya kadar yazdıklarımız ile asıl hedefimize ulaştık mı dersiniz? Elbette ki hayır. Amacımız; servis tarafında yapılan bir güncelleme sonrasında, istemci tarafının bir Proxy güncellemesine gitmeye gerek duymamasını sağlamaktır. Şimdi bu durumu test etmek amacıyla servis tarafına yeni bir operasyon ilave edildiğini ve bunun uygulandığını göz önüne alalım.

Sözleşme tarafı;

using System.ServiceModel;

namespace Karma.Contract
{
    [ServiceContract]
    public interface IAlgebra
    {
        [OperationContract]
        double Sum(double x, double y);

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

İmplementasyon tarafı;

using Karma.Contract;

namespace Karma.Implementation
{
    public class Algebra
        :IAlgebra
    {
        public double Sum(double x, double y)
        {
            return x + y;
        }

        public double Multiply(double x,double y)
        {
            return x * y;
        }
    }
}

Güzelll! Peki ya şimdi ne olacak? Normal şartlarda Add Service Reference tekniğine göre bir Proxyüretmiş olsaydık, istemci tarafında bu yeniliklerin görülebilmesi/kullanılabilmesi için söz konusu Proxyüzerinde bir güncelleme işlemi yapmamız gerekirdi. Oysa ki bu senaryoda durum biraz daha farklıdır. Bir Build işlemi yaptığımızda, istemci tarafındaki kodlarda, intellisense’ in devreye girdiği yerlerde yeni operasyonun da ele alınabildiğini görürüz.

wcfrf_6

Görüldüğü üzere yapılan yeni güncelleme build işlemi sonrasında istemci tarafında da görülmekte ve etkin olarak kullanılabilmektedir.

Bütün Solution’ ın Build edilmesine gerek yoktur. Sadece istemci uygulamanın refereans ettiği Karma.Contract kütüphanesinin derlenmesi yeterli olacaktır. Bu derleme, güncellemelerin istemci tarafındaki kodlarda kullanılabilmesini sağlayacaktır, nitekim derleme sonrası DLL’ in yeni hali otomatik olarak Web Site’ ın Bin klasörüne yansıyacaktır.

Son Test

Çalışma zamanında ki sonuçlara bakarak testimizi tamamlayalım ve senaryonun çalıştığından emin olalım.

wcfrf_7

Bu senaryoda dikkat edilmesi gereken nokta, Add Service Reference veya Update Service Reference gibi seçeneklerin kullanılmamış olmasıdır. Diğer yandan size düşen görev neden bu senaryo için Asp.Net Web Siteşablonunun tercih edildiğinin bulunmasıdır? Aynı durum Asp.Net Web Application tipleri için söz konusu olamaz mı? Söz konusu olamazsa, iki uygulama şekli arasındaki farklılıklar nelerdir? Yani Web Site’ ın Web Application’ dan farkı nedir? Peki ya bu senaryo Visual Studio 2013 ile gelen yeni nesil web projesi şablonlarında nasıl ele alınabilir? Elbette bu soruları da araştırmak gerekmektedir. Böylece geldik bir makalemizin daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

İpucu : Özellikle Web Site ile Web Application arasındaki farklara hızlıca bir göz atmak için şu adresteki blog girdisinden yararlanabilirsiniz.

Karma.zip (48,65 kb)

Meraklısına NuGet ve NLog ile 5 Dakikada Loglama

$
0
0

Merhaba Arkadaşlar,

Loglama kodlamanın vazgeçilmez unsurlarından birisidir. Ayrıca Enterprise seviyedeki çözümlerde kullanılan önemli CrossCutting' ler arasında yer almaktadır. Uygulamaların çeşitli yerlerinde çeşitli seviyelerde log atma işlemleri sıklıkla icra edilir. Bu işlemler, olası Exception’ ların tespit edilmesi, işleyen süreçlerde hareket eden verilerin tarihsel anlamda izlenmesi, uygulamaların sağlık durumları hakkında bilgi edinip tedbirler alınması, buna bağlı olarak gerekli sistemsel birimlerin uyarılması gibi durumlarda oldukça işe yaramaktadır. Her ne kadar kayıt altına alınacak verilerin ne olacağına karar vermek zor olsa da Loglama çoğu zaman hayat kurtarır. 

Günümüzde loglama özelliği taşımayan bir Enterpriseçözüm görmemiz neredeyse imkansızdır. Ar-Ge ve taşıdığı yazılım prensiplerinin uygulanma şekillerini öğrenmek gibi amaçlar dışında kimse sıfırdan bir Loglama mekanizması geliştirmemektedir. Bunun yerine hazır olarak sunulan açık kaynak kütüphanaler kullanılmaktadır. Hatta elinizin altında NuGet gibi bir paket yönetim aracı varsa, üzerinde çalıştığınız projeye birkaç adımda loglama kabiliyeti kazandırmak oldukça kolaydır. Nasıl mı? Haydi gelin 5 dakikada loglama yapalım.

Not : Güncel NLog sürümünü kontrol edin. Metodların kullanım şekillerinde farklılıklar olabilir ama teori aynıdır.

Asp.Net–Doğru async, await Kullanımı

$
0
0

Merhaba Arkadaşlar,

Bazen web sayfalarının yüklenmesi sırasında senkron olarak çalışan ve uzun süren işlemler gerçekleştiririz(ki aslında Web uygulamalarında bu tip yaklaşımları pek tercih etmeyiz) Sayfada ki kontrollerde gösterilmek üzere çeşitli kaynaklardan veri çekilmesi buna örnek olarak verilebilir. Bu tip veri yükleme işlemleri ağırlıklı olarak Page_Load olay metodu içerisinde gerçekleştirilir. Uzun süren işlemlerin kısa sürede tamamlanabilmesi için farklı teknikler mevcuttur. Bir tanesi de asenkron olarak çalışabilmelerini sağlamaktır(Örneğin zaman kaybettiren servis çağrılarının, veri çekme işlemlerinin eş zamanlı hale getirilmesi)

.Net dünyasında bu tip asenkron işleri kolaylaştıran async, await keyword’ leri artık yaygın olarak kullanılmakta. Lakin Web dünyasında biraz daha dikkatli olmak gerekir. Nitekim bir web sayfasının yaşam döngüsü(Page Life-Cycle),async geri bildirimlerini sorunsuz şekilde ele alan Windows UI Thread’ lerinden biraz daha farklı çalışmaktadır. Güvenilir ve stabil bir ortam söz konusu değildir. Dahası HTTP 503 hatasının alınmasına neden olabilecek vakalar vardır. İşte bu görsel dersimizde bir sayfanın yüklenmesi esnasında asenkron hale getirilmesi istenen işlemlerde uygulanabilecek doğru ve tavsiye edilen bir pratiği incelemeye çalışacağız.

Bir başka görsel dersimizde görüşmek dileğiyle.

Asp.Net–Farklı Ortam, Farklı Config, Farklı Deploy

$
0
0

Merhaba Arkadaşlar,

Bazen geliştirdiğimiz web uygulamaları farklı ortamlar için farklı parametrik değerler kullanır. Bu parametre değerleri çoğunlukla web.config dosyası içerisinde yer alır. Böyle bir durumda ortamlara göre Deployment yapmak zahmetli bir hal alabilir. Nitekim yaygın olarak kullanılan dört farklı ortam söz konusudur. Development, Test, PreProd ve Prod. Her bir ortam için parametreler farklı değerlere sahip olabilir/olması gerekir. Bu yüzden Publish adımlarında, ortamlara göre Profile hazırlanması tercih edilir. Peki bu farklı profiller, config dosyaları içerisindeki çeşitli değerleri (veritabanı bağlantıları, proxy veya servis adresleri, fiziki path bildirimleri vb) ortamlara göre nasıl değiştirebilir? İşte bu görsel dersimizde bu soruya cevap bulmaya çalışıyoruz.

Bir başka görsel dersimizde görüşünceye dek hepinize mutlu günler dilerim.

Y.T.Ü. Finans ve Yazılım Günleri - Finans Sektöründe Nasıl Verimli Yazılımcı Olunur?

$
0
0

Merhaba Arkadaşlar,

Bu sene üst üste 3ncü kez davet edildiğim, Y.T.Ü. Kalite ve Verimlilik Kulübü tarafından düzenlenen Finans ve Yazılım Günleri' nde, "Finans Sektöründe Nasıl Verimli Yazılımcı Olunur?" konulu bir sunum gerçekleştireceğim. İlk sene "Geleceğin Programcısı" konulu bir oturum ile yer almıştım. Önceki sene ise"Gizli Tehlike : Anti Patterns". Bu tip etkinliklerde, katılımcıları teknik açıdan fazla sıkmamak, onlara hayatın içinden bir takım tecrübeleri aktarmak taraftarıyım. İşte bu etkinlikteki sunumumda bu felsefe altında hazırlanmdı. Hatta halen daha hazırlanıyorum. Umarım katılımcılar için değerli bilgiler verebileceğim bir etkinlik olur.

Başkta KVK üyeleri olmak üzere, sevgili Oğuz Çolakoğlu ve KVK' nın yarattığı önemli değerlerden olan çalışma arkadaşım Kahan Akarca' ya sonsuz teşekkürlerimi sunuyorum.

Orada görüşmek dileğiyle...

Viewing all 513 articles
Browse latest View live