.NET comments

.NET ile kod yazarken en çok kullandığım collection tiplerinden biri olan Dictionary Generic yapısı ile de bizi bir çok casting işleminden kuratarıyor. Key olarak şu tipi, value olarak da şu tipi kullanacağız diyoruz ve key/value çiftlerini tiplerini de koruyarak tutabileceğimiz dört başı mamur bir listemiz olmuş oluyor. Bir de bu Dictionary tipimizi XML olarak Serialize edebilsek tadından yenmeyecek ama nedendir bilinmez bunu yapamıyoruz. Yaparsak da şuna benzer bir Exception alıyoruz:

“The type System.Collections.Generic.Dictionary is not supported because it implements IDictionary.”

İnternette bu işi yapabilecek bir şeyler araştırdım ve hatrı sayılır sitede bu sorunun farklı farklı çözümlerine rastladım. Bunlardan en çok Pete Welter’in çözümü hoşuma gitti. Bu çözümde Pete Welter, Dictionary generic tipini ve IXmlSerializable tipini implement eden yeni bir tip yaratmış. Dictionary’den dolayı yeni tipimiz Dictionary ile beraber gelen tüm özelliklere sahip, oh ne güzel. IXmlSerializable tipinin gerektirdiği metodları da implement ederek XML Serialization işlemi sırasında yeni tipimizin nasıl serialize edileceğini de bildirmiş sevgili Pete Welter. Sonuç olarak temiz ve çok rahat kullanılabilir yeni bir tip ortaya çıkmış. Kod şu şekilde:

[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue>
    : Dictionary<TKey, TValue>, IXmlSerializable
{
    #region IXmlSerializable Members
    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }
 
    public void ReadXml(System.Xml.XmlReader reader)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
 
        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();
 
        if (wasEmpty)
            return;
 
        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            reader.ReadStartElement("item");
 
            reader.ReadStartElement("key");
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();
 
            reader.ReadStartElement("value");
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();
 
            this.Add(key, value);
 
            reader.ReadEndElement();
            reader.MoveToContent();
        }
        reader.ReadEndElement();
    }
 
    public void WriteXml(System.Xml.XmlWriter writer)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
 
        foreach (TKey key in this.Keys)
        {
            writer.WriteStartElement("item");
 
            writer.WriteStartElement("key");
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();
 
            writer.WriteStartElement("value");
            TValue value = this[key];
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();
 
            writer.WriteEndElement();
        }
    }
    #endregion
}

Dictionary nesnemiz için serialize edilebilen key ve value tipleri seçtiğimiz sürece SerializableDictionary tipimiz de serialize edilebilir. Key/value tipleri olarak string kullandığımız bir SerializableDictionary nesnesini serialize ettiğimizde şöyle bir çıktıya sahip oluyor:

<dictionary>
  <item>
    <key>
      <string>foo</string>
    </key>
    <value>
      <string>bar</string>
    </value>
  </item>
</dictionary>

Buraya kadar her şey güzel, Dictionary yerine bu yeni tipi kullanarak varolan Dictionary yapılarımızı XML olarak da serialize edebiliriz artık. Ama bir şey içime sinmiyor hala, XML çıktısındaki etiketlerin isimlerini SerializableDictionary üzerinde hard-coded olarak yazıyoruz. Kökteki etiket ismini XmlRootAttribute’e farklı bir parametre göndererek veya SerializableDictionary tipini extend eden yeni bir tip tanımlayarak değiştirebiliriz. Ama hala alt seviyelerdeki item, key ve value şeklindeki etiket isimlerini hard-code etmek dışında bir seçeneğimiz görünmüyor. Eğer bu sınıfı tek tipteki bir Dictionary verimiz için kullanacaksak pek bir sorun yok ama farklı farklı XML çıktılarını beklediğimiz generic bir çözüm için bu tipi kullanmayı planlıyorsak bu etiket isimlerine müdahale etmenin daha generic bir yolunu bulmamız gerek (copy/paste yapmayı bir çözüm olarak aklımızdan bile geçirmiyoruz tabii ki). Bu amaçla SerializableDictionary tipini biraz değiştirerek şu kodu elde ettim:

public abstract class SerializableDictionary<TKey, TValue> 
    : Dictionary<TKey, TValue>, IXmlSerializable
{
    protected abstract string itemName { get; }
    protected abstract string keyName { get; }
    protected abstract string valueName { get; }
 
    #region IXmlSerializable Members
    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }
 
    public void ReadXml(XmlReader reader)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
 
        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();
 
        if (wasEmpty)
            return;
 
        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            reader.ReadStartElement(itemName);
 
            reader.ReadStartElement(keyName);
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();
 
            reader.ReadStartElement(valueName);
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();
 
            this.Add(key, value);
 
            reader.ReadEndElement();
            reader.MoveToContent();
        }
        reader.ReadEndElement();
    }
 
    public void WriteXml(System.Xml.XmlWriter writer)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
 
        foreach (TKey key in this.Keys)
        {
            writer.WriteStartElement(itemName);
 
 
            writer.WriteStartElement(keyName);
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();
 
            writer.WriteStartElement(valueName);
            TValue value = this[key];
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();
 
            writer.WriteEndElement();
        }
    }
    #endregion
}

