SQL Server platformunda Typed DataSet kullanmaya alışmış ve tüm alt yapısını SQL Server üzerine kuran bir yazılımcı Oracle Platformuna geçince ne olur? Bir süre sudan cıkmış balık durumda kalır. :) En büyük veri tabanı üreticilerinden biri olan Oracle'ın .Net platformu için SQL Server kadar hızlı ve kolay bütünleşik bir kod geliştirme aracı ne yazık ki yok. VS içinde ki Server Explorer penceresinden Oracle Packet içeriklerini bile göremiyorsunuz. Odp.Net ile gelen Oracle Explorer penceresi ile ancak Oracle veri tabanınızın detaylarını görebilmektesiniz. Fakat el alışkanlığı ile burada ki nesleri sürükleyip Xsd dosyanıza bırakmak istediğinizde nesnelerin taşınamaz olduğunu görürsünüz. Google'da Oracle Typed DataSet üzerine derin araştırmalar yapıp en sonunda Oracle XSD dosyalarını bir sonra ki VS sürümünde destekleyeceğini açıklayan duyurusu ile tüm hayalleriniz yıkılır. Ama iş beklemez çözüm bulunmalı. Peki, Oracle üzerinde Typed DataSet için 'minimum kodlama ile' Data Access Layer nasıl olacak ki?
Önce DataAccesss Layer nasıl olacak? Typed DataSet kullanan programlarda çalışma mantığı: Veriyi çek, kullanıcıya işlettir ve veri tabanını System.Data.DataAdapter sınıfı ile güncelle şeklindedir. Typed Data Set kullanıyorsanız DataAccess Layer System.Data.DataAdapter nesnesinin bir uygulamasını içermek zorundadır. SQL platformunda bunu TypedDataSetGenerator yaptığı için problem yok. Oracle platformu için çözüm üretelim:
İş ihtiyacımız nedir? Oracle Packetler içinde olan Store Procedure'leri kendi projemiz içinde çağırabilmek ve Typed DataSet üzerinde kullanmak. (Zaten diğer Oracle nesnelerine VS içinden erişilmekte.) Tüm bunları sürekli aynı kodları tekrar etmeden yapmalıyız.
Çözüm: Kod tekrarı yapmamak için Generic Data Access Layer oluşturmak. İhtiyacımız olan tüm Store Procedure'leri Typed DataTable sınıflarına attribute olarak atamak ve Generic Data Access Layer üzerinde bu attributeleri kullanarak gerekli işlemleri yerine getirmek.(Bir önceki konuda olduğu gibi bu konuda da Attribute sınıflar yardıma koşuyor.) Her zaman ki gibi kodu en üst seviyede yazmalıyız. Sürekli kendisini tekrar eden kodlama yerine en üst seviyede bir defa kod yazmak. Böylece DataAdapter nesnesine olan bağımlılı tek yerden ve kesin bir şekilde ortadan kaldırabiliriz.
Önce Store Procedure'leri tanımlayacağımız Attributeleri yazalım:

Store Procedure Attribute sınıflarımızı Typed DataTable sınıflarımıza ekleyelim.
[cs]
[SelectProcedure("ACME.PCK_TEST.PRC_GET_ALL_TEST", "refcursor P_TEST:", DefaultSelect = true ) ]
[SelectProcedure("ACME.PCK_TEST.PRC_GET_TEST_BY_ID","ID1",DefaultSelect=false)]
[InsertProcedure("ACME.PCK_TEST.PRC_INSERT_TEST", "NAME1", "VALUE1", "out P_ID1 : Int32 id1")]
[UpdateProcedure("ACME.PCK_TEST.PRC_UPDATE_TEST", "NAME1","VALUE1","ID1")]
[DeleteProcedure("ACME.PCK_TEST.PRC_DELETE_TEST","ID1")]
partial class TEST1DataTable
{
}
Neden birden çok Select Procedure var? DataAdapter nesnesi her bir kayıtın RowState özelliğine göre kendisine ait InsertCommand, UpdateCommand, DeleteCommand fonksiyonlarında biri kullanılarak veriyi güncelleyecektir. Bu sebebten dolayı sadece birer tane Insert, Update ve Delete Store Procedure eklenmesine izin verilmiştir. Birden cok SelectCommand gerekecektir. Böylece artık Typed DataTable nesneleri Oracle Packet üzerinde ilişkili oldukları Store Procedure'leri bilmektedir. DataTable nesneleri için artık bir Generic DataAdapter nesnesileri oluşturabiliriz. Veri şemasını biliyoruz, veriyi veri tabanında işleyecek Store Procedure'leri biliyoruz o halde Data Access Layer üzerinde sürekli aynı kodları yazmanın gereği var mıdır? En tepeye Generic bir Data Access Layer yazar ve tüm Logic içerisinde bu sınıfı kullanırız. (İp ucu: Yerinizde olsam Base Logic sınıflarınıda Generic yapardım. Bu sayede bütünleşik ve tamamen Generic bir çatınız olur. Projelerinizi minumum kodlama ile bitebilirsiniz.
IDataService ile genel veri tabanı etkileşim ihtiyaçları ortaya konmuştur.

Bu işlemleri gerçekleştiren OracleDataService sınıfıdır ki kendisi projede tek başına Data Access Layer olmaktadır. OracleDataService sınıfı kendisine tip parametresi olarak verilen Typed DataTable nesnesinin StoreProcedure Attribute'lerini almakta ve Odp.Net ile gelen OracleDataAdapter nesnesini bu Attribute'lere göre oluşturmaktadır.
[cs]
StoreProcedureAttribute[] sps = (StoreProcedureAttribute[])table.GetType()
.GetCustomAttributes(typeof(StoreProcedureAttribute), false);
foreach (StoreProcedureAttribute att in sps)
{
if (att is InsertProcedure)
InitInsertProcedure(att.ProcedureName, att.Paramters);
else if (att is UpdateProcedure)
InitUpdateProcedure(att.ProcedureName, att.Paramters);
else if (att is DeleteProcedure)
InitDeleteProcedure(att.ProcedureName, att.Paramters);
else if (att is SelectProcedure)
InitSelectProcedure(att.ProcedureName, att.Paramters,
(att as SelectProcedure).DefaultSelect );
else
InitCustomProcedure(att.ProcedureName, att.Paramters);
}
Odp.Net OracleDataAdapter nesnesine ait Commandları oluştur:[cs]
private Oracle.DataAccess.Client.OracleCommand
InitStoreProcedure(string procedureName, string[] parameters)
{
Oracle.DataAccess.Client.OracleCommand command =
new Oracle.DataAccess.Client.OracleCommand();
command.Connection = _connection;
command.CommandType = System.Data.CommandType.StoredProcedure;
command.CommandText = procedureName;
foreach (string parameter in parameters)
{
command.Parameters.Add(InitParameter(parameter));
}
return command;
}
Kullanımı gayet basittir: [cs]
Demo.Framework.Generic.Data
.OracleDataService
_dataAccessLayer = new Demo.Framework.Generic.Data
.OracleDataService();
public Form1()
{
InitializeComponent();
_dataAccessLayer.FillAll(dataSet1.TEST1);
}
private void tEST1BindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
_dataAccessLayer.UpdateTable(dataSet1.TEST1);
}

Değişken parametrele Store Procedure çağrılarını gene OracleDataService sınıfını kullanarak yapabilirsiniz.