ADO.NET kategorisi blog kayıtları
LINQ Many To Many İlişkiler

Kullanırken tanık olmuşsunuzdur, LINQ içerisinde Many To Many ilişkili olan örneğin :

Products

Orders

[Order Details]

gibi üç tabloyu dbml tarafına çektiğinizde, kullanım açısından bir Siparişin bütün ürünlerine ulaşmak için Order_Details koleksiyonunu kullanmak zorunda kalırsınız. Örnek :

var siparis = nv.Orders.Where(o => o.OrderID == 10248).First();

yukarıdaki kod içerisinde Siparişler içerisinden 5 numaralı olan siparişe konumlanıyoruz. Fakat sipariş üzerinden tek seferde Products kayıtlarına ulaşamıyoruz.

siparis.Products gibi bir kod yazma şansımız yok çünkü arada [Order Details] tablosu bulunmakta.

Bunu aşabilmek için Orders ve Products class'ına ufak bir property tanımlamanız yeterli :

public IEnumerable<Order> Orders{

get

{

return Order_Details.Select(od => od.Order);

}

}

yukarıdaki property'yi Product class'ına aşağıdaki property'yi ise Order class'ına ekliyoruz.

public IEnumerable<Product> Products

{

get

{

return Order_Details.Select(od => od.Product);

}

}

Bu işlemin sonucunda artık kod içerisinde aşağıdaki kodu yazarak Many To Many ilişkili class'ları olması gerektiği gibi kullanabiliyorsunuz :

var siparis = nv.Orders.Where(o => o.OrderID == 10248).First();dgProduct.DataSource = siparis.Products.ToList();

veya

var urun = nv.Products.Where(o => o.ProductID == 5).First();dcOrder.DataSource = urun.Orders.ToList();


LINQ çalışma mantığı

LINQ ile çalışırken aşağıdaki gibi bir kodda hata foreach satırında gelecektir

ArrayList arrayList = new ArrayList ();

arrayList.Add(45);

arrayList.Add("26");

arrayList.Add(13);

IEnumerable<int> names = arrayList.Cast<int>();

foreach (int name in names)

Console.WriteLine(name);

Halbuki hatanın IEnumerable<int> iteratörünün oluşturulduğu satırda dönmesi gerektiğini düşünürüz.

Hatanın foreach içerisinde geliyor olmasının sebebi, LINQ'in bu tip işlemlerde sadece hazırlama işlemini uyguluyor ve kodu çalıştırmıyor olmasıdır.

Kodun çalıştırılması foreach satırı ile birlikte gerçekleşecektir. Yani LINQ, eğer sorgunun içerisinde select veya where ifadeleri görmez ise sadece tanımlama yapıp çağırım yapıyor.

Ne zaman sonuc içerisinden bir değere erişilmek istenirse o zaman kod çalıştırılıyor.

Ufak bir dipnot :)


LINQ - Cast ile OfType arasındaki fark nedir ?

What is difference between Cast and OfType operators ?

Bu iki operatör arasındaki fark çok ufak gibi görünse de aslında büyük ve önemlidir.

Cast operatörü verilen koleksiyon içerisindeki tüm elementleri dönüştürmeye çalışır ve dönüştüremediği bir element bulur ise bir exception fırlatır. OfType ise buna karşılık bu elementlerden sadece dönüştürülebilenleri işleme sokar. Örnek olarak aşağıdaki kod foreach satırında hata verecektir.

ArrayList arrayList = new ArrayList();

arrayList.Add(45);

arrayList.Add("26");

arrayList.Add(13);

IEnumerable<int> names = arrayList.Cast<int>();

foreach (int name in names)

Console.WriteLine(name);

 

Ama aşağıdaki kod hata üretmeyecektir ve çıktısı

45

13

olacaktır :)

ArrayList arrayList = new ArrayList();

arrayList.Add(45);

arrayList.Add("26");

arrayList.Add(13);

IEnumerable<int> names2 = arrayList.OfType<int>();

foreach (int name in names2)

Console.WriteLine(name);


ADO.NET Entity Framework Beta 3 yayınlandı...

