مشخصات مقاله
-
6285
-
0.0
-
29137
-
0
-
1
کار با داده به صورت آفلاین در سی شارپ (C# DataSet)
وقتی در مسیر یادگیری کار با پایگاه داده در C# پیش میری، خیلی زود متوجه میشی که برای هر عملیاتی لازم نیست همیشه بهصورت مستقیم و مداوم به پایگاه داده متصل باشی. در بعضی شرایط، نگهداشتن دادهها در حافظه و کار کردن با اونها بهصورت آفلاین میتونه روش مناسبتر و انعطافپذیرتری باشه.
توی این درس قراره با یکی از روشهای مهم کار با دادهها در ADO.NET آشنا بشی؛ روشی که با استفاده از DataSet و SqlDataAdapter امکان کار با دادهها رو بدون برقراری اتصال دائمی با پایگاه داده فراهم میکنه. اگر در حال یادگیری C# هستی، آشنایی با این شیوه میتونه درک بهتری از نحوه مدیریت دادهها در برنامهها بهت بده.
در درسهای قبلی آموزش پایگاه داده در سی شارپ، با اصول کار با پایگاه داده و عملیات پایه روی دادهها آشنا شدیم. حالا میخوایم سراغ روش دیگهای برای کار با دادهها بریم که کاربردهای زیادی داره و در بسیاری از مواقع میتونه نسبت به روشهای معمول، کارایی و بازدهی بالاتری داشته باشه.
کار با داده به صورت آفلاین (Disconnected) با DataSet و SqlDataReader
در این درس، نحوه کار با دادههای آفلاین با استفاده از اشیای DataSet و SqlDataReader رو بررسی میکنیم. اهداف این درس عبارتاند از:
- درک ضرورت وجود دادههای آفلاین
- آشنایی با موارد استفاده از DataSet
- استفاده از SqlDataReader برای بهروزرسانی و بازیابی دادهها
مقدمه
در درس سوم، بهطور مفصل با نحوه ارتباط و تعامل با یک Data Source با استفاده از شی SqlCommand آشنا شدیم. در درس چهارم هم یاد گرفتیم که چطور دادهها رو با استفاده از شی SqlDataReader با سرعت بالا بخونیم. حالا در این درس میخوایم یاد بگیریم که چطور با استفاده از شی DataSet و شی SqlDataAdapter، ارتباطی بین SqlConnection و SqlDataReader برقرار کنیم.
DataSet یک منبع دادهای مقیم در حافظه است که میتونه جداول مختلفی رو در خودش نگه داره. DataSetها صرفاً دادهها رو نگهداری میکنن و هیچ تعاملی با منبع داده (Data Source) ندارن. از طرف دیگه، SqlDataAdapter اتصال با منبع داده (Data Source) رو مدیریت میکنه و امکان کار بهصورت آفلاین رو در اختیارمون قرار میده. شی SqlDataAdapter فقط زمانی که بهش نیاز باشه، اتصال (Connection) رو برقرار میکنه و بعد از اینکه کارش تموم شد، اون رو میبنده. برای مثال، وقتی SqlDataAdapter یک DataSet رو از دادهها پر میکنه، این مراحل رو انجام میده:
- باز کردن اتصال (Connection)
- بازیابی دادهها در DataSet
- بستن اتصال (Connection)
SqlDataAdapter هنگام بهروزرسانی دادهها هم، با توجه به تغییراتی که در DataSet ایجاد شده، مراحل زیر رو انجام میده:
- باز کردن اتصال (Connection)
- نوشتن تغییرات از DataSet در منبع داده
- بستن اتصال (Connection)
بین عملیات Fill و Update، اتصال به منبع داده بسته باقی میمونه و شما میتونید آزادانه و هر زمان که لازم بود، دادهها رو با DataSet بخونید یا در اونها تغییر ایجاد کنید. این همون مکانیزم کار با دادهها بهصورت آفلاینه. از اونجا که اتصال (Connection) فقط زمانی که اپلیکیشن بهش نیاز داره برقرار میشه، کار با دادهها سادهتر و انعطافپذیرتر خواهد بود.
سناریوهای زیر بهتر نشون میدن که چرا در بعضی شرایط، کار با دادههای آفلاین ضروریه. تصور کنید افرادی بدون اتصال شبکه باید به کار خودشون ادامه بدن. این موضوع میتونه انعطافپذیری بیشتری برای وبسایتها و برنامهها ایجاد کنه. برای مثال، یک فروشنده رو در نظر بگیرید که در طول سفرش به اطلاعات مشتریان نیاز داره. در ابتدای روز، این فروشنده باید برای دسترسی به آخرین اطلاعات موجود، با پایگاه داده اصلی همگامسازی بشه. ممکنه در طول روز اطلاعات مربوط به مشتریان رو تغییر بده، مشتریان جدیدی به فهرست اضافه کنه یا سفارشهای جدیدی ثبت کنه. از اونجا که یک نسخه از اطلاعات مشتریان در اختیار فروشنده قرار داره و دیگران امکان دسترسی به اون رو ندارن، این روش میتونه برای چنین شرایطی مناسب باشه. در پایان روز هم فروشنده دوباره به شبکه متصل میشه و تغییرات برای فرایند شبانه بهروزرسانی میشن.
سناریوی بعدی به ایجاد قابلیت کار و سادگی بیشتر برای وبسایت مربوط میشه. با وجود SqlDataAdapter، اگر هر بار که یک صفحه رو نمایش میدید برای دریافت رکوردها دوباره به پایگاه داده مراجعه کنید، هنگام بارگذاری هر صفحه به یک اتصال (Connection) جدید نیاز خواهید داشت. با افزایش تعداد کاربران، این موضوع میتونه روی Scalability (مقیاسپذیری) تأثیر منفی بذاره. یکی از راههای جلوگیری از این مشکل، استفاده از یک DataSet هست که قبلاً بهروزرسانی شده و در cache ذخیره شده باشه. هر درخواست برای صفحه، در صورت نیاز cache رو بررسی میکنه و دادهها رو بارگذاری میکنه، یا اینکه دادهها رو مستقیماً از cache دریافت و نمایش میده. به این ترتیب، دیگه نیازی به مراجعه مستقیم به پایگاه داده نیست و کل عملیات کارآمدتر انجام میشه.
موارد استثنا برای سناریوهای بالا، موقعیتهایی هستن که قصد دارید دادهها رو بهروزرسانی کنید. در این شرایط، بسته به اینکه در استراتژی خودتون چطور از دادهها استفاده میکنید، باید تصمیم مناسب رو بگیرید. زمانی که اطلاعات عمدتاً فقط خواندنی (read only) هستن، میتونید از دادههای آفلاین استفاده کنید. در موقعیتهای دیگه، مثل استفاده از شی SqlCommand برای بهروزرسانی سریع، بهتره شرایطی رو در نظر بگیرید که نیازهای شما به فرایندهای پویاتری احتیاج دارن. همچنین اگر حجم دادهها اونقدر زیاد باشه که نگهداری اونها در حافظه عملاً غیرعملی باشه، برای دادههای فقط خواندنی (read only) به شی SqlDataReader نیاز خواهید داشت. در واقع، استثناهای مختلفی وجود دارن، اما عامل اصلی در انتخاب روش مناسب اینه که نیازهای اپلیکیشن خودتون رو بشناسید؛ چون همین نیازها روی نحوه طراحی شما تأثیر میذارن.
ایجاد شی DataSet
برای نمونهسازی یک شی DataSet نکته خاصی وجود نداره. درست مثل هر شی دیگهای، کافیه یک نمونه جدید از اون ایجاد کنید.
DataSet dsCustomers = new DataSet();
ساختار DataSet به هیچ پارامتری نیاز نداره. با این حال، اگر قصد داشته باشید دادههای سریالشده (Serialized) رو به XML تبدیل کنید، یک سازنده دیگه هم وجود داره که یک رشته رو برای نام DataSet دریافت میکنه. در کد بالا، DataSet خالیه و برای پر کردن اون به یک شی SqlDataAdapter نیاز داریم.
ایجاد یک 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);
همونطور که میبینید، در کد بالا شی daCustomers به سازنده SqlCommandBuilder ارسال شده. این کار به SqlCommandBuilder میگه که شی SqlDataAdapter باید از چه دستورهایی استفاده کنه. SqlCommandBuilder دستور select رو میخونه و بر اساس اون، فرمانهای insert، update و delete رو استنباط میکنه و بعد این دستورهای جدید رو بهترتیب برای Propertyهای Insert، Update و Delete اختصاص میده.
همونطور که قبلاً اشاره شد، SqlCommandBuilder محدودیتهایی هم داره. زمانی که یک دستور ساده select رو روی یک جدول مجزا اجرا میکنید، میشه از این شی استفاده کرد. اما اگر لازم باشه دو یا چند جدول رو با هم join کنیم، یا بخوایم یک پروسه ذخیرهشده رو اجرا کنیم، این شی کاربردی نخواهد داشت. در درسهای آینده درباره این سناریوها توضیحات بیشتری ارائه خواهد شد.
پر کردن DataSet
وقتی یک DataSet و نمونههایی از SqlDataAdapter رو در اختیار دارید، مرحله بعدی پر کردن (Fill) DataSet هست. در کد زیر میبینید که چطور با استفاده از متد Fill مربوط به SqlDataAdapter این کار رو انجام بدیم:
daCustomers.Fill(dsCustomers, "Customers");
متد Fill در کد بالا دو پارامتر داره: یک DataSet و نام جدول. قبل از اینکه DataSet رو با دادهها پر کنید، باید اون رو نمونهسازی کرده باشید. پارامتر دوم، نام جدولی رو مشخص میکنه که قراره در DataSet ساخته بشه. میتونید هر نامی که میخواید برای این جدول انتخاب کنید. هدف این گزینه اینه که در ادامه بتونید یک نام معنادار برای جدول داشته باشید. معمولاً نامی رو انتخاب میکنیم که با نام جدول موجود در پایگاه داده یکسان باشه. با این حال، اگر دستور select مربوط به SqlDataAdapter شامل یک join باشه، لازم هست نام معنادار دیگهای برای اون انتخاب کنید.
متد Fill یک overload دیگه هم داره که فقط یک DataSet رو به عنوان پارامتر دریافت میکنه. در این حالت، جدول ایجادشده یک نام قراردادی مثل “Table1” خواهد داشت. شماره جدول برای هر جدول جدیدی که به DataSet اضافه بشه، بهترتیب افزایش پیدا میکنه (Table2, Table3, Table4,…).
استفاده از DataSet
DataSet هم با ASP.NET و هم با انواع DataGrid در ویندوز ترکیب میشه. در کد زیر، DataSet رو به DataGrid اختصاص دادیم:
dgCustomers.DataSource = dsCustomers; dgCustomers.DataMember = "Customers";
اولین کاری که در کد بالا انجام میدیم اینه که DataSet رو به ویژگی DataSource در DataGrid اختصاص بدیم. با این کار، DataGrid متوجه میشه که یک شی برای اتصال در اختیار داره. با این حال، علامت “+” رو در GUI مشاهده خواهید کرد، چون DataSet میتونه چندین جدول رو در خودش نگه داره. وجود چند جدول به شما اجازه میده جداول موجود رو بسط بدید. برای مشخص کردن اینکه دقیقاً باید از کدوم جدول استفاده بشه، ویژگی DataMember رو با نام جدول مورد نظر مقداردهی میکنیم. در مثال بالا، جدول رو با عنوان Customers نامگذاری کردیم؛ همون نامی که به عنوان پارامتر دوم برای متد Fill در SqlDataAdapter استفاده شد. به همین دلیل بهتره هنگام استفاده از متد Fill، یک نام معنادار برای جدول انتخاب کنیم، چون این کار باعث میشه کدهای بعدی خواناتر باشن.
انتقال تغییرات به پایگاه داده (Updating Changes)
بعد از اینکه تغییرات مورد نظرتون رو روی دادهها انجام دادید، ممکنه بخواید این تغییرات رو دوباره به پایگاه داده منتقل کنید. همونطور که در بخش قبلی مقاله درباره فرایند Updating توضیح دادیم، کد زیر نشون میده چطور میشه از متد Update در شی SqlDataAdapter برای برگردوندن تغییرات به پایگاه داده استفاده کرد:
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 میتونه چندین جدول رو در خودش نگه داره و دادهها رو در حافظه ذخیره کنه. شی SqlDataAdapter این امکان رو فراهم میکنه که DataSet رو پر کنید و تغییرات بهروزرسانیشده رو به پایگاه داده برگردونید. بنابراین لازم نیست نگران باز کردن یا بستن SqlConnection باشید، چون SqlDataAdapter این کارها رو بهصورت خودکار انجام میده. شی SqlCommandBuilder هم فرمانهای insert، update و delete رو بر اساس دستور select موجود در SqlDataAdapter ایجاد میکنه. برای پر کردن DataSet از متد Fill استفاده کنید و برای برگردوندن تغییرات به پایگاه داده، متد Update در SqlDataAdapter رو به کار ببرید.
اگر میخوای مسیر یادگیری C# رو ادامه بدی و مباحث کار با داده و برنامهنویسی رو اصولیتر دنبال کنی، میتونی آموزشهای مرتبط رو در دوره آموزش C# دنبال کنی و قدم بعدی یادگیری برنامهنویسی با C# رو برداری.