سلام با آموزش برنامه نویسی سی شارپ قسمت 13 در خدمت شما عزیزان هستیم, در قسمت قبلی با خواندن داده از SQL Server در C# آشنا شدیم. با ادامه آموزش سی شارپ همراه ما باشید.
لينك قسمت اول : آموزش #C
در دروس قبلی آموزش پایگاه داده در سی شارپ، با اصول کار با پایگاه داده و عملیات پایه روی داده ها آشنا شدید. حال می خواهیم به روشی دیگر برای کار با داده ها بپردازیم که کاربردهای فراوانی داشته و بسیاری از مواقع کارایی و بازدهی بالاتری نسبت به روش های معمول دیگر دارد.
در این درس، چگونگی کار با داده های آفلاین با استفاده از اشیای DataSet و SqlDataReader را توضیح خواهیم داد. اهداف این درس به شرح زیر می باشند:
در درس 11 به طور مفصل چگونگی ارتباط و تعامل با یک Data Source با استفاده از شی SqlCommand توضیح داده شد. در درس 12 نیز آموختیم که چگونه داده ها را با به کارگیری شی SqlDataReader به سرعت بخوانیم. در این درس خواهیم آموخت که چگونه با استفاده از شی DataSet و شی SqlDataAdapter، ارتباطی بین SqlConnection و SqlDataReader برقرار نماییم.
دیتاست DataSet یک منبع داده ایِ مقیم در حافظه است که می تواند جداول فراوانی را در خود جای دهد. DataSet ها صرفا نگهدارنده ی داده ها می باشند و هیچگونه تعاملی با منبع داده (Data Source) ندارند. از طرفی، SqlDataAdapter اتصال با منبع داده (Data Source) را مدیریت می کند و رفتارهایِ آفلاین را به ما ارایه می دهد. شی SqlDataAdapter تنها زمانی که نیاز باشد، اتصال (Connection) را برقرار نموده و به محضِ اتمام کار خود، آن را قطع می نماید. برای مثال، زمانی که SqlDataAdapter یک DataSet را از داده ها پر می کند، SqlDataAdapter وظایف زیر را انجام می دهد:
دیتا SqlDataAdapter همچنین با توجه به تغییراتِ DataSet، عملیات زیر را به هنگام به روزرسانیِ داده ها انجام می دهد:
بین عملیاتِ Fill و Update، اتصال های منبع داده بسته شده و شما می توانید آزادانه و در صورت نیاز، داده ها را با DataSet بخوانید و یا بنویسید. اینها مکانیزم های کار با داده های آفلاین می باشند. به دلیل اینکه تنها در صورت نیاز اتصال(Connection) برای اپلیکیشن ها برقرار می شود، بنابراین عملیات ساده و انعطاف پذیر می شود.
سناریوهای زیر لزوم کار با داده های آفلاین را نشان می دهند: افراد بدون اتصال شبکه به فعالیت خود ادامه می دهند و این امر موجب انعطاف پذیری بیشتر وب سایت ها می شود. فروشنده ای را در نظر بگیرید که در طول سفر خود نیازمند دسترسی به داده های مشتریان خود می باشد. در ابتدای روز، این فروشنده باید برای دستیابی به آخرین اطلاعاتِ در دسترس، با پایگاه داده اصلی همگام سازی نماید. ممکن است در طول روز اطلاعات موجود و مربوط به مشتریان را تغییر دهد، یا اینکه مشتریان جدید را به لیست خود اضافه کند، و یا ترتیب های جدید را وارد نمایند. به دلیل اینکه وی دارای یک پایگاه مشتری بوده که امکان دسترسی به آن برای بقیه امکان پذیر نمی باشد، این می تواند یک فرایند مطلوب باشد. در پایان روز، فروشنده به شبکه متصل شده و برای پروسه ی شبانه، تغییرات را به روز رسانی می نماید.
سناریوی بعدی عبارت است از ایجاد قابلیت کار و سادگی بیشتر وب سایت. با وجود SqlDataAdapter، مجبورید هر زمان که یک پیج را نمایش می دهید، برای دستیابی به رکوردها به پایگاه داده برگردید. این کار نیازمندِ یک اتصال (Connection) جدید به هنگامِ بارگذاریِ هر صفحه می باشد که با بالا رفتن تعداد کاربران، موجب آسیب رسیدن به Scalability (مقیاس پذیری) می شود. یک راه برای ممانعت از این آسیب، استفاده از DataSet ی می باشد که قبلا به روز رسانی شده و در cache ذخیره شده است. هر درخواستی برای صفحه، در صورتِ عدمِ وجود، cache را جست جو کرده و داده ها را بارگذاری می کند؛ و یا اینکه صرفا داده ها را از cache بیرون کشیده و آن ها را نمایش می دهد. این کار موجب می شود که نیازی به مراجعه به پایگاه داده نبوده، و کل عملیات را کارآمدتر می نماید.
موارد استثناء برای سناریوهای بالا شامل موقعیت هایی می شوند که در صدد هستید داده ها را به روز رسانی نمایید. سپس بسته به اینکه در استراتژی شما چگونه از داده ها استفاده می شود، باید تصمیم گیری کنید. زمانی که اطلاعات اصولا تنها خواندنی (read only) می باشند، از داده های آفلاین استفاده نمایید. موقعیت های دیگر، از قبیل استفاده از شی SqlCommand برای به روز رسانی سریع، را در نظر بگیرید که در این سناریوها نیازهای شما مستلزم فرآیندهای پویاتری می باشند. همچنین، اگر حجم داده به اندازه ای بزرگ است که نگهداری آن در حافظه عملا کاری غیر عملی است، برای داده های فقط خواندنی (read only) به شی SqlDataReader نیاز خواهید داشت. در واقع، استثنائاتِ فراوانی وجود دارد اما نیرویِ هدایت کننده ی اصلی عبارت است از شناخت نیازهای اپلیکیشن که بر چگونگی طراحیتان تاثیرگذار خواهد بود.
نکته ی خاصی درباره ی نمونه سازیِ یک شی DataSet وجود ندارد. صرفا درست مانند هر شی دیگری، یک نمونه ی جدید را ایجاد می کنید.
DataSet dsCustomers = new DataSet();
ساختارِ DataSet نیازمندِ هیچگونه پارامتری نمی باشد. با این وجود، اگر در صدد تبدیل موازی رشته های کد شده (Serialized) داده ای به XML می باشید، یک بار اضافه وجود خواهد داشت که یک رشته را برای نام DataSet می پذیرد. در کد بالا DataSet خالی می باشد و برای بارگذاری آن به یک شی SqlDataAdapter نیاز است.
دیتا SqlDataAdapter در بر دارنده ی دستورات SQL و شی اتصال (Connection Object) برای خواندن و نوشتن داده ها می باشد. این شی با دستورِ select مربوط برای بارگذاری داده ها و شی اتصال شروع می شود:
SqlDataAdapter daCustomers = new SqlDataAdapter("select CustomerID, CompanyName
from Customers", conn);
کد بالا یک SqlDataAdapter جدید به نام daCustomers را ایجاد می کند. دستورِ select تعیین می کند که کدام داده ها در DataSet خوانده می شوند. تا اینجای کار شی اتصال (Connection Object) باید ایجاد شده باشد که هنوز باز نشده است. در طول فراخوانی متد های Fill و Update، این وظیفه ی SqlDataAdapter است که اتصال (Connection) را برقرار نماید.
همانطور که قبلا اشاره شد، شی SqlDataAdapter شاملِ تمامیِ فرمان های مورد نیاز برای تعامل با منبع داده (Data Source) می باشد. کد بالا نشان می دهد که چگونه می توان دستور select را تعیین نمود، اما چیزی در مورد دستوراتِ insert، update، و delete ارایه نداده است. این دستورات، پس از نمونه سازی، به شی SqlDataAdapter اضافه شده اند.
دو روش برای اضافه کردن دستورهای insert، update، و delete وجود دارد: از طریق Property های شی SqlDataAdapter و یا از طریق یک شی SqlCommandBuilder. در این درس در صددیم که روش ساده ی اضافه کردن این دستورها توسط شی SqlCommandBuilder را توضیح دهیم. در درس آتی، چگونگیِ به کار گیریِ ویژگی های SqlDataAdapter را بحث خواهیم نمود و نشان می دهیم که این شی، با وجود اینکه کار بیشتری را می طلبد، اما توانمندی های افزون تری در مقابل شی SqlCommandBuilder برای شما فراهم می آورد. در کد زیر، چگونگیِ افزودن فرمان توسط SqlCommandBuilder به شی SqlDataAdapter نشان داده شده است:
SqlCommandBuilder cmdBldr = new SqlCommandBuilder(daCustomers);
توجه داشته باشید که در کد بالا، به سازنده شی SqlCommandBuilder آرگومان daCustomers ارسال شده است. این فرایند به SqlCommandBuilder می گوید که شی SqlDataAdapter چه دستوراتی را باید اضافه نماید. شی SqlCommandBuilder دستورِ select را، به ویژه زمانی که SqlDataAdapter نمونه سازی شده باشد، می خواند و همچنین فرمان های insert، update، و delete را استنباط کرده، و به ترتیب دستورهای جدید را برای Property های (ویژگی های) Insert، Update، و Delete تخصیص می دهد.
همانگونه که قبلا اشاره شد، SqlCommandBuilder دارای محدودیت هایی می باشد. زمانی که یک دستور ساده ی select را بر روی یک جدول مجزا انجام می دهید، این شی به کار گرفته می شود. با این وجود، زمانی که نیاز است دو یا چند جدول را join نماییم، و یا زمانی که بخواهید یک پروسه ی ذخیره شده را انجام دهید، این شی کارایی نخواهد داشت. در درس های آینده در رابطه با این سناریوها، توضیحاتی ارایه خواهد شد.
زمانی که یک DataSet و نمونه هایی از SqlDataAdapter را در اختیار دارید، باید DataSet را پر(fill) نمود. در کد زیر، با استفاده از متدِ Fill مربوط به SqlDataAdapter، چگونگیِ انجام این کار نشان داده شده است:
daCustomers.Fill(dsCustomers, "Customers");
متدِ Fill در کد بالا، در بردارنده ی دو پارامتر است: یک DataSet و نامِ جدول. قبل از اقدام به پر نمودنِ DataSet توسط داده ها، می توان آن را نمونه سازی کرد. دومین پارامتر، نام جدولی است که قرار است در DataSet ساخته شود. می توان جدول را با هرچیزی که تمایل داشت، نامگذاری نمود. هدف این آپشن این است که در آینده می توان یک اسم معنادار برای جدول برگزید. معمولا نامی را برای این جدول انتخاب می کنیم که هم نام با جدول پایگاه داده باشد. با این حال، اگر دستورِ select مربوط به SqlDataAdapter در بر دارنده ی یک join باشد، مجبور خواهید بود که اسم معنادار دیگری را برای آن برگزینید.
متدِ Fill دارای یک ویرایش (overload) دیگر است که تنها برای DataSet، یک پارامتر را بر می گزیند. در این صورت، جدولِ ایجاد شده دارای یک اسمِ قراردادی به صورتِ “Table1” خواهد بود. شماره ی جدول برای هر جدول جدیدی که به DataSet اضافه می شود، به ترتیب بالاتر خواهد رفت (Table2, Table3, Table4,…).
دیتا DataSet هم با ASP.NET و هم با اشکالِ ویندوزِ DataGrid ترکیب می شود. در کد زیر، DataSet به DataGrid تخصیص داده شده است:
dgCustomers.DataSource = dsCustomers;
dgCustomers.DataMember = "Customers"
اولین کاری که در کد بالا انجام می دهیم این است که DataSet را به ویژگی DataSource در DataGrid، اختصاص دهیم. این کار این امکان را برای DataGrid فراهم می آورد که بداند دارای یک شی برای اتصال است. با این وجود، علامتِ "+" را در GUI دریافت خواهید کرد، چراکه DataSet می تواند جداول متعددی را در بر داشته باشد. در اختیار داشتنِ این تعداد جدول موجب می گردد که بتوانید تمامیِ جداولِ موجود را بسط دهید. برای تعیین اینکه دقیقا از کدام جدول باید استفاده نمود، ویژگیDataMember را برای مشخص کردنِ نامِ جدول مورد نظر، مقداردهی کنید. در مثال ذکر شده، جدول را با عنوانِ Customers نام گذاری کردیم که دقیقا مشابه نامی است که به عنوان دومین پارامتر برای متدِ Fill در SqlDataAdapter استفاده شد. به همین دلیل است که بهتر است نام گذاری جدول با متدِ Fill باشد، چراکه موجب می شود که کدهای متوالی خواناتر باشند.
پس از این که تغییرات بر روی داده ها انجام گرفت، ممکن است بخواهید آن تغییرات را مجددا درون پایگاه داده برگردانید. به مبحث قبلی در بخش مقدمه ی این مقاله در بابِ راهنمای Updating مراجعه کنید. کد ذیل نشان می دهد که چگونه در شی SqlDataAdapter، از متدِ Update برای بازگردانیِ تغییرات به پایگاه داده، استفاده نماییم:
daCustomers.Update(dsCustomers, "Customers");
در کد بالا، متدِ Update در نمونه ی SqlDataAdapter، که اساسا DataSet مربوط به dsCustomers را پر نموده است، فراخوانی می شود. دومین پارامتر در متدِ Update، مشخص می کند که کدام جدول از DataSet باید Update شود. این جدول حاویِ لیست رکوردهایی است که تغییر یافته اند. همچنین، ویژگی های Insert، Update، و Delete در SqlDataAdapter در بردارنده ی دستورهای SQL هستند که به منظورِ انجام تغییرات در پایگاه داده، مورد استفاده قرار گرفته اند.
تا کنون، اقداماتِ مورد نیاز برای پیاده سازیِ مدیریتِ داده های آفلاین را فرا گرفتید. آنچه که بیشترین اهمیت را دارد مشاهده ی تمام این پیاده سازی ها در یک برنامه می باشد. برنامه زیر نشان می دهد که چگونه کد مورد استفاده در تمامیِ بخش های پیشین، در راستای اهداف این درس، ساده شده است:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Windows.Forms;
class DisconnectedDataform : Form
{
private SqlConnection conn;
private SqlDataAdapter daCustomers;
private DataSet dsCustomers;
private DataGrid dgCustomers;
private const string tableName = "Customers"
// initialize form with DataGrid and Button
public DisconnectedDataform()
{
// fill dataset
Initdata();
// set up datagrid
dgCustomers = new DataGrid();
dgCustomers.Location = new Point(5, 5);
dgCustomers.Size = new Size(this.ClientRectangle.Size.Width - 10,
this.ClientRectangle.Height - 50);
dgCustomers.DataSource = dsCustomers;
dgCustomers.DataMember = tableName;
// create update button
Button btnUpdate = new Button();
btnUpdate.Text = "Update"
btnUpdate.Location = new Point(
this.ClientRectangle.Width/2 - btnUpdate.Width/2,
this.ClientRectangle.Height - (btnUpdate.Height + 10));
btnUpdate.Click += new EventHandler(btnUpdateClicked);
// make sure controls appear on form
Controls.AddRange(new Control[] { dgCustomers, btnUpdate });
}
// set up ADO.NET objects
public void Initdata()
{
// instantiate the connection
conn = new SqlConnection("Server=(local);
DataBase=Northwind;
Integrated Security=SSPI");
// 1. instantiate a new DataSet
dsCustomers = new DataSet();
// 2. init SqlDataAdapter with select command and connection
daCustomers = new SqlDataAdapter("select CustomerID, CompanyName
from Customers", conn);
// 3. fill in insert, update, and delete commands
SqlCommandBuilder cmdBldr = new SqlCommandBuilder(daCustomers);
// 4. fill the dataset
daCustomers.Fill(dsCustomers, tableName);
}
// Update button was clicked
public void btnUpdateClicked(object sender, EventArgs e)
{
// write changes back to DataBase
daCustomers.Update(dsCustomers, tableName);
}
// start the Windows form
static void Main()
{
Application.Run(new DisconnectedDataForm());
}
}
متدِ InitData در این برنامه، شامل متدهای مورد نیاز برای مقداردهیِ SqlDataAdapter و DataSet می باشد. توجه داشته باشید که شی های داده ای در سطوح کلاسی تعریف شده اند، بنابراین می توانند در متدهای مختلفی مورد استفاده قرار گیرند. ویژگی DataSource از DataGrid در سازنده (Constructor) مقدار دهی می شود. هر زمان که کاربر بر روی گزینه ی update کلیک می کند، متدِ Update در btnUpdateCliked فراخوانی شده و تغییرات را به پایگاه داده برمی گرداند.
دیتا DataSet در بر دارنده ی جدول های زیادی است و می تواند در حافظه و حافظه ی Reused نگهداری شود. شی SqlDataAdapter این امکان را فراهم می آورد که DataSet را پر نموده و تغییراتِ Updat شده را به پایگاه داده برگرداند. جای نگرانی برای باز کردن یا بستنِ SqlConnection نیست، چرا که SqlDataAdapter این کارها را به طور خودکار انجام می دهد. شی SqlCommandBuilder فرمان های insert، update، و delete را بر اساس دستورِ select در SqlDataAdapter پر می کند. از متد Fill برای پر کردن DataSet با داده، استفاده کرده و به منظورِ بازگرداندنِ تغییرات به پایگاه داده، از متدِ Update در SqlDataAdapter استفاده نمایید.
پايان قسمت 13 آموزش سي شارپ