CLR Destekli User Defined Type Yazmak
SQL Server 2005 in CLR ile çalışmabilmesi sonucu, CLR Destekli diller ile Stored Procedure ler, Trigger lar Kullanıcı Tanımlı Fonksiyonlar ve Kullanıcı Tanımlı tipler oluşturabilirsiniz. Sizin için önemli olan tarafı, daha önceki makalelerdede değindiğimiz gibi, farklı bir dil, farklı bir platform öğrenmek zorunda olmadan kullanıdığınız .Net dili ile bu yapıları oluşturabilmenizdi, Stored Procedure ve Trigger ları inceledik, şimdi sırada ise Kullanıcı tanımlı tipler var.
SQL Server 2000 de kullanıcı tanımlı tipler i oluştururken varolan tiplerden faydalanıp bir tip oluşturabiliyorduk, ancak şimdi tamamen .Net yapılarını kullanarak bir Structure ın SQL Server 2005 de kullanılacak bir tip olmasını sağlayabilirsiniz. Bu işlemi yaparkende tamamen .Net platformunu, .Net yetenekleri ve kolaylıklarını kullanabilirsiniz.
Daha önceki makalelerdede belirttiğim gibi benim kullandığım platform, SQL Server 2005 CTP (SQL Server 9.0.1116) ve Visual Studio 2005 Beta 2 ve bu platform da işlerinizi çok daha kolaylaştıracak ve hızlandıracak yapılar var. Örneklerimizi yapmak için, Visual Studio 2005 i açıp, Create Project diyoruz daha sonra Visual Basic altından Database i oradanda uygulama tipi olarak SQL Server Project i seçiyoruz ve isim olarakta YazGelistirOrnek giriyoruz, Tamam a bastığımızda karşımıza Add Database Reference ekranı geliyor, bu ekranda hangi veri kaynağına bağlanmak istediğimiz soruluyor, buradan varolan bir veritabanını seçebilirsiniz yada Add New Reference butonuna basıp yeni bir veri kaynağı ekleyebilirsiniz, biz örneklerimiz için daha önceden hazırlamış olduğumuz YazGelistirOrnek isimli veritabanını kullanacağız. Yeni oluşturduğumuz projeye sağtuş ile tıklayıp Add menüsünden User-Defined Type ı seçiyoruz isim olarakta Nokta veriyoruz. Visual Studio 2005 tarafından bize bir şablon tip tanımlaması oluşturuluyor, şablon aşağıdaki şekilde,
Imports System
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
<Serializable()> _
<Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native)> _
Public Structure Nokta
Implements INullable
Public Overrides Function ToString() As String
' Put your code here
Return ""
End Function
Public ReadOnly Property IsNull() As Boolean Implements INullable.IsNull
Get
' Put your code here
Return m_Null
End Get
End Property
Public Shared ReadOnly Property Null As Nokta
Get
Dim h As Nokta = New Nokta
h.m_Null = True
Return h
End Get
End Property
Public Shared Function Parse(ByVal s As SqlString) As Nokta
If s.IsNull Then
Return Null
End If
Dim u As Nokta = New Nokta
' Put your code here
Return u
End Function
' This is a place-holder method
Public Function Method1() As String
' Put your code here
Return "Hello"
End Function
' This is a place-holder static method
Public Shared Function Method2() As SqlString
' Put your code here
Return New SqlString("Hello")
End Function
' This is a place-holder field member
Public var1 As Integer
' Private member
Private m_Null As Boolean
End Structure
Dikkat ederseniz, ihtiyacım olabilecek bütün yapıları Visual Studio 2005 şablon olarak bana oluşturdu, ben sadece kendi ihtiyacım olabilecek ekstra yapıları ekleyip, bunların kullanılmasını sağlayacağım. Visual Studio 2005 in benim için hazırladığı yapıları inceleyelim, daha önceki makalelerden farklı olarak bu sefer benim için bir Structure oluşturuluyor. Yani benim tipim bir class değil Structure, ismi ise dosyaya isim verirken yazdığım Nokta. Namespace ler için daha önceden yazdığım makaleleri inceleyebilirsiniz, Attribute lerde Serializable ve SqlUserDefinedType Attribute leri mevcut, SqlUserDefinedType a parametre olarak verilen Format, Seriileştirme işleminin nasıl yapılacağını anlatıyor, Structure mıza, INullable Interface i Imlement edilmiş durumda, Structure lar value tipleri olduklarından, Null tutamazlar, INullable Interfacesi Null değer tutulmasını sağlar (Yazılım Mimarisi kategorisindeki Nullable Tipler ile ilgili makalemden daha detaylı bilgi edinebilirsiniz) birisi Public diğeri Private olmak üzere iki tane değişken tanımlanmış durumda, private olan değişken, Structure ın Null mı değilmi bilgisini tutuyor, diğeri ise sizin yazacağınız Tipin değerini null olarak yollayacak yapı. SQL Server da Allow Null gibi bi özellik olduğundan, yazdığınız Tipin Null değer alabilmesi çok önemli. İki tane ReadOnly property mevcut, bu property ler Null değerler ile ilgili bir tanesi Structure ın tuttuğu değer Null mı diye bakıyor, diğeri ise Null değeri atanmış, bir Nokta döndürüyor. Bununla birlikte 4 tanede Fonksiyon var, bu fonksiyonlardan özellikle iki tanesi işimize çok yarayacak, bunların isimleri ToString ve Parse, bunula birlikte iki tanede örnek metot mevcut, bu örnek metotlar sadece size şablon olarak verilmiş durumda, şimdi kendi yazıcağımız tipe bakalım, aşağıdaki kodları inceleyin lütfen.
<Serializable()> _
<Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native)> _
Public Structure Nokta
Implements INullable
Public Property proX() As Integer
Get
Return Me.memX
End Get
Set(ByVal value As Integer)
Me.memX = value
End Set
End Property
Public Property proY() As Integer
Get
Return Me.memY
End Get
Set(ByVal value As Integer)
Me.memY = value
End Set
End Property
Public Overrides Function ToString() As String
Return Me.memX & "," & Me.memY
End Function
Public ReadOnly Property IsNull() As Boolean Implements INullable.IsNull
Get
Return m_Null
End Get
End Property
Public Shared ReadOnly Property Null() As Nokta
Get
Dim h As Nokta = New Nokta
h.m_Null = True
Return h
End Get
End Property
Public Shared Function Parse(ByVal s As SqlString) As Nokta
If s.IsNull Then
Return Null
End If
Dim u As Nokta = New Nokta
Dim varKoordinat() As String = s.ToString().Split(",".ToCharArray())
u.proX = Int32.Parse(varKoordinat(0))
u.proY = Int32.Parse(varKoordinat(1))
Return u
End Function
Public Function funSifiraUzaklik() As SqlDouble
Return CType(Math.Sqrt(Me.memX ^ 2 + Me.memY ^ 2), SqlDouble)
End Function
Private memX As Integer
Private memY As Integer
Private m_Null As Boolean
End Structure
Benim Nokta tipimde özellikle grafiksel uygulamalar için kullanılabilecek bir yapı mevcut, yani kullanıcı değer olarak Nokta tipini girebilir, böylece String ile uğraşmak yerine direk Nokta olarak tanımladığı tipi kullanabilir, yazdığım kodda Null olabilmek ile ilgili kodları bıraktım, çünkü o bölüm zaten düzgün olarak çalışıyor. Bunun yanında iki tane değişken ekledim, memX ve memY değişkenleri, bu değişkenler dışarıdan değer alabilsinler diye iki tane property tanımladım, ve kullanıcı örneğin INSERT işlemini yaparken, virgül “,” ile ayrılmış veriyi girdiğinde, Parse() Metodunu otomatik olarak çalışıp, bunları benim Structure ım daki bu tanımladığım değişkenlere atayacak, peki ya okuduğu zaman, okuduğu zaman ise otomatik olarak ToString() Metodu çalışacak ve benim Structure ım içindeki değişkenleri okuyup, onları Virgul ile birleştirip String olarak geri yollayacak. Bunun yanında, yaptığım işlemleri biraz daha pekiştirmek için bu noktanın “0,0” yani merkeze olan uzaklığını bulması amacıyla funSifiraUzaklik() isimli bir metot yazdım, eğer isterse kullanıcı ekstra hiçbir işlem yapmadan, bu noktaların sıfıra olan uzaklıklarınıda bulabilecek. Şimdi yazdığımız bu kodu nasıl kullanacağımızı inceleyelim, kodunuzu Build edin ve daha sonra Deploy edin. SQL Server Management Studio da aşağıdaki şekilde bir görünüm elde etmiş olmalısınız.