ADO.NET Entity Framework Beta 3 birçok eklenti ile birlikte yayınlandı. Bu eklentilerin içerisinde Visual Studio 2008 RTM ile görsel modelleme yapabilme ve düzeltilen hatalar, performans iyileştirmeleri gibi birçok detay bulunuyor.

Eklenenler :

Performance improvements

  • Much quicker object query execution
  • Simpler generated SQL
  • Faster view generation

Easier disconnected operation

  • Public, serializable EntityKey property on EntityReference
  • ApplyPropertyChanges
  • Attach on EntityReference
  • Improvements to EntityKey serialization
Extensibility and business logic enhancements
  • Partial methods in code generation for property changing and property changed events
  • Load with MergeOption

Query improvements

  • Additional canonical functions for LINQ to Entities
  • Apply operator elimination (makes more operations work in SQL Server 2000 and other databases)
  • Compiled LINQ query
  • ToTraceString() method on ObjectQuery<T> and EntityCommand to facilitate debugging

ve diğerleri :)

  • Connection management refinements
  • Provider interface allows better reasoning about primitive types

Enterprise Library 4.0 - May 2008

Enterprise Library 4.0 yayınlandı. Enterprise Library sevenlere duyurulur :)

Visual Studio 2008'e özel olması Enterprise Library'nin biraz daha ilgi çekici olmasına sebep oluyor


MARS (Multiple Active Results Sets)

            Makalemizin konusu olan MARS (Multiple Active Results Sets) ADO.NET 2.0 ile gelen yeni ve veritabanı konusunda önemli bir yapıdır. MARS (Multiple Active Results Sets) kısaca tek bağlantı (connection) üzerinden birden fazla sorguyu (command) işleyebilen ve/veya geriye sonuç döndürebilen bir yapıdır.

 

         MARS yapısını daha iyi anlayabilmek için öncelikle ADO.NET 1.0 ve ADO.NET 1.1 içerisindeki connection command nesnelerinin işleyişini ve sorgu (query) işleme yapısını incelemek gereklidir.ADO.NET 1.1 ve öncesinde database ile ilgili bir işlem yapmak istediğimizde her sorgumuz için veritabanına bir bağlantı açmamız ve bu bağlantı üzerinden sorgumuzu işlememiz gerekiyordu ya da aynı bağlantıyı her sorgu için aktif hale getirip tekrar kapatmamız (close) gerekiyordu. Mesela bir console uygulaması yazdığımızı düşünelim. Bu uygulamada Northwind database ? i gibi employees ve orders isminde iki tablodan bir rapor almak istediğimizi varsayalım.

 

EmployeeID             firstName                 lastName

                1                     Nancy                    Davolio

 

Bu bilginin altında da orders tablosundan bu çalışana bağlı Order ları listelemek isteyelim. Orders tablosundan OrderID alanını listeleyelim. Bu raporu ADO.NET 1.1 ve öncesinde yapabilmek için veritabanı ile birden fazla sayıda bağlantı kurmamız gerekirdi. İlk önce employees tablosu için bir command çalışmalı :

 

         Select EmployeeID,firstName,lastName from employees

 

Sorgusunu çalıştırdıktan sonra buna bağlı başka bir sorgu daha çalıştırmalıyız:

 

         Select OrderID from Orders Where EmployeeID = @EmpID

 

Bu sorgu sonuçlarını da ekrana detay bilgi gibi listelemek istediğimizi varsayalım:

Bu durumda iki ayrı bağlantı yardımı ile raporumuzu üretebiliyorduk.Fakat ADO.NET 2.0 ile artık iki ayrı datareader nesnemizi tek connection üzerinden koşturabileceğiz.Yine iki sorgu çalışacak ama tek fark bu iki sorgu tek connection nesnesi üzerinden eş zamanlı olarak çalışacak.

 

 

Örnek olarak bir Windows uygulaması yapalım ve Northwind database ? indeki Products, Categories, Employees, Orders, Shippers tablolarını okuyup formumuzdaki 5 adet listbox kontrolünde listeyelim. Bu işlemide tek baglantı  üzerinden yapmaya çalışalım :    

 

SqlConnection cn;

 

private void btnSorgu_Click(object sender, System.EventArgs e)

