LINQ to SQL یک بستر Object-Relational Mapping ایجاد می کند که اجازه می دهد یک mapping یک به یک بین MS SQL و کلاس های دات نت ایجاد شود . به طور خلاصه LINQ to SQL برای نرم افزارهایی که به روش Rapid Development پیاده سازی می شوند و احتیاج به کار و استفاده از بانک اطلاعاتی دارند بسیار مفید و باعث تسهیل در کار با بانک اطلاعاتی می شود . در این مدل امکان پرس و جو از بانک اطلاعاتی ، افزودن ، حذف و ویرایش اطلاعات به راحتی فراهم است . عملیات کار با بانک اطلاعاتی بوسیله ترجمه پرس و جوهای language integrated به sql برای اجرا در بانک اطلاعاتی و سپس ترجمه خروجی SQL به آبجکت های داخل زبان برنامه نویسی انجام می شود .
DataContext مجرای اصلی دریافت اطلاعات از بانک اطلاعاتی و فرستادن تغییرات به آن است . روش استفاده از آن به همان روشی است که در ADO.NET از Connection استفاده می کردید . هدف استفاده از DataContext ترجمه درخواست هایی است که به آبجکت ها می رسد به زبان SQL ، سپس ترجمه جواب بانک اطلاعاتی به Object ها . DataContext اجازه می دهد عملگرهایی که در LINQ استفاده می کنیم مشابه عملگرهای استاندارد SQL شوند ، مانند Where . برای استفاده از DataContext به روش ADO.NET Connection می بایست آدرس فایل بانک اطلاعاتی را به آن فرستاد . روش دیگر استفاده از بخشی است که در Visual Studio برای این کار فراهم شده و بسیار ساده تر از روش دستی است .
برای ادامه احتیاج به یک بانک اطلاعاتی داریم . لذا با بانکی ساده شامل جداولی که اطلاعات محصولات و گروه بندی آنها ، مشتریان و خرید آنها را نگهداری می کند شروع می کنیم . در شکل زیر دیاگرام جداول را مشاهده می کنید .

جداول بالا حاوی اطلاعات زیر است :
جدول گروه بندی محصولات

جدول محصولات

جدول مشتریان

سپس در Visual Studio در قسمت Server Explorer در Data Connections یک اتصال جدید به بانک اطلاعاتی ایجاد می کنیم .

وقتی بانک اطلاعاتی به لیست اضافه شد امکان مشاهده جداول آن فراهم می شود .

حالا در Solution Explorer روی نام پروژه کلیک سمت راست را زده و یک فایل LINQ to SQL Classes با نام ProductSales ایجاد می کنیم .

در Solution Explorer روی ProductSales.dbml دبل کلیک می کنیم تا باز شود . سپس هر یک از جداولی که در Connection جدید وجود دارد را به شکل drag and drop روی فایل dbml قرار می دهیم .

شکل بالا دیاگرام mappingی است که کلاس های مورد نیاز در نرم افزار از بانک اطلاعاتی ایجاد شده. اگر توجه کنید نام جداول نسبت به بانک اطلاعاتی کمی تغییر کرده. نام ها در بانک اطلاعاتی به شکل جمع انگلیسی بود ولی در اینجا به شکل جمع نیست . این سبک در نام ها هنگام به کار گیری کلاس ها از ابهام آنها جلوگیری می کند . البته با دبل کلیک بر روی هر کدام از نام ها امکان تغییر آن وجود دارد . درباره property های این کلاسها در dbml در پست های بعدی مطالبی خواهید خواند .
بر روی یک فرم ، یک DataGridView و یک button قرار می دهیم مانند شکل زیر :

در رویداد Click در Button کد زیر را می نویسیم .
Dim db As New ProductSalesDataContext
Dim ProCats = From pc In db.ProductCategories _
Select pc
DataGridView1.DataSource = ProCats
ProductSalesDataContext db = new ProductSalesDataContext();
var ProCats = from pc in db.ProductCategories
select pc;
dataGridView1.DataSource = ProCats;
خط اول یک نمونه از DataContext می سازد . در خط بعدی تمامی رکوردهای موجود در جدول ProductCategories را از بانک اطلاعاتی می خواند .
این ساده ترین شکل استفاده از LINQ to SQL است.

