Çoğu yazılımcının yaptığı gibi "sürekli yapma alışkanlığı"nızdan kurtulun. Aynı kodu defalarca yazamaktan sıkılmadınız mı? Yada her yeni proje ile birlikte aynı şeyleri baştan yazmaya başladığınızı düşünmediniz mi? Tekrarlamaları farketmeye çalışın çünkü bunları otomatikleştirebilirsiniz. En basit örneği olarak bir tabloyu .net class'ına dönüştürme işlemini neden her seferinde baştan yazasınız ki? Yada bu tablo için gerekli CRUD operasyonlarının kullanacağı stored procedure'leri neden tek tek yazmanız gerekiyor? Bu ve bu gibi durumlar için okumaya devam edin.

Nedense bazı şeyleri otomatik yaptırmak tembellikle özdeşleştiriliyor. Bence bu tarz kendime standartlaştırdığım bazı işleri otomatik yapmamın hiçbir zararı yok çünkü bu tür işler çok fazla zamanımı alıyor ve asıl iş gücümü harcamam gereken aşamalar için daha az zamanım kalmış oluyor. Tembellik değil tasarruf edin. Bu noktada code generatorlar iş bitirir. Bu yazıda code smith neler yapabileceği hakkında bilgi vereceğim.

Code smith temelde generate edilecek kaynağın şemalarını barındıran bir programdır. .netTiers, CSLA, NHibernate, PLINQO, Wilson's ORMapper, APOSA ve daha bir çok frameworkle çalışabilecek kodlarınızı kolayca oluşturmanızı sağlar. Uygulamanın öğrenci sürümünü alabilmeniz de mümkün bunun için öğrenci belgenizi göndermeniz de yeterli oluyor. Visual studioya entegre çalışıyor. Tool menusunun altından yada view sekmesinin altındaki CodeSmith Explorer ile ulaşılabiliyor. Varsayınlan olarak tanımlanmış active snippet'ler sahesinde 3-4 kelime yazarak tüm kodu oluşturabilirsiniz.

Bu makalede basit bir veri erişim katmanını inceleyeceğim.Uygulama içinde hazır gelen TableProperties.cst şablonu üzerinde yapacağım geliştirmelerle devam ediyoruz.

TableProperties.cst dosyasını açtığınızda Visual Studio 2003'e çok benzer(aynısı olabilir) kod editörü ile karşılacaksınız. Kodlar şu şekilde:

<%--
Name: Database Table Properties
Author: Paul Welter
Description: Create a list of properties from a database table
--%>
<%@ CodeTemplate Language="C#" TargetLanguage="C#" Debug="False" Description="Create a list of properties from database table." %>
<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="Context" Description="Table that the object is based on." %>
<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %>
<%@ Assembly Name="SchemaExplorer" %>
<%@ Import Namespace="SchemaExplorer" %>

<% foreach (ColumnSchema column in this.SourceTable.Columns) {  %>
private <%= CSharpAlias[column.SystemType.FullName] %> _<%= StringUtil.ToCamelCase(column.Name) %>;

public <%= CSharpAlias[column.SystemType.FullName] %> <%= StringUtil.ToPascalCase(column.Name) %>
{
    get { return _<%= StringUtil.ToCamelCase(column.Name) %>; }
    set { _<%= StringUtil.ToCamelCase(column.Name) %> = value; }
}

<% } %>

Bu kod SourceTable değişkeni ile aldığı değeri şablona göre oluşturuyor. CSharpAlias sınıfınıda tanımlamış durumda. Klasik bir for döngüsüyle işlemi gerçekleştiriyor. Tabiki buna benzer bir uygulamayı kendinizde yazabilirsiniz ancak herşeyi düşünmek gerekiyor oysa burada düşünülmüşü var. Karar size kalmış. İlerleyen yazılarda farklı özellikleride farkettiğinizde codesmith'den kopamayacaksınız.

Şablonu olduğu gibi çalıştırmaya çalıştığımda source table tanımlı olmadığı için hata ile karşılaştım. Sağ taraftaki Properties panelinden source table tanımlamasını yapmaya başlıyorum. CodeSmith daha önce de bahsettiğim gibi birçok farklı data provider'ı destekliyor. Önce data source bilgilerini giriyorum(Add ile). Örnek olarak bir sanal mağaza veritabanını kullanmayı uygun görüyorum. Gerekli tanımlamaların ardından küçük bir tablo ile işe başlayalım. Kategori tablosunu şablonda hiçbir değişiklik yapmadan generate ettim.