Override edilmek üzere 3 tane abstract property ekleyerek hard-coded string değerlerinden kurtulmuş olduk. Tipimize abstract üyeler eklediğimiz için artık bu tipi direk olarak kullanılamaz hale getirdik ve bir abstract tipimiz oldu. Tipimizi kullanmak istediğimizde yamamız gereken kod artık şu şekilde olacak:

public class Fields : SerializableDictionary<string, string>
{
    protected override string itemName
    {
        get { return "Field"; }
    }
 
    protected override string keyName
    {
        get { return "Name"; }
    }
 
    protected override string valueName
    {
        get { return "Value"; }
    }
}

Burada az önce değiştirdiğimiz SerializableDictionary tipini extend ederek kullanacağımız yeni tipi yaratıyoruz. Bu aşmada Key/Value tiplerimizi ve XML çıktısında oluşacak etiketlerin isimlerini belirtiyoruz. Eğer kök XML etiketi için kullanılacak ismin tipimizin isminden farklı olmasını istiyorsanız yeni tipin üzerinde XmlRootAttribute kullanarak bunu da belirtebilirsiniz. Bu tipimizi de artık şu şekilde kullanabiliriz:

// 2 kayıt içeren bir Dictionary oluşturalım
// .NET 3.5/VS2008 ile gelen Collection Initializers özelliği
// bu gibi durumlarda daha temiz/kısa kod yazmamıza yarıyor
Fields fields = new Fields {
    { "foo", "bar" },
    { "hede", "hödö"}
};
 
// XML Serialize işlemi için gerekli altyapıyı oluşturalım
StringBuilder sb = new StringBuilder();
 
// Burada da bir başka yenilik olan Object Initializer özelliğini
// kullanarak nesneyi oluşturduğumuz satırda nesne üzerindeki
// bazı özelliklere de ilk değerlerini verebiliyoruz
XmlTextWriter xtw = new XmlTextWriter(new StringWriter(sb)) 
    { Formatting = Formatting.Indented };
xtw.WriteRaw("");
 
// Burada XML Serialize işlemi gerçekleşiyor
XmlSerializer xs = new XmlSerializer(typeof(Fields));
xs.Serialize(xtw, fields);
 
string xmlFields = sb.ToString();
 
Console.WriteLine(xmlFields);
 
// Burada da daha önce serialize ettiğimiz nesneyi deserialize ederek
// tekrar hafızda bir nesne haline çeviriyoruz
Fields deserializedFields = xs.Deserialize(new StringReader(xmlFields)) as Fields;

Bu kodun çıktısı da şu şekilde:

<Fields>
  <Field>
    <Name>
      <string>foo</string>
    </Name>
    <Value>
      <string>bar</string>
    </Value>
  </Field>
  <Field>
    <Name>
      <string>hede</string>
    </Name>
    <Value>
      <string>hödö</string>
    </Value>
  </Field>
</Fields>

SerializableDictionary tipini bir kere tanımlayarak projemiz içerisinde bunu baz alan farklı farklı Dictionary tiplerimizi oluşturabilir, bunların farklı farklı XML çıktıları olmasını sağlayabiliriz.

.NET comments

Visual Studio 2008

Daha önce Kasım ayında çıkacağı duyurulan Visual Studio 2008 ve .NET Framework 3.5′in RTM sürümleri bugün duyuruldu. MSDN Subscription sahipleri bugün itibariyle bu ürünü indirebilir. Eğer 90 günlük deneme sürümünü indirmek istiyorsanız şu sayfaya gidip Team System Edition’larını indirebilirsiniz. Burada daha “Visual Studio 2008 Professional Edition” için download linki hazır değil ama 1 haftaya kadar o da çıkacakmış. Tamamen ücretsiz olan Express sürümlerinin 2008 versiyonları da indirilebilir durumda. .NET Framework’ün 3.5 internetten kurulan versiyonunu (2.7 MB) ve tekrar dağıtılabilen (offline) versiyonunu (197.1 MB) da Microsoft Downloads’dan indirebilirsiniz. .NET Framework 3.5 ayrıca 2.0 ve 3.0 versiyonlarının SP1′lerini de içermekte.

.NETİpucu comments

Geçenlerde bir videoda ?? şeklinde bir C# operatörünün kullanımına şahit oldum. Başta .NET 3.5 ile gelen yeni özelliklerden biri gibi gelse de bu operatör aslında .NET 2.0′dan beri varmış da haberimiz yokmuş. Videodaki kodun gelişinden operatörün ne amaçla kullanıldığı açıktı; operatörün solunda bulunan operandın (operandın Türkçe’si işlenenmiş bu arada) değeri null değilse işlem sonucu bu (soldaki) operand, null ise de sağdaki operand dönüyor. Şu şekilde:

