ویرایش بانک اطلاعاتی با استفاده از LINQ to SQL

تبدیل جداول دیتابیس و رکوردهای آن به کلاس‌های متناظر در LINQ to SQL کار دستکاری دیتابیس را راحت‌تر می‌کند. وقتی که کلاس‌های LINQ to SQL تولید شدند شما به طور مستقیم می‌توانید اشیاء آنها را دستکاری کنید. کلاس DataContext دارای متدهایی برای حذف، اضافه و ویرایش کردن رکوردها می‌باشد. این متدها عبارتند از ()DeleteOnSubmit() ، InsertOnSubmit() ، UpdateOnSubmit .
اگر بخواهید یک خاصیت از شیء که در اصل یک رکورد از جدول است را بروزرسانی کنید می‌توانید به طور مستقیم این کار را انجام دهید. همه این اعمال فوراً بر روی جداول اصلی و رکورهای آن‌ها تأثیر نمی‌گذارند. لازم است متد ()SubmitChanges از کلاس DataContext را فراخوانی کنیم. برای دستیابی به یک عنصر یا رکورد خاص از جدول می‌توانیم از متد ()ElementAt استفاده کنیم. این متد یک مقدار از نوع صحیح می‌گیرد که بیانگر اندیس رکورد می‌باشد. در برنامه‌ای که قرار است برای شما توضیح دهیم کاربر می‌تواند مشخصات افراد را مشاهده و همچنین آنها را حذف و ویرایش کند.
برای کار با برنامه از دیتابیسی که در درس قبل ایجاد کردیم استفاده می‌کنیم. می‌خواهیم برنامه را به صورتی طراحی کنیم که کاربر در لحظه بتواند اطلاعات یک شخص (person) را به دست آورده و با استفاده از دکمه‌ها در بین سایر رکوردها حرکت کند. این برنامه، همچنین به کاربر اجازه حذف، اضافه و ویرایش اطلاعات را می‌دهد. مشاهده خواهید کرد که این کار با استفاده از کلاس‌های LINQ to SQL بسیار راحت است. حال به محیط طراحی رفته و برنامه‌مان را که دارای دکمه‌هایی برای حذف و اضافه و ویرایش رکوردها است را با توجه به شکل و جدول زیر طراحی می‌کنیم :
linq-to-sql-4101

Number Name
1 firstButton
2 prevButton
3 nextButton
4 lastButton
5 idTextBox
6 firstNameTextBox
7 lastNameTextBox
8 ageTextBox
9 addButton
10 deleteButton
11 updateButton