{

      cn = new SqlConnection();

      cn.ConnectionString = "data source=.;initial

catalog=Northwind;trusted_connection=SSPI;";

 

      SqlCommand cmd1 = new SqlCommand();

      cmd1.Connection=cn;

      cmd1.CommandText = "Select * From Products";

 

      SqlCommand cmd2 = new SqlCommand();

      cmd2.Connection=cn;

      cmd2.CommandText = "Select * From Categories";

                 

      SqlCommand cmd3 = new SqlCommand();

      cmd3.Connection=cn;

      cmd3.CommandText = "Select * From Employees";

                 

      SqlCommand cmd4 = new SqlCommand();

      cmd4.Connection=cn;

      cmd4.CommandText = "Select * From Orders";

                 

      SqlCommand cmd5 = new SqlCommand();

      cmd5.Connection=cn;

      cmd5.CommandText = "Select * From Shippers";

 

      try

      {

            cn.Open();

            SqlDataReader dr1 = cmd1.ExecuteReader();

            SqlDataReader dr2 = cmd2.ExecuteReader();

            SqlDataReader dr3 = cmd3.ExecuteReader();

            SqlDataReader dr4 = cmd4.ExecuteReader();

            SqlDataReader dr5 = cmd5.ExecuteReader();

            lstProducts.Items.Clear();

            while (dr1.Read())

            {

                  lstProducts.Items.Add(dr1["ProductName"].ToString());

            }

 

            lstCategories.Items.Clear();

            while (dr2.Read())

            {

                  lstCategories.Items.Add(dr2["CategoryName"].ToString());

            }

 

            lstEmployees.Items.Clear();

            while (dr3.Read())

            {

lstEmployees.Items.Add(dr3["firstName"].ToString()+" "+dr3["lastName"].ToString());

            }

 

            lstOrders.Items.Clear();

            while (dr4.Read())

            {

lstOrders.Items.Add(dr4["OrderID"].ToString()+" "+dr4["OrderDate"].ToString());

            }

 

            lstShippers.Items.Clear();

            while (dr5.Read())

            {

                  lstShippers.Items.Add(dr5["companyName"].ToString());

            }

      }

      catch (Exception ex)

      {

            MessageBox.Show(ex.Message,"Error",MessageBoxButtons.OK,MessageBoxIcon.Error);}

      finally

      {

            cn.Close();

      }          

}

 

 

Bu örnek programı çalıştırdığımızda SQL Server tarafından istemci uygulamaya bir istisna (Exception) gönderilecektir. Hata mesajı :

 

?There is already an open DataReader associated with this Connection which must be closed first.?

 

Çünkü sqldatareader nesnesi kendi komutunun çalışması bitene kadar bağlantının açık kalmasını zorunlu koşar ve aynı bağlantıyı baksa bir nesnenin kullanmasına izin vermez.

 

 

Bu programdaki hatayı çözebilmek için ya tek  connection nesnemiz olan cn ? i her sefer açıp kapatmamız gerekir ya da her sqldataReader nesnemiz için ayrı bir connection nesnesi oluşturmamız gerekir.Bu her iki çözümde de karşımıza bir hata mesajı gelmeyecek fakat bu tip kullanımlar bizim sistem kaynaklarımızı fazlasıyla tüketecektir.Şu anda verdiğimiz örnekte bu kaynak tüketimini tam anlamıyla hissedemeyiz fakat öyle bir program düşünün ki kayıt sayısı 5,000,000 dan fazla iki adet tabloyu sorgulamamız gerekmekte. Böyle bir durumda yaratacağımız her bağlantı nesnesi veritabanımızda ayrı bir bağlantı açacak ve sistem kaynaklarını fazlasıyla tüketmeye başlayacaklardır. Ayrıca bunlar eş zamanlı ilerleyemeyeceği için ilk gönderilen komut işlemini bitirmeden diğerine geçilmeyecek tüm komutlar teker teker birbirinin bitmesini bekleyecek ve biz bilgisayarın başında epey zaman harcamak zorunda kalacağız.

 

Fakat artık ADO.NET 2.0 bu ve bunun gibi ihtiyaçlar doğrultusunda yeni bir yapı getirmiştir.Bu yapının ismi MARS (Multiple Active Results Sets) (Asenkron komut yürütme de diyebiliriz). MARS açık bir bağlantı üzerinden birden fazla sql komutunun eş zamanlı olarak çalıştırılmasını sağlar. MARS yapısını kullanabilmemiz için veritabanı sistemimizin bu yapıyı destekliyor olması gerekmektedir.

 