private int _kategoriID;

public int KategoriID
{
    get { return _kategoriID; }
    set { _kategoriID = value; }
}

private int _ustKategoriID;

public int UstKategoriID
{
    get { return _ustKategoriID; }
    set { _ustKategoriID = value; }
}

private string _ısim;

public string Isim
{
    get { return _ısim; }
    set { _ısim = value; }
}

private string _resimUrl;

public string ResimUrl
{
    get { return _resimUrl; }
    set { _resimUrl = value; }
}

private bool _yayinDurumu;

public bool YayinDurumu
{
    get { return _yayinDurumu; }
    set { _yayinDurumu = value; }
}

private int _tip;

public int Tip
{
    get { return _tip; }
    set { _tip = value; }
}

Bazı türkçe karakterlerin sorunlu olduğunu farketmişsinizdir. Küçültme fonksionundan kaynaklı tabiki. Kendi fonksiyonunuzu yazıp bu sorunu çözebilirsiniz. Yaşadığım sıkıntıların biriside sonu "s" ile biten tabloların "s" karakterini çoğul olarak düşüp silmesiydi. Şablona geri dönelim. Code-Behind özelliği sahesinde methodlarımı aynı sayfada tanımlayabiliyoruz. Örnek tanımlama:

<script runat="template">

public string DegerGetir(string strSqlDbTipi, int Uzunluk)
{
    if(strSqlDbTipi == "NVarChar") return ", " + Uzunluk.ToString();
    else return "";
}

</script>

runat="template" dikkatinizi çekmiştir. runat="server" tanımlamasının neden yapıldığını düşünüyordunuz?

Türkçe karakterlerin küçültülmesiyle ilgili yaşadığım problemden dolayı bir method yazdım:

public string IlkHarfKucult(string Veri)
{
    if (Veri.Length>=1)
    {
        string tmpVeri = Veri;
        return tmpVeri.Substring(0, 1).ToLower(new System.Globalization.CultureInfo("en-US",false)) + tmpVeri.Substring(1, tmpVeri.Length - 1);
    }

    return Veri;
}

ve şablondaki private kod bloğunu oluşturan blok la değiştirdim.

<% foreach (ColumnSchema column in this.SourceTable.Columns) {  %>
        private <%= CSharpAlias[column.SystemType.FullName] %> _<%= IlkHarfKucult(column.Name) %>;

<% } %>

this.SourceTable.Columns koleksiyonunu farketmişsinizdir. CodeSmith bünyesindeki sqlschemaprovider sahesinde mssql veri tabanındaki tabloyu nesneye dönüştürüyor ve hizmetinize sunuyor. Biraz kurcalandığında ne kadar emek verildiği farkediliyor. Kullanmak isteyebileceğiniz tüm niteliklere ulaşabilirsiniz. Sonraki yazılarımda master templateleri inceyeceğim. Orada da aynı şablonu tek tetikleme ile tüm tablolara uygulatıp sonucu istediğiniz bir klasöre yazdırabiliyorsunuz. Bu sahede standart erişim katmanınızın tamamınını kolayca oluşturabilme imkanınız oluyor.

Tablo isimlerinin sonundaki "s" karakteri yüzünden yaşadığım sıkıntıyı şu method ile düzeltmiş oldum:

public string GetClassName(TableSchema table)
{
    if (table.Name.EndsWith("s"))
    {
        return table.Name.Substring(0, table.Name.Length - 1);
    }
    else
    {
        return table.Name;
    }
}

Böylece class isimleri için tablo adını kullabilecek durumdayım. Biraz kurcalayım property'lerin üzerine karakter uzunluğunu yazmak istedim. Sunum katmanında yapacağım kodlamalar sırasında faydalı olacağını düşünüyorum. Yalnızca string tiplerinine özet bilgisi olarak kaç karakter olduğunu yazdırmak istiyorum. Aşağıdaki method ile işimi gördüm:

public string BilgiGetir(System.Type SistemTipi, int Uzunluk)
{
    if(SistemTipi.FullName == "System.String")
    {
        return "/// <summary>Alan Uzunlugu = " + Uzunluk + "</summary>";
    }
   
    return "";
}

Hemen hemen aynı bir kodu string değerlerinin set'inde girdinin veri tabanına aktarımı sırasında taşmaması için kullandım.