خاصیت ReadOnly جعبه متن idTextBox را بر روی true قرار می‌دهیم تا همانند یک کلید اصلی رفتار کرده و قابل تغییر نباشد. خاصیت StartPosition فرم را بر روی CenterScreen قرار می‌دهیم. دکمه‌های بالای هم برای نمایش رکوردهای قبلی، بعدی، اول و آخر و جعبه‌های متن هم برای نمایش اطلاعات شخص فعلی (current person) به کار می‌روند. دکمه‌های پایینی هم برای اضافه، حذف و ویرایش رکوردها مورد استفاده قرار می‌گیرند. با کلیک بر روی دکمه addButton محتوبات جعبه‌های متن پاک شده و شما می‌توانید مقادیر جدید را در داخل آنها نوشته و به جدول اضافه کنید. دکمه deleteButton برای حذف سطر فعلی و دکمه updateButton هم برای ویرایش آن به کار می‌رود. ما از کد زیر در برنامه‌مان استفاده می‌کنیم :

    1: using System;
    2: using System.Collections.Generic;
    3: using System.Linq;
    4: using System.Windows.Forms;
    5: 
    6: namespace LinqToSqlDemo2
    7: {
    8:     public partial class Form1 : Form
    9:     {
   10:         private int currentIndex;
   11:         private int minIndex;
   12:         private int maxIndex;
   13:         private bool addPending;
   14:         private SampleDataContext database;
   15:         private IEnumerable<Person> persons;
   16: 
   17:         public Form1()
   18:         {
   19:             InitializeComponent();
   20: 
   21:             database = new SampleDataContext();
   22:             persons = from p in database.Persons
   23:                       select p;
   24: 
   25:             currentIndex = 0;
   26: 
   27:             minIndex = 0;
   28:             maxIndex = persons.Count() - 1;
   29: 
   30:             DisableButtons();
   31: 
   32:             addPending = false;
   33:         }
   34: 
   35:         private void Form1_Load(object sender, EventArgs e)
   36:         {
   37:             ShowPersonInfo(currentIndex);
   38:         }
   39: 
   40:         private void firstButton_Click(object sender, EventArgs e)
   41:         {
   42:             ShowPersonInfo(minIndex);
   43:             currentIndex = minIndex;
   44:             DisableButtons();
   45:         }
   46: 
   47:         private void lastButton_Click(object sender, EventArgs e)
   48:         {
   49:             ShowPersonInfo(maxIndex);
   50:             currentIndex = maxIndex;
   51:             DisableButtons();
   52:         }
   53: 
   54:         private void prevButton_Click(object sender, EventArgs e)
   55:         {
   56:             ShowPersonInfo(--currentIndex);
   57:             DisableButtons();
   58:         }
   59: 
   60:         private void nextButton_Click(object sender, EventArgs e)
   61:         {
   62:             ShowPersonInfo(++currentIndex);
   63:             DisableButtons();
   64:         }
   65: 
   66:         private void addButton_Click(object sender, EventArgs e)
   67:         {
   68:             if (addPending == false)
   69:             {
   70:                 ClearFields();
   71:                 int newID = persons.Count() == 0 ? 1 : persons.Last().PersonID + 1;
   72:                 idTextBox.Text = newID.ToString();
   73:                 addButton.Text = "Done";
   74:                 addPending = true;
   75:             }
   76:             else
   77:             {
   78:                 try
   79:                 {
   80:                     //Create new person                                                 
   81:                     Person newPerson = new Person();
   82:                     newPerson.PersonID = Int32.Parse(idTextBox.Text);
   83:                     newPerson.FirstName = firstNameTextBox.Text;
   84:                     newPerson.LastName = lastNameTextBox.Text;
   85:                     newPerson.Age = Int32.Parse(ageTextBox.Text);
   86: 
   87:                     //Add newPerson                                                    
   88:                     database.Persons.InsertOnSubmit(newPerson);
   89:                     database.SubmitChanges();
   90:                     maxIndex++;
   91:                     currentIndex = maxIndex;
   92:                     DisableButtons();
   93:                     MessageBox.Show("Successfully added to database.", "Success",
   94:                                     MessageBoxButtons.OK, MessageBoxIcon.Information);
   95:                     addButton.Text = "Add";
   96:                     addPending = false;
   97:                 }
   98:                 catch
   99:                 {
  100:                     MessageBox.Show("Failed to add new record to database. Make sure " +
  101:                                     "that every field is not empty and in a correct " +
  102:                                     "format", "Failed",
  103:                                     MessageBoxButtons.OK, MessageBoxIcon.Error);
  104:                 }
  105:             }
  106:         }
  107: 
  108:         private void deleteButton_Click(object sender, EventArgs e)
  109:         {
  110:             try
  111:             {
  112:                 database.Persons.DeleteOnSubmit(persons.ElementAt(currentIndex));
  113:                 database.SubmitChanges();
  114: 
  115:                 maxIndex--;
  116: 
  117:                 if (currentIndex > maxIndex)
  118:                     currentIndex--;
  119: 
  120:                 MessageBox.Show("Successfully removed from the database.", "Success",
  121:                     MessageBoxButtons.OK, MessageBoxIcon.Information);
  122: 
  123:                 ShowPersonInfo(currentIndex);
  124:                 DisableButtons();
  125:             }
  126:             catch
  127:             {
  128:                 MessageBox.Show("Unable to delete.", "Error",
  129:                                 MessageBoxButtons.OK, MessageBoxIcon.Error);
  130:             }
  131:         }
  132: 
  133:         private void updateButton_Click(object sender, EventArgs e)
  134:         {
  135:             try
  136:             {
  137:                 Person modifiedPerson = persons.ElementAt(currentIndex);
  138:                 modifiedPerson.FirstName = firstNameTextBox.Text;
  139:                 modifiedPerson.LastName = lastNameTextBox.Text;
  140:                 modifiedPerson.Age = Int32.Parse(ageTextBox.Text);
  141: 
  142:                 database.SubmitChanges();
  143:                 MessageBox.Show("Update success!", "Success",
  144:                                 MessageBoxButtons.OK, MessageBoxIcon.Error);
  145:             }
  146:             catch
  147:             {
  148:                 MessageBox.Show("Error on updating.", "Error",
  149:                                 MessageBoxButtons.OK, MessageBoxIcon.Information);
  150:             }
  151:         }
  152: 
  153:         private void ShowPersonInfo(int index)
  154:         {
  155:             if (persons.Count() == 0)
  156:             {
  157:                 ClearFields();
  158:                 MessageBox.Show("Nothing to show.", "Error",
  159:                                 MessageBoxButtons.OK, MessageBoxIcon.Error);
  160:                 return;
  161:             }
  162: 
  163:             Person currentPerson = persons.ElementAt(index);
  164: 
  165:             idTextBox.Text = currentPerson.PersonID.ToString();
  166:             firstNameTextBox.Text = currentPerson.FirstName;
  167:             lastNameTextBox.Text = currentPerson.LastName;
  168:             ageTextBox.Text = currentPerson.Age.ToString();
  169:         }
  170: 
  171:         private void DisableButtons()
  172:         {
  173:             if (persons.Count() <= 1)
  174:             {
  175:                 firstButton.Enabled = false;
  176:                 prevButton.Enabled = false;
  177:                 nextButton.Enabled = false;
  178:                 lastButton.Enabled = false;
  179:                 return;
  180:             }
  181: 
  182:             if (currentIndex == minIndex)
  183:             {
  184:                 firstButton.Enabled = false;
  185:                 prevButton.Enabled = false;
  186:                 nextButton.Enabled = true;
  187:                 lastButton.Enabled = true;
  188:             }
  189:             else if (currentIndex == maxIndex)
  190:             {
  191:                 firstButton.Enabled = true;
  192:                 prevButton.Enabled = true;
  193:                 nextButton.Enabled = false;
  194:                 lastButton.Enabled = false;
  195:             }
  196:             else if (currentIndex > minIndex && currentIndex < maxIndex)
  197:             {
  198:                 firstButton.Enabled = true;
  199:                 prevButton.Enabled = true;
  200:                 nextButton.Enabled = true;
  201:                 lastButton.Enabled = true;
  202:             }
  203:         }
  204: 
  205:         private void ClearFields()
  206:         {
  207:             idTextBox.Text = String.Empty;
  208:             firstNameTextBox.Text = String.Empty;
  209:             lastNameTextBox.Text = String.Empty;
  210:             ageTextBox.Text = String.Empty;
  211:             firstNameTextBox.Focus();
  212:         }
  213:     }
  214: }