Şimdi bir tablo oluşturalım, bu tabloda da tanımladığımız Nokta tipini kullanalım, Tabloyu çok basit, aşağıdaki şekilde oluşturabilirsiniz.
CREATE TABLE [dbo].[NoktaTablosu](
[NoktaId] [tinyint] IDENTITY(1,1) NOT NULL,
[NoktaKoor] [dbo].[Nokta] NULL,
CONSTRAINT [PK_NoktaTablosu] PRIMARY KEY CLUSTERED
(
[NoktaId] ASC
) ON [PRIMARY]
) ON [PRIMARY]
Gördüğünüz gibi, NoktaKoor isimli sütun, Nokta tipinden tanımlanmış durumda, peki ben ekleme işlemini nasıl yapacağım, ilk iş olarak SQL Server Management Studio dan ekleyelim, Tabloya sağ tuş işe tıklayın ve Open Table a basın. İlk önce 3,5 koordinatını gireceğim, daha sonra ise hatalı girişe izin vermediğini göstermek için 3:6 şeklinde gireceğim ve şu hatayı alacağım,

Peki ya INSERT cümleciği ile nasıl eklemeliydim,
INSERT INTO NoktaTablosu VALUES('3,7')
Yukarıdada belirttiğim gibi, Parse() Metodu benim için atamanın yapıldığı yer, bu metot SqlString alıyor ve geriye Nokta döndürüyor. Bende bir String verdim ve Parse() Metodundan sonra geriye X ve Y koordinatları atanmış bir Nokta döndü.
Şimdi bu eklediğimiz noktaları nasıl çekeceğimize, kısacası SELECT cümleciğimize bakalım, yine yukarıda belirttiğim gibi eğer SELECT yapacaksak, ToString() metodu çalışacak bu Metot ta geriye String döndürüyor, SELECT cümleciğinizi şöyle yazabilirsiniz,
SELECT NoktaId,NoktaKoor.ToString() FROM NoktaTablosu
Son olarakta eklediğim ekstra metodu inceleyelim, funSifiraUzaklik() Metodunu, bu Metot Koorinatları buluyor ve geriye SqlDouble döndürüyor, böylece küsüratlı verileride tutabiliyorum, kullanımına bakalım,
SELECT NoktaKoor.funSifiraUzaklik() FROM NoktaTablosu
Dahada gelişmiş bir işlem yapabilirsiniz, örneğin tablomda kayıtlı olan bütün Noktaların sıfıra olan uzaklıklarının ortalmasını alabilirsiniz,

Bu makale SQL Serverdaki CLR ile birlikte gelen yeniliklerden biri olan User Defined Type ları inceledik.
Levent Cenk ÇAĞLAR