حالا یک button جدید با نام افزودن به فرم اضافه کرده و در رویداد Click آن کد زیر را می نویسیم .
Dim db As New ProductSalesDataContext
Dim pc As New ProductCategory With {.Name = "cat 1"}
db.ProductCategories.InsertOnSubmit(pc)
db.SubmitChanges()
ProductSalesDataContext db = new ProductSalesDataContext();
ProductCategory pc = new ProductCategory { Name = "cat 1"};
db.ProductCategories.InsertOnSubmit(pc);
db.SubmitChanges();
اگر دوباره کلید دریافت را بزنیم رکورد جدید نیز نمایش داده می شود .

در خط اول یک نمونه از DataContext ساخته شده سپس یک ProductCategory می سازیم و آن را به ProductCategories اضافه می کنیم . آن تغییر نام که در نام کلاس ها ایجاد شده بود و از جمع بودن (ProductCategories) خارج شده بود اینجا خود را نمایان می کند .
برای ویرایش یک button دیگر روی فرم اضافه کرده و کد زیر را در آن می نویسیم .
Dim db As New ProductSalesDataContext
Dim pc = (From p In db.ProductCategories Where _
p.Name =
"cat 1" _
Select p).SingleOrDefault
If Not pc Is Nothing Then
pc.Name =
"Cat 1+1"
db.SubmitChanges()
End If
ProductSalesDataContext db = new ProductSalesDataContext();
var pc = (from p in db.ProductCategories
where
p.Name ==
"cat 1"
select p).SingleOrDefault ();
if (pc != null)
{
pc.Name =
"Cat 1+1";
db.SubmitChanges();
}
در خط دوم سعی می کنیم رکوردی را که نامش cat 1 است را پیدا کنیم . خروجی این درخواست از بانک اطلاعاتی می بایست یک رکورد باشد یا هیچ باشد . اگر cat 1 وجود نداشت هیچ رکوردی بازگردانده نمی شود و اگر وجود داشت فقط یک رکورد لذا از SingleOrDefault استفاده شده است .
در بخش آخر هم اگر رکورد مورد نظر ما وجود داشت مقدار آن را تغییر داده و تغییرات را به بانک اطلاعاتی می فرستیم . اگر دوباره button دریافت را بزنیم اطلاعات جدید نمایش داده می شود

یک Button دیگر برای حذف به فرم اضافه کرده و کد زیر را در رویداد کلیک آن می نویسیم :
Dim db As New ProductSalesDataContext
Dim pc = (From p In db.ProductCategories Where _
p.Name =
"Cat 1+1" _
Select p).SingleOrDefault
If Not pc Is Nothing Then
db.ProductCategories.DeleteOnSubmit(pc)
db.SubmitChanges()
End If
ProductSalesDataContext db = new ProductSalesDataContext();
var pc = (from p in db.ProductCategories
where
p.Name ==
"cat 1"
select p).SingleOrDefault ();
if (pc != null)
{
db.ProductCategories.DeleteOnSubmit (pc);
db.SubmitChanges();
}
تنها تفاوت با ویرایش در خط قبل از Submit است .
حالا یک فرم دیگر به پروژه اضافه کرده و یک کلید و یک DataGridView به آن اضافه می کنیم . در رویداد کلیک برای کلید کد زیر را می نویسیم .
Dim db As New ProductSalesDataContext
Dim Ps = From p In db.Products _
Select p
DataGridView1.DataSource = Ps
ProductSalesDataContext db = new ProductSalesDataContext();
var ps = from p in db.Products select p;
dataGridView1.DataSource = ps;
در این کد تمام رکورد های جدول محصولات دریافت می شود ، مانند شکل زیر :

البته ما نیازی به همه فیلدها نداریم . نام ، هزینه و گروه محصول کافی است . برای این منظور از کد زیر استفاده می کنیم .
Dim db As New ProductSalesDataContext
Dim Ps = From p In db.Products _
Select New With {.Name = p.Name, .Price = p.Price, .CatName = p.ProductCategory.Name}
DataGridView1.DataSource = Ps
ProductSalesDataContext db = new ProductSalesDataContext();
var Ps = from p in db.Products select new { Name = p.Name, Price = p.Price, CatName = p.ProductCategory.Name};
dataGridView1.DataSource = Ps;
و شکل زیر خروجی را نمایش می دهد :