در خطوط 15-10 متغیرهای لازم که قرار است از آنها در داخل برنامه استفاده کنیم را تعریف کرده‌ایم. در خط 10 متغیری که اندیس شخص (person) فعلی را در خود ذخیره می‌کند را تعریف کرده‌ایم. در خطوط 12-11 متغیرهایی تعریف شده‌اند که برای نگهداری اندیس‌های اول و آخر در بانک به کار می‌روند. متغیر خط 13 همانطور که بعداً مشاهده خواهید کرد، در دکمه addButton به کار می‌رود. در خط 14 یک شیء SampleDataContext که مترادف با دیتابیس Sample ایجاد کرده‌ایم که از آن در فراخوانی متدهای حذف، اضافه، ویرایش و واکشی رکوردهای جداول دیتابیس Sample استفاده خواهیم کرد. در خط 15 یک شیء از نوع IEnumerable تعریف کرده‌ایم و همه رکوردهای Person پرس و جو شده از دیتابیس را دران قرار می‌دهیم. از آنجاییکه خروجی کوئری Linq از نوع IEnumerable است پس نوع متغیر persons در خط 15 را IEnumerable قرار می‌دهیم. این کار را برای این انجام می‌دهیم که هر بار نیازی به واکشی تمامی اطلاعات نداشته باشیم. تنها یک بار این کار را بر روی دیتابیس انجام می‌دهیم و خروجی آنرا در یک متغیر سراسری (persons) قرار می‌دهیم. ابتدا متدهای کاربردی که در برنامه بالا مورد استقاده قرار گرفته‌اند را توضیح می‌دهیم.