Şimdide MARS yapısına dayanan, ADO.NET 2.0 ile SQL Server 2005 üzerinde çalışan bir program yazalım. Bu programda tek bağlantı nesnesi üzerinden farklı 2 adet komutu nasıl yürüteceğimizi göreceğiz :

 

 

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Data.SqlClient;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

 

namespace MARSOrnegi

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }

 

        private void btnSorgula_Click(object sender, EventArgs e)

        {

            try

            {

                SqlConnection con = new SqlConnection("data source=.;initial catalog=Calisma;uid=****;password=********;MultipleActiveResultSets=true");

 

                SqlCommand cmd1 = new SqlCommand("Select * From Musteriler");

                SqlCommand cmd2 = new SqlCommand("Select * From Urunler");

 

                cmd1.Connection = con;

                cmd2.Connection = con;

 

                SqlDataReader dr1;

                SqlDataReader dr2;

 

                con.Open();

 

                dr1 = cmd1.ExecuteReader();

                dr2 = cmd2.ExecuteReader();

 

                lstMusteriler.Items.Clear();

                while (dr1.Read())

                {

                    lstMusteriler.Items.Add(dr1["Ad"].ToString()+" "+dr1["Soyad"].ToString());

                }

               

                lstUrunler.Items.Clear();

                while (dr2.Read())

                {

                    lstUrunler.Items.Add(dr2["UrunID"].ToString()+" "+dr2["UrunAdi"].ToString());

                }

 

                con.Close();

            }

            catch (SqlException ex)

            {

                MessageBox.Show(ex.Message);

            }

        }

    }

}

 

Yukarıda görmüş olduğunuz kod MARS yapısını kullanarak aynı database içerisindeki iki ayrı tabloyu sorgulayıp kullanıcının karşısına getirir. Bunu yaparken sql komutları birbirinin bitmesini beklemez ve ikiside aynı bağlantı üzerinde koşar. Böylece hem sistem kaynaklarında gereksiz bir tüketimi engellemiş oluruz hemde kullanıcıya istediği bilgiyi daha çabuk döndürmüş ve son olarak da bazen müşterilerimizden çıkan ?Bu program çok yavaş çalışıyor? şikayetinden bir ölçüde kurtulmuş oluruz.

 

      Şimdi gelelim MARS?ın uygulanabilme şartlarına. MARS?ı kullanabilmek için sadece MARS yapısını destekleyen bir veritabanı sistemi ile çalışıyor olmak yeterli. Tabiki ADO.NET 2.0 ?ı unutmamak lazım Visual Studio 2005 bu aşamada sorununuzu çözecektir.Bunlar dışında MARS? tan faydalanabilmek için connectionstring property mize  ?MultipleActiveResultSets=true? ifadesini eklemeyi unutmuyoruz. Bu ifade eklendiği takdirde veritabanı sistemimiz bizim MARS yapısına dayanan bir uygulama geliştirdiğimizi anlıyor ve komutlarımızı buna göre yorumlamaya başlıyor.

 

      Makalemizin en başından beri birçok yerde eş zamanlı çalışmak şeklinde bir ifade geçti. Bunun ne anlama geldiğine bir sonraki makalemizde değiniyor olacağız. Şimdilik herkese iyi günler diliyorum, bir sonraki makalede görüşmek dileğiyle.

 

 

Not: Yukon içerisindeki Calisma isimli databasedeki tablolar

 

Musteriler :

 

[Ad] [varchar](50) COLLATE Turkish_CI_AS NULL

[Soyad] [varchar](50) COLLATE Turkish_CI_AS NULL

 

 

Urunler :

 

[UrunID] [int] IDENTITY(1,1) NOT NULL

[UrunAdi] [varchar](50) COLLATE Turkish_CI_AS NULL

 

 

Bora BURGUCUGİL

E-mail : bora.burgucugil@bilgeadam.com

 

BilgeAdam BTA

Bireysel Yazılım Geliştirme Eğitmeni

Beşiktaş