string a = null;
string b = "foo";
string c = a ?? b;

kodu çalıştırıldığında c değişkeni a null değerine sahip olduğu için b’nin değeri olan “foo” değerine sahip oluyor. İşimizi bir çok sefer kolaylaştıran diğer bir operatör ?: Ternary operatöründen bile daha okunabilir bir kod sağlıyor. Ternary ile bunu şu şekilde yazabilirdik:

string a = null;
string b = "foo";
string c = a != null ? a : b;

Hele bir de if kullansaydık bu işlem için bu kadar daha kod yazmamız gerekecekti:

string a = null;
string b = "foo";
 
if (a != null)
    c = a;
else
    c = b;

Operatörü iç içe geçmiş bir şekilde kullanırsak bir kaç değişken arasından ilk null olmayanını bulabiliriz:

string a = null;
string b = null;
string c = "foo";
string d = a ?? b ?? c; // d = "foo"

Operatörün MSDN sayfasına şuradan ulaşabilirsiniz.

Bu operatör aslında JavaScript yazarken kullandığımız **   ** operatörü ile aynı işi yapıyor; ilk operand null değilse onu null ise sonrakini döndür.
var foo = "bar";
var baz = null;
alert(foo || baz);

.NET comments

ASP.NET’in varsayılan programlama yaklaşımı Web Forms‘a bir türlü ısınamıyorum, ısınamayacağım da. Oysa ki klasik ASP (3.0) ne güzeldi, bir Request bir de Response nesnesi yetiyordu. Tamam, sayfanın yenilenmesi gibi durumlarda form değerlerini olması gereken yerlere doldurma hammalığı programcıya kalıyordu, ama ASP.NET’in <form runat=”server”> mantığı da pek hoş değildi. Binbir türlü yol (postback, viewstate, vs.) ile Microsoft mühendisleri web programcıların daha az kod ile hızlı bir şekilde nam-ı diğer Web Form Uygulamaları yazmalarını sağlıyordu. Bu çabaların hakkını vermek lazım, zira ASP.NET ile web programlama öyle bir duruma gelmişti ki Visual Studio ortamında masaüstü uygulama yazmaktan pek bir farkı kalmamıştı. Asıl amaç da bu değil miydi zaten, Visual Studio ile yıllardan beri aşina olan Microsoft teknolojileri odaklı programcıların bu yeni web programlama yaşam döngüsüne hızlı bir şekilde uyum sağlamaları. İyi güzel de bir şey unutuldu gibi geliyor bana, yazılan uygulamalar web uygulamaları olacaktı. Yani masaüstünden tamamen ayrı bir ortamda hayatlarını sürdüreceklerdi. Öyle bir ortam ki sunucu tarafını ayrı düşünmek, istemci (browser) tarafını ayrı düşünmek ve ona göre kod yazmak gerekiyordu. Web’in eti-kemiği olan HTTP protokolünü ve HTML dilini sular seller gibi bilmesek de bir formu GET veya POST ile göndermenin farkını, hangi etiketin nerede kullanıldığında daha anlamlı olacağını bilmek bir web programcısının boynunun borcu olsa gerek.

Oysa ki ASP.NET’in arkasında muazzam bir framework, BCL (Base Class Library) var. Klasik ASP’de olan Request, Response, Session, vs. nesneleri bunda yok mu; var tabii ki hem de çok daha zengin, çok daha kullanılabilir. Bana göre tek yanlış olan bunların üzerine kurulan ASP.NET Web Forms yapısı. MonoRail isimli proje ile RoR’daki web programlama mantığı .NET ortamına getirilmeye çalışılıyor. Bunun ile bir proje geliştirmeye fırsatım olmadı ancak muhtemelen Web Forms mantığından daha uygulanabilir olacağını tahmin ediyorum.

İşte bu noktada ScottGu geçtiğimiz haftalarda ALT.NET konferansında Microsoft tarafından geliştirilmekte olan ASP.NET MVC Framework’unu tanıttı. Daha üzerinde çalışmalar süren bu frameworkun bu yıl sonuna doğru genel önizleme (“public preview” demek istiyorum :) ) sürümü geliştiricilere bir CTP olarak sunulacak. .NET 3.5′e dahil olmayacak bu framework, aynı ASP.NET AJAX (Asynchronous Javascript And XML) (daha önce Atlas olarak da bilinen) gibi ASP.NET üzerine ek bir framework olarak dağıtılacak. 2008′in ilk yarısında da son halinin çıkması planlanıyor.

ASP.NET MVC ile “Merhaba Dünya”

Orjinal duyuruyu ScottGu’nun blogunda; ayrıntılı bilgiyi, blog camiasından konu ile ilgili linkleri ve ScottGu’nun tanıtım videosunu (izlemeniz şiddetle tavsiye olunur) ScottHa’nın blogunda bulabilirsiniz. Konu ile ilgili (blogunu yeni keşfettiğim) Muhammed Tahiroğlu’nun yazısını da okumanız tavsiye ederim.