متد ()ClearFields که در خطوط 212-205 تعریف شده است برای پاک کردن جعبه‌های متن و قرار دادن Focus بر روی جعبه متن firstNameTextBox به کار می‌رود.

از متد ()DisableButtons ( خطوط 203-172) برای غیر فعال کردن دکمه‌هایی که نشان دهنده‌ی رکوردهای اول و آخر دیتابیس هستند استفاده می‌شود متد مذکور اینکار را با استفاده از چک کردن اینکه آیا کاربر به ابتدا یا انتهای مجموعه رکوردها رسیده است انجام می‌دهد .
متد ()ShowPersonInfo (خطوط 169-153) یک اندیس می‌گیرد و شیء Person متناظر با آن اندیس را بر می‌گرداند. در خطوط 161-155 این متد چک می‌کند که آیا رکوردی برای نمایش وجود دارد یا خیر؟ اینکار با استفاده از متد ()Count فیلد persons انجام می‌شود. در صورتی که رکوردی برای نمایش وجود نداشته باشد یک پیغام خطا به کاربر نمایش می‌دهیم و با دستور return از اجرای بقیه دستورات متد جلوگیری می‌کنیم. در خط 163 این متد با استفاده از متد ()ElementAt از فیلد persons شیء با اندیس مناسب را برگشت می‌دهیم. سپس خصوصیات آن شیء را نمایش در جعبه‌های متن متناظر نمایش می‌دهیم.

حال اجازه دهید به سازنده فرم در خطوط (33-17) نگاهی بیندازیم. در خط 21 یک نمونه از SampleDataContext ایجاد کرده‌ایم در نتیجه می‌توانیم عملیات دلخواه را بر روی دیتابیس انجام دهیم. در خطوط 23-22 یک پرس و جوی ساده Linq که لیست تمامی رکوردها را برگشت می‌دهد را روی دیتابیس انجام می‌دهیم. سپس مقدار متغیر currentIndex را بر روی 0 قرار می‌دهیم که به برنامه می گوییم که مشخصات اولین رکورد را نمایش دهد. در خطوط 28-27 متغیرهای minIndex و maxIndex را مقداردهی می‌کنیم. که نشان دهنده‌ی اندیس اولین و آخرین رکوردها هستند. اولین رکورد در مجموعه دارای اندیس 0 می‌باشد به همین دلیل مقدار minIndex روی 0 تنظیم شده است. مقدار تعداد کل رکوردها منهای 1 را به متغیر maxIndex اختصاص می‌دهیم زیرا، اندیس‌ها از 0 شروع می‌شوند.

متد DisableButtons بسته به نوع شرایط رفتارهای متفاوتی از خود نشان می‌دهد به عنوان مثال اگر به اول مجموعه رکوردها رسیده باشیم (یعنی مقدار currentIndex 0 باشد) دکمه‌ی قبلی و اولین را غیر فعال می‌کنیم زیرا نیازی به آنها نیست. و در آخر مقدار متغیر addPending را برابر false قرار می‌دهیم. همانطور که بعداً مشاهده خواهید کرد از این متغیر در کنترل کننده رویداد addButton استفاده خواهد شد .