public string DegerGetir2(string strSqlDbTipi, int Uzunluk)
{
    if(strSqlDbTipi == "NVarChar") return "Metin.Ozet(value," + Uzunluk.ToString() + ")";
    else return "value";
}

Şablon kodlaması sırasında codesmith'in bünyesindeki StringUtil sınıfından da faydalanıyorum.

Sıra geldi constructor da atayacağım başlangıç değerlerine. Her SqlDbType için farklı bir değer ataması yapabilmek için column koleksiyonundaki SystemType property'sini kullanmaya karar verdim. Değer alacağım method da tip incelenip uygun bir değerle dönüş yapacak. En sık kullandığım SqlDbType lardan bazılarını tanımladım ve şöyle bir method ortaya çıktı:

public string CTORDegerGetir(Type SistemTipi)
{
    string DonusDeger = "\"\"";
    switch(SistemTipi.Name)
    {
        case "Int16":
            DonusDeger = "0";
            break;
        case "Int32":
            DonusDeger = "0";
            break;
        case "Int64":
            DonusDeger = "0";
            break;
        case "DateTime":
            DonusDeger = "DateTime.MinValue";
            break;
        case "Boolean":
            DonusDeger = "false";
            break;
        case "Decimal":
            DonusDeger = "0m";
            break;
        case "Single":
            DonusDeger = "0f";
            break;
        case "Double":
            DonusDeger = "0d";
            break;
        case "Guid":
            DonusDeger = "Guid.Empty";
            break;
        case "Byte[]":
            DonusDeger = "new byte[] { }";
            break;
        case "Byte":
            DonusDeger = "new byte()";
            break;
        default:
            break;
    }
   
    return DonusDeger;
}

Kodlama neredeyse tamamlandı sıra using deyimlerini eklemeye geldi. Şablonumu kontrol ettim ve oluşabilecek durumlara karşı aşağıdaki kod parçasını şablonumun üstüne ekledim.

using System;
using System.Collections.Generic;
using System.Text;
using Cekirdek.Araclar;

Cekirdek.Araclar namespace'ini Metin.Ozet statik methodunu barındırdığı için yazdım. Son olarak class için namespace bildirisi yapacak şablon kodunuda ekledim ve ilk şablonum bitmiş oldu. Aldığım çıktı:

using System;
using System.Collections.Generic;
using System.Text;
using Cekirdek.Araclar;

namespace OMR
{
    public class Kategori
    {
        #region private ozellikler
        private int _kategoriID;
        private int _ustKategoriID;
        private string _isim;
        private string _resimUrl;
        private bool _yayinDurumu;
        private int _tip;
        #endregion
        #region public ozellikler
         
        public int KategoriID
        {
            get { return _kategoriID; }
            set { _kategoriID = value; }
        }
       
         
        public int UstKategoriID
        {
            get { return _ustKategoriID; }
            set { _ustKategoriID = value; }
        }
       
         /// <summary>Alan Uzunlugu = 100</summary>
        public string Isim
        {
            get { return _isim; }
            set { _isim = Metin.Ozet(value,100); }
        }
       
         /// <summary>Alan Uzunlugu = 255</summary>
        public string ResimUrl
        {
            get { return _resimUrl; }
            set { _resimUrl = Metin.Ozet(value,255); }
        }
       
         
        public bool YayinDurumu
        {
            get { return _yayinDurumu; }
            set { _yayinDurumu = value; }
        }
       
         
        public int Tip
        {
            get { return _tip; }
            set { _tip = value; }
        }
       
        #endregion
        #region constructor
        public Kategori()
        {
            _kategoriID = 0;
            _ustKategoriID = 0;
            _isim = "";
            _resimUrl = "";
            _yayinDurumu = false;
            _tip = 0;
        }
        #endregion
    }

}

Aynı işlemi Visual Studio da "tp tanimli_dbadi.sema.tablo_adi" olarak da çalıştırabilirdim.

Bu işlemlerin ardından yazdığım şablon için ayırdığım zamanın kazanacağım zamana göre önemsiz olduğuna karar verdim ve şablonu tamamlamak için 2 tam günümü karcadım. Sonuç gayet rahatlatıcıydı ve bir daha aynı kodlamaları sıkıcı bir şekilde yazmamın gerekmediğini düşündükçe oldukca rahatladım. Sonraki yazılarda İş zekası katmanı için hazırladığım şablonu inceleyeceğim. CodeSmith içinde halihazırda bulunan stored procedure şablonları hakkında bilgi vermeyi de düşünüyorum.

Sağlıcakla