در جدول فروش تعدادی رکورد به روش دستی اضافه کردم که شامل فروش فقط از دو محصول می شود . اگر بخواهیم در کنار لیست محصولات ، فروش آنها را هم ببینیم می توان از کد زیر استفاده کرد .
Dim db As New ProductSalesDataContext
Dim Ps = From p In db.Products _
Select New With {.Name = p.Name, .Price = p.Price, .CatName = p.ProductCategory.Name _
, .Foroosh =
Aggregate F In p.Factors Into Sum(New Decimal?(F.Total))}
DataGridView1.DataSource = Ps
ProductSalesDataContext db = new ProductSalesDataContext();
var Ps = from p in db.Products select new { Name = p.Name, Price = p.Price, CatName = p.ProductCategory.Name ,
Foroosh = p.Factors.Sum(a => (
decimal?)a.Total) };
dataGridView1.DataSource = Ps;

با توجه به اینکه بین جداول رابطه برقرار است می توانیم مستقیماً فاکتورهایی که به مربوط به هر محصول است را بازیابی کنیم . از تابع Aggregate برای جمع آوری اطلاعات از جدول فاکتورها و قرار دادن آن در فیلد فروش استفاده شده . خروجی این تابع از نوع Decimal? است . Decimal? به این معنی است که یا خروجی Decimal است یا Null . اگر از New Decimal? استفاده نکنیم در زمان اجرا خطا رخ خواهد داد زیرا تعداد زیادی از محصولات فروخته نشده اند و عددی برای جمع قیمت فروش آنها قابل استخراج نیست .
حالا می خواهیم سیمان به یکی از مشتریان بفروشیم ! یک button جدید ایجاد کرده و از کد زیر استفاده می کنیم .
Dim db As New ProductSalesDataContext
Dim p = db.Products.Single(Function(c1) c1.Name = "سیمان")
Dim c = db.Customers.Single(Function(c2) c2.Name = "حامد بنایی")
Dim f As New Factor With {.Customer = c, .Product = p, .Quantity = 1, .Total = p.Price}
db.Factors.InsertOnSubmit(f)
db.SubmitChanges()
ProductSalesDataContext db = new ProductSalesDataContext();
var p = db.Products.Single(c1 => c1.Name == "سیمان");
var c = db.Customers.Single(c2 => c2.Name == "حامد بنایی");
Factor f = new Factor {Customer = c , Product = p , Quantity = 1 , Total = p.Price };
db.Factors.InsertOnSubmit(f);
db.SubmitChanges();
از جدول محصولات سیمان را انتخاب کردیم . از جدول مشتریان حامد بنایی انتخاب شده و در جدول فاکتورها اضافه شده اند . اگر ID مربوط به مشتری و محصول را می دانستیم به جای .Customer = c می توانستیم از .CustomerID = 123 که 123 شماره آن مشتری است استفاده کنیم .
نتیجه بعد از اجرای این دستورات :

البته شاید ما بخواهیم همیشه بیشتر از یک عدد از هر محصول خریداری شود ! یک راهش این است که در کد هنگام افزودن به فاکتور این کار را انجام دهیم . راه دیگر را در کد زیر می بینید .
Partial
Public Class Factor
Private Sub OnQuantityChanging(ByVal value As Integer)
If value <= 1 Then
Throw New Exception("Bishtar bekhar")
End If
End Sub
End
Class
از آنجایی که تمام جدول های ما به کلاس ها تبدیل شده اند می توان همه را به شکلی که در بالا می بینید تغییر داد . هر کدام از فیلدهای جدول نیز دارای OnChanging هستند که اگر یک خطا در آن برای مقدار نا معتبر رخ دهد همان کاری را خواهد کرد که می خواستیم .
کد های سی شارپ این پست را طی هفته آینده به همین مطلب اضافه می کنم . انواع مثال های مربوط به LINQ را در مطلب جدیدی اضافه خواهم کرد .
منابع :
http://msdn.microsoft.com/en-us/library/bb425822.aspx
http://msdn.microsoft.com/en-us/library/cc161164.aspx
http://www.asp.net/LEARN/linq-videos