به محیط طراحی برگشته و با دوبار کلیک بر روی عنوان فرم یک کنترل کننده رویداد برای رویداد load فرم ایجاد می‌کنیم (خطوط 38-35). در داخل کنترل کننده رویداد متد ()ShowPersonInfo را فراخوانی کرده و مقدار فعلی currentIndex که همان 0 می‌باشد را به آن ارسال می‌کنیم. این کار بعث نمایش اطلاعات اولین رکورد در جعبه‌های متن می‌شود. حال نوبت به اضافه کردن رویداد کلیک به دکمه‌ی پیمایشگر رسیده است. در محیط طراحی بر روی دکمه‌ی firstButton کلیک کنید و خطوط 44-42 را به کنترل کننده رویداد آن اضافه کنید. در اولین خط با فراخوانی متد ()ShowPersonInfo و ارسال مقدار minIndex به آن اولین رکورد را نمایش می‌دهیم. سپس مقدار متغیر minIndex را به متغیر currentIndex نسبت می‌دهیم. این بدین معنی است که اولین رکورد همان رکورد جاری است. سپس با فراخوانی متد ()DisableButtons دکمه‌های firstButton و prevButton را غیر فعال می‌کنیم. با کمی دقت در کدهای بالا که کدهای prevButton و nextButton مشابه کدهای دکمه‌ی firstButton می‌باشند. در کدهای این دو دکمه نیز متد ()ShowPersonInfo فراخوانی شده و مقدار فیلد currentIndex برای نمایش رکورد مناسب به آن ارسال می‌شود. این کار با افزایش یا کاهش مقدار currentIndex و ارسال آن به ان متد انجام می‌شود. به عنوان مثال زمانی که قصد داریم رکورد قبلی را نمایش دهیم ابتدا باید یک واحد از مقدار این متغیر کم کنیم و سپس مقدار جدید را به متد ()ShowPersonInfo ارسال کنیم .

کنترل کننده رویداد addButton ( خطوط 106-66) دارای قابلیت‌های زیر است. دکمه‌ی addButton دارای 2 حالت و وضعیت می‌باشد: اولین حالت زمانی است که مقدار فیلد addPending برابر false باشد. در این حالت زمانی که بر روی این دکمه کلیک شود مقادیر موجود در جعبه‌های متن پاک می‌شود (خط 70). سپس مقدار مناسب برای خاصیت PersonID رکورد جدید محاسبه می‌شود. زیرا مقدار این خاصیت در بانک اطلاعاتی نباید تکراری باشد. نحوه‌ی محاسبه بدین شکل است که اگر رکوردی در بانک وجود نداشته باشد مقدار این خاصیت برابر 1 می‌شود (71) و در غیر اینصورت مقدار خاصیت PersonID آخرین رکورد بانک اطلاعاتی را بدست می‌آوریم و یک واحد به آن اضافه می‌کنیم و در نهایت این مقدار را در جعبه متن مربوطه نمایش می‌دهیم. متن دکمه‌ی addButton را به Done تغییر می‌دهیم که نشان دهنده‌ی این است که برنامه منتظر این است که کاربر اطلاعات جدید را در جعبه‌های متن وارد کرده تا به بانک اطلاعاتی اضافه شوند. در نهایت مقدار true را در فیلد addPending قرار می‌دهیم تا وضعیت دوم دکمه فعال شود. وضعیت دوم دکمه زمانی فعال می‌شود که برنامه منتظر وارد کردن مقادیر توسط کاربر است .

وقتی که کاربر برای بار دوم بر روی دکمه‌ی addButton کلیک می‌کند، رکورد جدید در بانک اطلاعاتی ذخیره می‌شود. استفاده از متد ()SubmitChanges ممکن است باعث بروز خطا یا استثناتی مانند FormatExceptions یا ChangeConflictExceptions شود، به همین خاطر دستورات اضافه کردن رکورد جدید را در بلاک try…catch قرار داده‌ایم (104-78). در خطوط 85-81 یک شیء جدید Person ایجاد کرده‌ایم و مقادیر خاصیت‌های آن را برابر جعبه‌های متن متانظر قرار داده‌ایم.

شیء ایجاد شده را در خط 88 به متد ()InsertOnSubmit ارسال می‌کنیم. وظیفه اصلی این متد ثبت تغییرات در بانک اطلاعاتی می‌باشد. همانطور که از اسم متد مشخص است، تغییرات زمانی در بانک اطلاعاتی اعمال می‌شوند که متد ()SubmitChanges فراخوانی شود. فراخوانی این متد (()SubmitChanges) را در خط 89 باعث اعمال تغییرات (در اینجا اضافه شدن رکورد جدید) به جدول Persons می‌شود. در خط 90 مقدار فیلد maxIndex را یک واحد افزایش داده‌ایم. زیرا یک رکورد جدید ثبت شده و تعداد کل رکوردها یک واحد افزایش پیدا کرده است .
بعد از ثبت رکورد جدید مقدار currentIndex را به فیلد maxIndex اختصاص می‌دهیم. سپس متد ()DisableButtons را فراخوانی کرده و یک پیغام مبنی بر ثبت موفقیت آمیز رکورد جدید در دیتابیس را به کاربر نمایش می‌دهیم. و در نهایت عنوان دکمه را به Add و مقدار فیلد addPending را به false تغییر می‌دهیم تا وضعیت اول دکمه فعال شود. در قسمت Catch به شکل ساده یک پیغام خطا را به کاربر مبنی بر عدم موفقیت یا مشکل در ثبت اطلاعات نشان می‌دهیم.

کدهای اداره گر رویداد کلیک دکمه‌ی deleteButton ( خطوط 131-108) را در بلوک try…catch قرار می‌دهیم تا از استثناهای رخ داده را مدیریت کنیم. در خطوط 112 از متد ()DeleteOnSubmit برای ثبت تغییرات (در اینجا حذف رکورد) در پایگاه داده استفاده کرده‌ایم. این متد به عنوان ورودی یک شیء را دریافت می‌کند و رکورد مرتبط با آن را از جدول پایگاه داده حذف می‌کند. برای بدست آوردن شی ای که می‌بایست حذف شود از متد ()ElementAt استفاده می‌کنیم و مقدار فیلد currentIndex را به آن ارسال می‌کنیم. در نهایت برای اعمال تغییرات در بانک اطلاعاتی باید متد ()SubmitChanges را فراخوانی کنیم. دقت داشته باشید اگر این متد را فراخوانی نکنید تغییرات در بانک اطلاعاتی اعمال نمی‌شود. سپس مقدار فیلد maxIndex را یک واحد کاهش می‌دهیم زیرا یک رکورد از بانک اطلاعاتی حذف شده است همچنین مقدار مناسب را به فیلد currentIndex نسبت می‌دهیم. کاهش مقدار فیلد maxIndex باعث می‌شود که مقدار فیلد currentIndex از آن بیشتر شود به همین خاطر باید مقدار این فیلد ( currentIndex ) را نیز کاهش دهیم. در نتیجه رکورد قبل از رکورد حذف شده نمایش داده می‌شود. در اداره گر رویداد کلیک دکمه updateButton برای انجام عمل ویرایش اطلاعات ابتدا یک شیء از کلاس Person ایجاد می‌کنیم و مقدار خاصیت‌های آنرا با مقادیر جدید پر می‌کنیم، سپس برای انجام عمل ویرایش این شیء را به متد ()UpdateOnSubmit ارسال می‌کنیم و در نهایت برای اعمال تغییرات متد ()SubmitChanges را فراخوانی می‌کنیم (خطوط 151-133).