9. دسترسی به پایگاه داده
9.1. اتصالدهنده ADO.NET
بیایید معماری لایهبندیشدهای را که در چندین مورد استفاده شده است، مجدداً بررسی کنیم
![]() |
در مثالهای بررسیشده، لایه [dao] تاکنون از دو نوع منبع داده استفاده کرده است:
- دادههای سختکدشده در کد
- دادهها از فایلهای متنی
در این فصل، موردی را بررسی خواهیم کرد که در آن دادهها از یک پایگاه داده میآیند. معماری سهلایه سپس به یک معماری چندلایه تبدیل میشود. معماریهای چندلایه متنوعی وجود دارند. ما مفاهیم پایهای را با استفاده از موارد زیر بررسی خواهیم کرد:
![]() |
در نمودار بالا، لایه [dao] [1] از طریق لایه SGBD [3] ارتباط برقرار میکند.یک کتابخانه کلاسی که مختص SGBD مورد استفاده است و با آن ارائه میشود. این لایه، قابلیتهای استاندارد را که تحت عنوان ADO (ابجکتهای داده ActiveX) گروهبندی شدهاند، پیادهسازی میکند. به چنین لایهای، ارائهدهنده (در این مورد، ارائهدهنده دسترسی به پایگاه داده) یا کانکتور گفته میشود. اکنون اکثر SGBDها دارای یک ADO.NET کانکتور هستند، که در روزهای اولیه پلتفرم .NET اینطور نبود. کانکتورهای NET رابط استاندارد برای لایه [dao] فراهم نمیکنند؛ در نتیجه، کد لایه دوم شامل نام کلاسهای کانکتور است. اگر کسی SGBD را تغییر دهد، در واقع کانکتور و کلاسها را تغییر میدهد و سپس لایه [dao] باید اصلاح شود. این هم یک معماری با کارایی بالا است زیرا .NET برای یک SGBD خاص نوشته شده و میتواند از آن بهترین استفاده را ببرد، و ساختاری سختگیرانه است زیرا تغییر SGBD مستلزم تغییر لایه [dao] است. این استدلال دوم باید در بستر مناسب خود بررسی شود: شرکتها به ندرت SGBD خود را تغییر میدهند. علاوه بر این، در ادامه خواهیم دید که از نسخه 2.0 از .NET، یک کانکتور عمومی وجود داشته است که انعطافپذیری را بدون قربانی کردن عملکرد فراهم میکند.
9.2. دو روش بهرهبرداری از یک منبع داده
پلتفرم .NET امکان استفاده از یک منبع داده را به دو روش مختلف فراهم میکند:
- حالت متصل
- حالت آفلاین
در حالت متصل، برنامه
- یک اتصال به منبع داده برقرار میکند
- در حالت خواندن/نوشتن با منبع داده کار میکند
- اتصال را میبندد
در حالت آفلاین، برنامه
- یک اتصال به منبع داده باز میکند
- یک نسخهٔ در حافظهای از تمام یا بخشی از دادهها را از منبع بازیابی میکند
- اتصال را قطع میکند
- با کپی موجود در حافظهٔ دادهها کار میکند، از آن میخواند و در آن مینویسد
- وقتی کار کامل شد، یک اتصال باز میکند، دادههای اصلاحشده را به منبع داده میفرستد تا بهروزرسانی شوند، و اتصال را میبندد
در اینجا تنها حالت متصل را در نظر میگیریم.
9.3. مفاهیم پایهای عملیات پایگاه داده
ما مفاهیم اصلی استفاده از پایگاه داده با پایگاه داده SQL Server Compact 3.5 را تشریح خواهیم کرد. این SGBD همراه با Visual Studio Express ارائه میشود. این یک SGBD سبک است که فقط میتواند در هر زمان یک کاربر را مدیریت کند. با این حال، برای یک معرفی اولیه به برنامهنویسی پایگاه داده کافی است. ما در مرحله بعد به سایر پایگاههای داده SGBD خواهیم پرداخت.
معماری مورد استفاده به شرح زیر خواهد بود:
![]() |
یک برنامه کنسولی [1] از یک پایگاه داده فشرده SqlServer [3,4] از طریق کانکتور Ado.Net این SGBD QZ استفاده خواهد کرد.XW2HTMLP003397ZQX.
9.3.1. : پایگاه دادهٔ نمونه
ما پایگاه داده را مستقیماً در Visual Studio Express خواهیم ساخت. برای این کار، یک پروژه کنسول جدید ایجاد خواهیم کرد.
![]() |
- [1]: پروژه
- [2]: نمای «کاوشگر پایگاه داده» را باز کنید
- [3]: ایجاد یک اتصال جدید
![]() |
- [4]: نوع SGBD را انتخاب کنید
- [5,6]: سرور کامپکت SGBD SQL را انتخاب کنید
- [7]: ایجاد پایگاه داده
- [8]: یک پایگاه داده SQL Server Compact در یک فایل واحد با پسوند .sdf جای داده میشود. مشخص کنید که آن را کجا ایجاد کنید؛ در این مورد، در پوشه پروژه C#.
- [9]: پایگاه داده جدید با نام [dbarticles.sdf] نامگذاری شده است.
- [10]: زبان فرانسوی به عنوان زبان انتخاب شده است. این امر بر عملیات مرتبسازی تأثیر میگذارد.
- [11,12]: پایگاه داده میتواند با رمز عبور محافظت شود. در اینجا، رمز عبور «dbarticles» است.
- [13]: صفحه اطلاعات را تأیید کنید. پایگاه داده اکنون به صورت فیزیکی ایجاد خواهد شد:
![]() |
- [14]: نام پایگاه دادهای که به تازگی ایجاد شده است
- [15]: گزینه «ذخیره رمز عبور من» را علامت بزنید تا هر بار مجبور به وارد کردن مجدد آن نباشید
- [16]: اتصال را بررسی کنید
- [17]: همه چیز درست است
- [18]: صفحه تأیید اطلاعات
- [19]: اتصال در کاوشگر پایگاه داده نمایش داده میشود
- [20]: در حال حاضر، پایگاه داده هیچ جدولی ندارد. یکی ایجاد خواهیم کرد. یک مقاله دارای فیلدهای زیر خواهد بود:
- id: یک شناسه یکتا – کلید اصلی
- nom: نام کالا – یکتا
- prix: قیمت کالا
- stockactuel: سطح موجودی فعلی آن
- stockminimum: حداقل سطح موجودی که زیر آن باید کالا دوباره موجود شود
![]() |
- [21]: فیلد [id] از نوع عدد صحیح است و کلید اصلی [22] جدول است.
- [23]: این کلید اصلی از نوع Identity است. این مفهوم، مختص سرور SGBD SQL، نشان میدهد که کلید اصلی توسط خود SGBD تولید خواهد شد. در اینجا، کلید اصلی یک عدد صحیح خواهد بود که از 1 شروع شده و برای هر کلید جدید به اندازه 1 افزایش مییابد.
![]() |
- [24]: سایر فیلدها ایجاد میشوند. توجه داشته باشید که فیلد [nom] دارای محدودیت یکتایی بر روی the ، [25] است.
- [26]: به جدول نامی داده میشود
- [27]: پس از اعتبارسنجی ساختار جدول، جدول در پایگاه داده ظاهر میشود.
![]() |
- [28]: ما درخواست مشاهده محتویات جدول را داریم
- [29]: در حال حاضر خالی است
- [30]: ما آن را با مقداری داده پر میکنیم. یک سطر به محض اینکه به وارد کردن سطر بعدی میرویم، اعتبارسنجی میشود. فیلد [id] وارد نمیشود: این فیلد به طور خودکار هنگام اعتبارسنجی سطر ایجاد میشود.
اکنون باید پروژه را طوری پیکربندی کنیم که این پایگاه داده، که در حال حاضر در ریشه پروژه قرار دارد، به طور خودکار به پوشه زمان اجرای پروژه کپی شود:
![]() |
- [1]: ما درخواست مشاهده همه فایلها را داریم
- [2]: پایگاه داده [dbarticles.sdf] ظاهر میشود
- [3]: در پروژه گنجانده شده است
![]() |
- [4]: عملیات افزودن منبع داده به پروژه، یک جادوگر را راهاندازی میکند که در اینجا به آن نیازی نداریم [5].
- [6]: پایگاه داده اکنون بخشی از پروژه است. ما به حالت عادی بازمیگردیم [7].
- [8]: پروژه و پایگاه داده آن
- [9]: در ویژگیهای پایگاه داده، میتوانیم ببینیم ([10]) که آن بهطور خودکار به پوشهٔ زمان اجرای پروژه کپی خواهد شد. این همان جایی است که برنامهای که قرار است بنویسیم، آن را بازیابی خواهد کرد.
اکنون که یک پایگاه داده در دسترس داریم، میتوانیم استفاده از آن را آغاز کنیم. پیش از آن، بیایید چند یادآوری SQL را مرور کنیم.
9.3.2. چهار دستور اصلی زبان SQL
SQL (پرسوجوی ساختارمند زبان) یک زبان تا حدی استانداردشده برای پرسوجو و بهروزرسانی پایگاههای داده است. تمام گویشهای SGBD با بخش استانداردشده SQL مطابقت دارند اما افزونههای اختصاصی به زبان اضافه میکنند که از ویژگیهای خاصی از SGBD استفاده میکنند. ما قبلاً با دو مثال از این موضوع مواجه شدهایم: تولید خودکار کلیدهای اصلی و انواع دادههای مجاز برای ستونهای یک جدول اغلب به SGBD بستگی دارند.
چهار دستور اصلی زبان SQL که در اینجا ارائه میکنیم، استاندارد شده و توسط همه SGBDها پذیرفته شدهاند:
پرسوجویی که برای بازیابی دادهها از یک پایگاه داده استفاده میشود. تنها کلمات کلیدی در خط اول اجباری هستند؛ سایر کلمات اختیاریاند. کلمات کلیدی دیگری نیز وجود دارند که در اینجا نشان داده نشدهاند.
| |
یک سطر را در جدول درج میکند. (col1, col2, ...) ستونهای سطری را که باید با مقادیر (val1, val2, ...) پر شوند، مشخص میکند. | |
ردههای جدول را که شرط را برآورده میکنند، بهروزرسانی میکند (در صورت عدم وجود عبارت WHERE، تمام ردهها). برای این ردهها، ستون `coli` به مقدار `vali` تنظیم میشود. | |
تمام سطرهای جدول را که شرط را برآورده میکنند، حذف میکند |
ما قصد داریم یک برنامه کنسول بنویسیم که دستورات SQL را روی پایگاه داده [dbarticles] که قبلاً ایجاد کردهایم، صادر کند. در اینجا یک نمون از خروجی آمده است. از خواننده دعوت میشود تا دستورات صادرشده SQL و نتایج آنها را درک کند.
- خط ۱: رشته اتصال: این شامل تمام پارامترهای لازم برای اتصال به پایگاه داده است.
- خط ۳: محتوای جدول [articles] درخواست میشود
- خط ۱۶: یک سطر جدید درج میشود. توجه داشته باشید که فیلد id در این عملیات مقداردهی اولیه نمیشود، زیرا این SGBD است که مقدار این فیلد را تولید خواهد کرد.
- خط ۱۹: تأیید. خط ۲۸: سطر واقعاً اضافه شده است.
- خط ۳۰: قیمت کالایی که همین حالا اضافه شده است ۱۰ درصد افزایش مییابد.
- خط ۳۳: تأیید
- خط ۴۲: افزایش قیمت واقعاً انجام شده است
- خط ۴۴: ما آیتمی را که قبلاً اضافه کرده بودیم حذف میکنیم
- خط ۴۷: بررسی
- خطوط ۵۳–۵۵: آیتم دیگر وجود ندارد.
9.3.3. رابطهای پایه ADO.NET برای حالت متصل
بیایید به نمودار یک برنامه که از طریق یک کانکتور ADO.NET به یک پایگاه داده دسترسی پیدا میکند، بازگردیم:
![]() |
در حالت متصل، برنامه:
- یک اتصال به منبع داده باز میکند
- در حالت خواندن/نوشتن با منبع داده کار میکند
- ارتباط را قطع میکند
سه رابط ADO.NET عمدتاً در این عملیات دخیل هستند:
- IDbConnection، که خواص و متدهای اتصال را در بر میگیرد.
- IDbCommand، که خواص و متدهای دستور اجراشده SQL را در بر میگیرد.
- IDataReader، که خواص و متدهای نتیجهٔ فرمان Select SQL را در بر میگیرد.
رابط IDbConnection
برای مدیریت اتصال به پایگاه داده استفاده میشود. متدها (M) و ویژگیها (P) این رابط که ما از آنها استفاده خواهیم کرد به شرح زیر است:
نام | نوع | نقش |
P | رشته اتصال پایگاه داده. این مشخص میکند تمام پارامترهای لازم برای برقراری اتصال به یک پایگاه دادهٔ خاص. | |
M | ارتباط را به پایگاه داده تعریفشده توسط ConnectionString باز میکند | |
M | پایان دادن به اتصال | |
M | یک تراکنش را آغاز میکند. | |
P | وضعیت اتصال: ConnectionState.Closed, ConnectionState.Open, ConnectionState.Connecting, ConnectionState.Executing, ConnectionState.Fetching, ConnectionState.Broken |
اگر Connection کلاسی باشد که رابط IDbConnection را پیادهسازی میکند، اتصال به شرح زیر باز میشود:
رابط IDbCommand
برای اجرای یک دستور SQL یا یک رویهٔ ذخیرهشده استفاده میشود. متدهای M و ویژگیهای P این رابط که ما از آنها استفاده خواهیم کرد به شرح زیر هستند:
نام | نوع | نقش |
P | مشخص میکند که چه چیزی باید اجرا شود – مقادیر آن را از یک شمارششده میگیرد: - CommandType.Text: دستور SQL را که در خصوصیت CommandText تعریف شده است، اجرا میکند. این مقدار پیشفرض است. - CommandType.StoredProcedure: یک رویهٔ ذخیرهشده در پایگاه داده را اجرا میکند | |
P | - متن دستور SQL که در صورت برابر بودن CommandType با CommandType.Text اجرا میشود - نام رویهٔ ذخیرهشدهای که در صورت برابر بودن CommandType با CommandType.StoredProcedure اجرا میشود | |
P | اتصال IDbConnection که برای اجرای دستور SQL استفاده میشود | |
P | معامله IDbTransaction که در آن فرمان SQL اجرا میشود | |
P | فهرست پارامترها برای یک فرمان پیکربندیشده SQL. فرمان `update articles set price=price*1.1 where id=@id` دارای پارامتر @id است. | |
M | برای اجرای یک دستور SQL Select. این یک شیء IDataReader را بازمیگرداند که نمایانگر نتیجه Select است. | |
M | برای اجرای دستور SQL Update, Insert, Delete. این دستور تعداد ردیفهای تحت تأثیر عملیات (بهروزرسانیشده، درجشده، حذفشده) را بازمیگرداند. | |
M | برای اجرای دستوری SQL Select، که تنها یک نتیجه را بازمیگرداند، مانند: select count(*) from articles. | |
M | برای ایجاد پارامترها IDbParameter برای یک فرمان پیکربندیشده SQL. | |
M | اجازه میدهد تا اجرای یک پرسوجوی پارامتریک هنگام اجرای مکرر با پارامترهای مختلف بهینهسازی شود. |
اگر Command کلاسی باشد که رابط IDbCommand را پیادهسازی میکند، اجرای یک فرمان SQL بدون تراکنش شکل زیر را خواهد داشت:
رابط IDataReader
برای محصور کردن نتایج یک فرمان SQL یا Select استفاده میشود. یک شیء IDataReader نمایانگر جدولی با سطرها و ستونها است که بهصورت متوالی پردازش میشوند: ابتدا سطر اول، سپس سطر دوم و غیره. متدهای M و ویژگیهای P این رابط که ما استفاده خواهیم کرد به شرح زیر هستند:
نام | نوع | نقش |
P | تعداد ستونها در جدول IDataReader | |
M | GetName(i) نام ستون شماره i در جدول IDataReader را برمیگرداند. | |
P | Item[i] نمایانگر شماره ستون i از ردیف جاری در جدول IDataReader است. | |
M | به سطر بعدی جدول IDataReader میرود. اگر عملیات خواندن با موفقیت انجام شود، مقدار بولی True را برمیگرداند، در غیر این صورت False. | |
M | جدول IDataReader را میبندد. | |
M | GetBoolean(i): مقدار بولی ستون شماره i در ردیف جاری جدول IDataReader را برمیگرداند. سایر روشهای مشابه به شرح زیر هستند: GetDateTime, GetDecimal, GetDouble, GetFloat, GetInt16, GetInt32, GetInt64, GetString. | |
M | Getvalue(i): مقدار ستون شماره i در ردیف جاری جدول IDataReader را به نوع object برمیگرداند. | |
M | IsDBNull(i) مقدار True را بازمیگرداند اگر ستون شماره i از ردیف جاری در جدول IDataReader فاقد مقدار باشد، که با مقدار SQL NULL نشان داده میشود. |
یک پرسوجو روی یک شیء IDataReader اغلب به شکل زیر است:
9.3.4. مدیریت خطا
بیایید بار دیگر نگاهی به معماری یک برنامه مبتنی بر پایگاه داده بیندازیم:
![]() |
لایه [dao] ممکن است در حین کار با پایگاه داده با خطاهای متعددی مواجه شود. این خطاها به صورت استثناهایی که توسط کانکتور ADO.NET پرتاب میشوند، گزارش خواهند شد. کد در لایه [dao] باید این خطاها را مدیریت کند. هر عملیاتی که شامل پایگاه داده میشود باید در داخل یک بلوک try/catch/finally انجام شود تا هرگونه استثنا را رهگیری و مدیریت کرده و منابع لازم را آزاد کند. بنابراین، کد نشان داده شده در بالا برای پردازش نتیجه یک فرمان Select به شکل زیر درمیآید:
در هر صورت، اشیاء IDataReader و IDbConnection باید بسته شوند. به همین دلیل است که آنها در داخل دستورات finally بسته میشوند.
بستن اتصال و شیء IDataReader را میتوان با استفاده از عبارت using خودکار کرد:
- خط ۳: کلمهي `using` تضمین میکند که اتصال بازشده در بلوک `using(...){...}`، صرفنظر از نحوه خروج از بلوک (به صورت عادی یا به دلیل پرتاب یک استثنا)، در خارج از آن بسته خواهد شد. این کار یک finally را ذخیره میکند، اما فایده در این صرفهجویی جزئی نیست. استفاده از یک using توسعهدهنده را از بستن اتصال به صورت دستی بینیاز میکند. با این حال، فراموش کردن بستن یک اتصال ممکن است نادیده گرفته شود و باعث شود برنامه به روشی ظاهراً تصادفی «کرش» کند، هر زمان که SGBD به حداکثر تعداد اتصالات باز پشتیبانیشده برسد.
- خط ۱۱: ما برای بستن شیء IDataReader از رویهای مشابه پیروی میکنیم.
9.3.5. پیکربندی پروژهٔ نمونه
پروژه نهایی به شرح زیر خواهد بود:
![]() |
- [1]: پروژه دارای یک فایل پیکربندی [App.config] خواهد بود
- [2]: این کلاسها از دو فایل DLL استفاده میکنند که بهصورت پیشفرض ارجاع نشدهاند و بنابراین باید به ارجاعات پروژه اضافه شوند:
- [System.Configuration] برای استفاده از فایل پیکربندی [App.config]
- [System.Data.SqlServerCe] برای استفاده از پایگاه داده SQL Server Compact
- [3, 4]: توضیح میدهد چگونه ارجاعات را به یک پروژه اضافه کنیم.
- [5, 6]: توضیح میدهد چگونه فایل [App.config] را به یک پروژه اضافه کنید.
فایل پیکربندی [App.config] به شرح زیر خواهد بود:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbSqlServerCe" connectionString="Data Source=|DataDirectory|\dbarticles.sdf;Password=dbarticles;" />
</connectionStrings>
</configuration>
- خطوط ۳–۵: تگ جمع <connectionStrings> رشتههای اتصال پایگاه داده را تعریف میکند. یک رشته اتصال به شکل "parameter1=value1;parameter2=value2;..." است. این پارامترها تمام موارد لازم برای برقراری ارتباط با یک پایگاه داده خاص را مشخص میکنند. این رشتههای اتصال برای هر SGBD متفاوت هستند. سایت [http://www.connectionstrings.com/] قالب این رشتهها را برای فایلهای اصلی SGBD فراهم میکند.
- خط ۴: یک رشته اتصال خاص را تعریف میکند، در این مورد برای پایگاه داده SQL Server Compact dbarticles.sdf که قبلاً ایجاد کردیم:
- name = نام رشته اتصال. این نام است که برنامه C# از طریق آن رشته اتصال را بازیابی میکند
- connectionString: رشته اتصال برای پایگاه داده SQL Server Compact
- DataSource: مسیر پایگاه داده را مشخص میکند. سینتکس |DataDirectory| به پوشه اجرای پروژه اشاره دارد.
- Password: رمز عبور پایگاه داده. این پارامتر در صورتی که رمز عبوری وجود نداشته باشد، حذف میشود.
کد C# برای بازیابی رشته اتصال قبلی به شرح زیر است:
string connectionString = ConfigurationManager.ConnectionStrings["dbSqlServerCe"].ConnectionString;
- ConfigurationManager کلاس DLL [System.Configuration] است که امکان استفاده از فایل [App.config] را فراهم میکند.
- ConnectionsStrings["nom"].ConnectionString: به ویژگی connectionString از < add name="name" connectionString="..."> تگ در بخش <connectionStrings> از [App.config]
پروژه اکنون پیکربندی شده است. اکنون کلاس [Program.cs] را بررسی خواهیم کرد، نمونهای از آن را قبلاً در حال اجرا دیدیم.
9.3.6. برنامهٔ نمونه
برنامه [program.cs] به شرح زیر است:
using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.Text;
using System.Text.RegularExpressions;
using System.Configuration;
namespace Chap7 {
class SqlCommands {
static void Main(string[] args) {
// برنامه کنسول – پرسوجوهای SQL را که از طریق صفحهکلید وارد شدهاند اجرا میکند
// روی یک پایگاه داده که رشته اتصال آن از یک فایل پیکربندی گرفته میشود
// پردازش فایل پیکربندی [App.config]
string connectionString = null;
try {
connectionString = ConfigurationManager.ConnectionStrings["dbSqlServerCe"].ConnectionString;
} catch (Exception e) {
Console.WriteLine("Erreur de configuration : {0}", e.Message);
return;
}
//نمایش رشته اتصال
Console.WriteLine("Chaîne de connexion à la base : [{0}]\n", connectionString);
// یک فرهنگ لغت از دستورات SQL پذیرفتهشده ساخته میشود
string[] commandesSQL = new string[] { "select", "insert", "update", "delete" };
Dictionary<string, bool> dicoCommandes = new Dictionary<string, bool>();
for (int i = 0; i < commandesSQL.Length; i++) {
dicoCommandes.Add(commandesSQL[i], true);
}
//خواندن و اجرای دستورات SQL وارد شده از طریق صفحهکلید
string requête = null; //متن پرسوجو SQL
string[] champs; // fields in the query
Regex modèle = new Regex(@"\s+"); // مجموعهای از فضاها
//حلقه برای وارد کردن و اجرای دستورات SQL تایپشده روی صفحهکلید
while (true) {
// درخواست پرسوجو
Console.Write("\nRequête SQL (rien pour arrêter) : ");
requête = Console.ReadLine().Trim().ToLower();
// تمام شد؟
if (requête == "")
break;
// شکستن پرسوجو به فیلدها
champs = modèle.Split(requête);
// آیا پرسوجو معتبر است؟
if (champs.Length == 0 || ! dicoCommandes.ContainsKey(champs[0])) {
// پیام خطا
Console.WriteLine("Requête invalide. Utilisez select, insert, update, delete ou rien pour arrêter");
// پرسوجوی بعدی
continue;
}
// اجرای پرسوجو
if (champs[0] == "select") {
ExecuteSelect(connectionString, requête);
} else
ExecuteUpdate(connectionString, requête);
}
}
// اجرای یک پرسوجوی بهروزرسانی
static void ExecuteUpdate(string connectionString, string requête) {
...
}
//اجرای یک پرسوجوی SELECT
static void ExecuteSelect(string connectionString, string requête) {
....
}
}
}
- خطوط ۱–۶: فضاهای نامی مورد استفاده در برنامه. مدیریت پایگاه دادهٔ فشردهٔ SQL Server نیازمند فضای نامی [System.Data.SqlServerCe] در خط ۳ است. در اینجا وابستگی به یک فضای نام (namespace) خاص برای SGBD وجود دارد. بنابراین اگر SGBD تغییر کند، برنامه باید اصلاح شود.
- خط ۱۸: رشته اتصال پایگاه داده از فایل [App.config] خوانده شده و در خط ۲۵ نمایش داده میشود. این رشته برای برقراری ارتباط با پایگاه داده استفاده خواهد شد.
- خطوط ۲۸–۳۲: یک فرهنگ لغت که نام چهار دستور مجاز SQL را ذخیره میکند: select، insert، update، delete.
- خطوط ۴۰–۶۲: حلقه برای وارد کردن دستورات SQL تایپشده روی صفحهکلید و اجرای آنها روی پایگاه داده
- خط ۴۸: خط واردشده از طریق صفحهکلید به فیلدها تقسیم میشود تا اولین عبارت شناسایی شود که باید یکی از موارد زیر باشد: select, insert, update, delete
- خطوط ۵۰–۵۵: اگر پرسوجو نامعتبر باشد، یک پیام خطا نمایش داده میشود و برنامه به پرسوجوی بعدی میرود.
- خطوط ۵۷–۶۱: فرمان واردشده SQL اجرا میشود. فرآیند اجرا بسته به اینکه فرمان select باشد یا یکی از فرمانهای insert، update یا delete، متفاوت است. در حالت اول، دستور دادهها را از پایگاه داده بدون تغییر بازیابی میکند؛ در حالت دوم، پایگاه داده را بدون بازیابی هیچ دادهای بهروز میکند. در هر دو حالت، اجرای آن به متدی واگذار میشود که به دو پارامتر نیاز دارد:
- رشته اتصال که به آن امکان اتصال به پایگاه داده را میدهد
- دستور SQL که باید روی این اتصال اجرا شود
9.3.7. اجرای پرسوجوی SELECT
اجرای دستورات SQL نیازمند مراحل زیر است:
- اتصال به پایگاه داده
- ارسال دستورات SQL به پایگاه داده
- پردازش نتایج دستور SQL
- بستن اتصال
مراحل ۲ و ۳ به طور مکرر انجام میشوند؛ اتصال تنها پس از پردازش کامل پایگاه داده بسته میشود. اتصالات باز منابع محدودی در یک SGBD هستند. آنها باید حفظ شوند. بنابراین، ما همیشه در تلاش خواهیم بود تا طول عمر یک اتصال باز را محدود کنیم. در مثال مورد بررسی، اتصال پس از هر فرمان SQL بسته میشود. یک اتصال جدید برای فرمان بعدی SQL باز میشود. باز و بسته کردن یک اتصال پرهزینه است. برای کاهش این هزینه، برخی از سیستمهای SGBD مفهوم استخرهای اتصالات باز را ارائه میدهند: هنگامی که برنامه کاربردی راهاندازی میشود، N اتصال باز شده و به استخر اختصاص داده میشوند. آنها تا پایان عمر برنامه باز باقی میمانند. هنگامی که برنامه یک اتصال را باز میکند، یکی از N اتصال از پیش باز شده در استخر را دریافت میکند. هنگامی که اتصال را میبندد، آن را به سادگی به استخر بازمیگرداند. مزیت این سیستم این است که برای توسعهدهنده شفاف است: برای استفاده از استخر اتصال، نیازی به تغییر برنامه نیست. پیکربندی استخر اتصالات به SGBD بستگی دارد.
ابتدا به اجرای دستورات SQL و Select میپردازیم. متد ExecuteSelect در برنامه نمونه ما به شرح زیر است:
//اجرای یک پرسوجوی SELECT
static void ExecuteSelect(string connectionString, string requête) {
// پردازش هرگونه استثنا
try {
using (SqlCeConnection connexion = new SqlCeConnection(connectionString)) {
// باز کردن یک اتصال
connexion.Open();
// اجرای sqlCommand با یک پرسوجوی SELECT
SqlCeCommand sqlCommand = new SqlCeCommand(requête, connexion);
SqlCeDataReader reader= sqlCommand.ExecuteReader();
// نمایش نتایج
AfficheReader(reader);
}
} catch (Exception ex) {
// پیام خطا
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
//نمایش خواننده
static void AfficheReader(IDataReader reader) {
...
}
- خط ۲: متد دو پارامتر میگیرد:
- رشته اتصال [connectionString]، که امکان اتصال به پایگاه داده را فراهم میکند
- دستور SQL Select [requête] که باید روی این اتصال اجرا شود
- خط ۴: هر عملیات پایگاه داده میتواند یک استثنا تولید کند که ممکن است بخواهید آن را مدیریت کنید. این موضوع در اینجا اهمیت بیشتری دارد زیرا دستورات SQL وارد شده توسط کاربر ممکن است حاوی خطاهای نحوی باشند. ما باید بتوانیم کاربر را از این موضوع مطلع کنیم. بنابراین کل کد در یک بلوک try/catch قرار داده شده است.
- خط ۵: چندین مورد در اینجا وجود دارد:
- اتصال به پایگاه داده با رشته اتصال [connectionString] inicialize میشود. این اتصال هنوز باز نشده است؛ در خط ۷ باز خواهد شد.
- کلاز `using (Resource) {...}` یک ابزار دستوری است که تضمین میکند منبع `Ressource` – در این مورد، یک اتصال – پس از خروج از بلوکی که توسط `using` کنترل میشود، آزاد گردد.
- این اتصال از نوع اختصاصی SqlCeConnection است که مختص سرور کامپکت SGBD SQL میباشد.
- خط ۷: اتصال باز میشود. در این نقطه است که پارامترهای رشته اتصال مورد استفاده قرار میگیرند.
- خط ۹: یک دستور SQL از طریق یک شی اختصاصی SqlCeCommand صادر میشود. خط ۹ این شی را با دو اطلاعات اولیه راهاندازی میکند: اتصال مورد استفاده و دستور SQL که باید از طریق آن صادر شود. ابژه SqlCeCommand برای اجرای یک فرمان Select و همچنین یک فرمان Update، Insert یا Delete استفاده میشود. ویژگیها و متدهای آن در بخش 9.3.3 شرح داده شدهاند.
- خط ۱۰: یک فرمان SQL Select از طریق متد ExecuteReader از ... اجرا میشودشیء SqlCeCommand، که یک شیء IDataReader را بازمیگرداند، که متدها و ویژگیهای آن در بخش 9.3.3 توضیح داده شده است.
- خط ۱۲: نمایش نتایج توسط متد زیر AfficheReader انجام میشود:
//نمایشدهنده
static void AfficheReader(IDataReader reader) {
using (reader) {
// تحلیل نتایج
//-- ستونها
StringBuilder ligne = new StringBuilder();
int i;
for (i = 0; i < reader.FieldCount - 1; i++) {
ligne.Append(reader.GetName(i)).Append(",");
}
ligne.Append(reader.GetName(i));
Console.WriteLine("\n{0}\n{1}\n{2}\n", "".PadLeft(ligne.Length, '-'), ligne, "".PadLeft(ligne.Length, '-'));
//-- دادهها
while (reader.Read()) {
//پردازش سطر فعلی
ligne = new StringBuilder();
for (i = 0; i < reader.FieldCount; i++) {
ligne.Append(reader[i].ToString()).Append(" ");
}
Console.WriteLine(ligne);
}
}
}
- خط ۲: این متد یک شیء IDataReader را بهعنوان آرگومان خود میپذیرد. توجه کنید که در اینجا از یک رابط بهجای یک کلاس خاص استفاده کردهایم.
- خط ۳: عبارت using برای مدیریت خودکار بستهشدن شیء IDataReader استفاده میشود.
- خطوط ۸–۱۰: نام ستونهای جدول نتایج Select نمایش داده میشوند. اینها ستونهای coli از پرسوجوی `SELECT col1, col2, ... FROM table ...` هستند.
- خطوط 14–21: جدول نتایج بهصورت حلقهای پردازش شده و مقادیر هر سطر نمایش داده میشوند.
- خط ۱۸: نوع ستون شماره i در نتیجه نامشخص است زیرا جدول مورد پرسوجو ناشناخته است. بنابراین، سینتکس reader.GetXXX(i) – که در آن XXX نوع ستون شماره i است – قابل استفاده نیست، زیرا این نوع نامشخص است. بنابراین از عبارت reader.Item[i].ToString() برای به دست آوردن نمایش ستون شماره i به صورت یک رشته کاراکتری استفاده میکنیم. سینتکس reader.Item[i].ToString() میتواند به reader[i].ToString() خلاصه شود.
9.3.8. اجرای یک فرمان بهروزرسانی: INSERT, UPDATE, DELETE
کد متد ExecuteUpdate به شرح زیر است:
//اجرای درخواست بهروزرسانی
static void ExecuteUpdate(string connectionString, string requête) {
// پردازش هرگونه استثنا
try {
using (SqlCeConnection connexion = new SqlCeConnection(connectionString)) {
// باز کردن یک اتصال
connexion.Open();
// درخواست بهروزرسانی را با sqlCommand اجرا میکند
SqlCeCommand sqlCommand = new SqlCeCommand(requête, connexion);
int nbLignes = sqlCommand.ExecuteNonQuery();
//نمایش نتیجه
Console.WriteLine("Il y a eu {0} ligne(s) modifiée(s)", nbLignes);
}
} catch (Exception ex) {
// پیام خطا
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
ما بیان کردهایم که اجرای دستورات پرسوجو Select با اجرای دستورات بهروزرسانی Update متفاوت است، Insert، Delete، به جز روش شیء SqlCeCommand مورد استفاده: ExecuteReader برای Select، ExecuteNonQuery برای Update، Insert، Delete. ما تنها در مورد این روش آخر در کد بالا نظر میدهیم:
- خط ۱۰: دستور Update, Insert, Delete توسط متد ExecuteNonQuery از شیء SqlCeCommand اجرا میشود. در صورت موفقیت، این متد تعداد سطرهای بهروزرسانیشده، درجشده یا حذفشده را برمیگرداند.
- خط ۱۲: این تعداد سطر روی صفحه نمایش داده میشود
از خواننده دعوت میشود برای مشاهدهٔ نمونهای از اجرای این کد به بخش 9.3.2 مراجعه کند.
9.4. کانکتورهای دیگر: ADO.NET
کدی که بررسی کردهایم اختصاصی است: این کد به فضای نام [System.Data.SqlServerCe] وابسته است که برای سرور کامپکت SGBD SQL در نظر گرفته شده است. اکنون همان برنامه را با استفاده از کانکتورهای مختلف .NET میسازیم و میبینیم چه تغییراتی رخ میدهد.
9.4.1. کانکتور SQL Server 2005
معماری مورد استفاده به شرح زیر خواهد بود:
![]() |
نصب SQL Server 2005 در ضمیمهها تحت بخش 1.1 شرح داده شده است.
ما یک پروژه دوم را در همان راهحل قبلی ایجاد میکنیم، سپس پایگاه داده SQL Server 2005 را ایجاد میکنیم. نسخههای SGBD و SQL Server 2005 باید قبل از انجام عملیات زیر راهاندازی شوند:
![]() |
- [1]: یک پروژه جدید در راهحل فعلی ایجاد کرده و آن را به عنوان پروژهٔ جاری تنظیم کنید.
- [2]: یک اتصال جدید ایجاد کنید
- [3]: نوع اتصال را انتخاب کنید
![]() |
- [4]: سرور SGBD SQL را انتخاب کنید
- [5]: نتیجه انتخاب قبلی
- [6]: از دکمه [Browse] برای مشخص کردن محل ایجاد پایگاه داده SQL Server 2005 استفاده کنید. پایگاه داده در یک فایل .mdf جای گرفته است.
- [7]: دایرکتوری ریشه پروژه جدید را انتخاب کرده و پایگاه داده را [dbarticles.mdf] نامگذاری کنید.
- [8]: از احراز هویت ویندوز استفاده کنید.
- [9]: صفحهٔ اطلاعات را تأیید کنید
![]() |
- [11]: پایگاه داده سرور SQL
- [12]: یک جدول ایجاد کنید. این دقیقاً مشابه پایگاه داده SQL Server Compact است که قبلاً ایجاد شده بود.
- [13]: فیلد [id]
- [14]: فیلد [id] از نوع Identity است.
- [15,16]: فیلد [id] یک کلید اصلی است
![]() |
- [17]: سایر فیلدهای جدول
- [18]: هنگام ذخیره (Ctrl+S)، جدول را با نام [articles] نامگذاری کنید.
اکنون باید دادهها را در جدول وارد کنیم:
![]() | ![]() |
ما پایگاه داده را در پروژه وارد میکنیم:
![]() |
مراجع پروژه به شرح زیر است:
![]() |
فایل پیکربندی [App.config] به شرح زیر است:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="connectString1" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True;" />
<add name="connectString2" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;Connect Timeout=30;" />
</connectionStrings>
</configuration>
- خط ۴: رشته اتصال به پایگاه داده [dbarticles.mdf] با احراز هویت ویندوز
- خط ۵: رشته اتصال به پایگاه داده [dbarticles.mdf] با احراز هویت سرور SQL. [sa,msde] جفت (نام کاربری، رمز عبور) برای مدیر سرور SQL است، همانطور که در بخش 1.1 تعریف شده است.
برنامه [Program.cs] به شرح زیر تکامل مییابد:
using System.Data.SqlClient;
...
namespace Chap7 {
class SqlCommands {
static void Main(string[] args) {
...
//پردازش فایل پیکربندی [App.config]
string connectionString = null;
try {
connectionString = ConfigurationManager.ConnectionStrings["connectString2"].ConnectionString;
} catch (Exception e) {
...
}
...
//خواندن و اجرای دستورات SQL وارد شده از طریق صفحهکلید
...
}
//اجرای یک درخواست بهروزرسانی
static void ExecuteUpdate(string connectionString, string requête) {
// پردازش هرگونه استثنا
try {
using (SqlConnection connexion = new SqlConnection(connectionString)) {
// باز کردن یک اتصال
connexion.Open();
// دستور sqlCommand را با یک درخواست بهروزرسانی اجرا میکند
SqlCommand sqlCommand = new SqlCommand(requête, connexion);
int nbLignes = sqlCommand.ExecuteNonQuery();
// نمایش نتیجه
Console.WriteLine("Il y a eu {0} ligne(s) modifiée(s)", nbLignes);
}
} catch (Exception ex) {
....
}
}
// یک پرسوجوی SELECT را اجرا میکند
static void ExecuteSelect(string connectionString, string requête) {
// پردازش هرگونه استثنا
try {
using (SqlConnection connexion = new SqlConnection(connectionString)) {
// باز کردن اتصال
connexion.Open();
// اجرای sqlCommand با یک پرسوجوی SELECT
SqlCommand sqlCommand = new SqlCommand(requête, connexion);
SqlDataReader reader = sqlCommand.ExecuteReader();
//پردازش نتایج
...
}
} catch (Exception ex) {
...
}
}
}
}
- خط ۱: فضای نام [System.Data.SqlClient] شامل کلاسهای مورد استفاده برای مدیریت پایگاه داده SQL Server 2005 است
- خط ۲۴: اتصال از نوع SQLConnection است
- خط ۲۸: ابجکتی که دستورات SQL را در بر میگیرد از نوع SQLCommand است
- خط ۴۷: ابجکتی که نتیجه دستور Select از نوع SQL را در بر میگیرد، از نوع SQLDataReader است.
این کد به جز نام کلاسها، با آنچه در SGBD SQL Server Compact استفاده میشود، یکسان است. برای اجرای آن، میتوانید از (خط ۱۱) هر یک از دو رشته اتصال تعریفشده در [App.config] استفاده کنید.
9.4.2. اتصالدهنده MySQL5
معماری مورد استفاده به شرح زیر خواهد بود:
![]() |
نصب MySQL5 در ضمیمهها تحت بخش 1.2 شرح داده شده است و نصب کانکتور Ado.Net تحت بخش 1.2.5.
ما یک پروژه سوم را در همان راهحل قبلی ایجاد میکنیم و مراجع مورد نیاز آن را اضافه میکنیم:
![]() |
- [1]: پروژه جدید
- [2]: که به آن ارجاعات اضافه میکنیم
- [3]: DLL، [MySQL.Data]، کانکتور Ado.Net، MySql5، و همچنین [System.Configuration] و [4].
اکنون پایگاه داده [dbarticles] و جدول آن [articles] را ایجاد خواهیم کرد. فرآیندهای SGBD و MySQL5 باید راهاندازی شوند. علاوه بر این، کلاینت [Query Browser] راهاندازی میشود (به بخش 1.2.3 مراجعه کنید).
![]() |
- [1]: در [Query Browser]، در ناحیه [Schemata] [2]، روی [3] کلیک راست کرده تا یک اسکیمای جدید که به یک پایگاه داده اشاره میکند، ایجاد شود.
- [4]: پایگاه داده [dbarticles] نامگذاری خواهد شد. این را میتوان در [5] مشاهده کرد. در حال حاضر هیچ جدولی ندارد. ما قصد داریم اسکریپت زیر را اجرا کنیم: SQL:
- خط ۱: پایگاه داده [dbarticles] به پایگاه داده فعلی تبدیل میشود. دستورات بعدی SQL روی آن اجرا خواهند شد.
- خطوط ۴–۱۰: تعریف جدول [ARTICLES]. توجه داشته باشید که SQL مالک MySQL است. انواع ستونها و تولید خودکار کلید اصلی (ویژگی AUTO_INCREMENT) با آنچه در جداول SGBD و SQL Server Compact و Express مشاهده شد، متفاوت است.
- خطوط ۱۲–۱۴: درج سه خط
- خطوط 16–21: افزودن محدودیتهای یکپارچگی به ستونها.
این اسکریپت در [MySQL Query Browser] اجرا میشود:
![]() |
- در [MySQL Query Browser] و [6]، اسکریپت [7] بارگذاری میشود. این موضوع را میتوان در [8] مشاهده کرد. در [9]، این اسکریپت اجرا میشود.
![]() |
- در [10]، جدول [articles] ایجاد شده است. روی آن دوبار کلیک کنید. این کار پنجره [11] را که حاوی پرسوجوی [12] است، باز میکند و آماده اجرای آن توسط [13] است. در [14]، نتیجه اجرای آن است. ما در واقع سه سطر مورد انتظار را داریم. توجه داشته باشید که مقادیر در فیلد [ID] به طور خودکار تولید شدهاند (ویژگی AUTO_INCREMENT فیلد).
اکنون که پایگاه داده آماده است، میتوانیم به توسعهٔ برنامه در ویژوال استودیو بازگردیم.
![]() |
در [1]، برنامه [Program.cs] و فایل پیکربندی [App.config]. فایل دوم به شرح زیر است:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbArticlesMySql5" connectionString="Server=localhost;Database=dbarticles;Uid=root;Pwd=root;" />
</connectionStrings>
</configuration>
در خط ۴، اجزای رشته اتصال به شرح زیر هستند:
- سرور: نام ماشینی که SGBD، MySQL، localhost و c.a.d روی آن قرار دارند؛ این همان ماشینی است که برنامه روی آن اجرا خواهد شد.
- پایگاه داده: نام پایگاه داده در حال مدیریت، در این مورد dbarticles
- Uid: نام کاربری، در اینجا root
- Pwd: رمز عبور کاربر، در این مورد root. این دو مورد اطلاعات به مدیر ایجادشده در بخش 1.2 اشاره دارند.
برنامه [Program.cs] به جز جزئیات زیر، با نسخههای قبلی یکسان است:
MySql.Data.MySqlClient | |
MySqlConnection | |
MySqlCommand | |
MySqlDataReader |
برنامه از رشته اتصال با نام dbArticlesMySql5 در فایل [App.config] استفاده میکند. اجرای برنامه نتایج زیر را تولید میکند:
9.4.3. اتصالدهنده ODBC
معماری مورد استفاده به شرح زیر خواهد بود:
![]() |
مزیت کانکتورهای ODBC این است که یک رابط استاندارد را برای برنامههایی که از آنها استفاده میکنند، فراهم میکنند. بدین ترتیب، برنامه جدید قادر خواهد بود با استفاده از یک کد واحد، با هر SGBD که دارای کانکتور ODBC یا c.a.d باشد، و همچنین با اکثر دستگاههای SGBD ارتباط برقرار کند. عملکرد کانکتورهای ODBC به خوبی کانکتورهای «اختصاصی» نیست، که قادر به استفاده از تمام ویژگیهای یک SGBD خاص هستند. از سوی دیگر، این امر انعطافپذیری زیادی در برنامه فراهم میکند: شما میتوانید بدون تغییر کد، به یک SGBD دیگر سوئیچ کنید.
ما در حال بررسی مثالی هستیم که در آن برنامه بسته به رشته اتصال ارائهشده، از یک پایگاه داده MySQL5 یا یک پایگاه داده SQL Server Express استفاده میکند. در ادامه، ما فرض میکنیم که:
- نسخههای Express Server با شناسه SGBD، SQL و MySQL5 راهاندازی شدهاند
- که درایور ODBC برای MySQL5 روی سیستم موجود است (به بخش 1.2.6 مراجعه کنید). درایور برای SQL Server 2005 بهطور پیشفرض موجود است.
- پایگاههای دادهٔ مورد استفاده، همانهایی هستند که در بخش 9.4.2 برای پایگاه دادهٔ MySQL5 و در بخش 9.4.1 برای پایگاه دادهٔ SQL Server Express مشخص شدهاند.
پروژه جدید Visual Studio به شرح زیر است:
![]() |
در بالا، پایگاه داده SQL Server [dbarticles.mdf] که در بخش 9.4.1 ایجاد شده است، به پوشه پروژه کپی شده است.
فایل پیکربندی [App.config] به شرح زیر است:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbArticlesOdbcMySql5" connectionString="Driver={MySQL ODBC 3.51 Driver};Server=localhost;Database=dbarticles; User=root;Password=root;" />
<add name="dbArticlesOdbcSqlServer2005" connectionString="Driver={SQL Native Client};Server=.\SQLExpress;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;" />
</connectionStrings>
</configuration>
- خط ۴: رشته اتصال برای منبع ODBC MySQL5. این رشتهای است که قبلاً مورد بحث قرار گرفته و شامل پارامتر جدید «Driver» است که درایور ODBC مورد استفاده را تعریف میکند.
- خط ۵: رشته اتصال برای منبع ODBC SQL Server Express. این رشتهای است که قبلاً در یک مثال قبلی استفاده شده و پارامتر 'Driver' به آن اضافه شده است.
برنامه [Program.cs] از نظر عملکرد با نسخههای قبلی یکسان است، به جز جزئیات زیر:
System.Data.Odbc | |
OdbcConnection | |
OdbcCommand | |
OdbcDataReader |
برنامه از یکی از دو رشته اتصال تعریفشده در فایل [App.config] استفاده میکند. اجرای برنامه نتایج زیر را تولید میکند:
با رشته اتصال [dbArticlesOdbcSqlServer2005]:
با رشته اتصال [dbArticlesOdbcMySql5]:
9.4.4. کانکتور OLE DB
معماری مورد استفاده به شرح زیر خواهد بود:
![]() |
مانند کانکتورهای ODBC، کانکتورهای OLE و DB (پیوند و درج شیء DataBase) یک رابط استاندارد را برای برنامههایی که از آنها استفاده میکنند، فراهم میکنند. رانندههای ODBC دسترسی به پایگاههای داده را فراهم میکنند. منابع داده برای رانندههای OLE و DB متنوعتر هستند: پایگاههای داده، سیستمهای ایمیل، دایرکتوریها و غیره. هر منبع دادهای میتواند موضوع یک درایور OLE DB باشد، اگر فروشنده نرمافزار چنین تصمیم بگیرد. این دسترسی استاندارد به طیف گستردهای از دادهها را فراهم میکند.
در ادامه، مثالی را بررسی خواهیم کرد که در آن، برنامه بسته به رشته اتصال ارائهشده، از یک پایگاه داده ACCESS یا یک پایگاه داده SQL Server Express استفاده میکند. در ادامه، فرض میکنیم که سرور اکسپرس SGBD SQL راهاندازی شده و پایگاه داده مورد استفاده همان پایگاه داده مثال قبلی است.
پروژه جدید Visual Studio به شرح زیر است:
![]() |
- در [1]: فضای نام مورد نیاز برای کانکتورهای OLE و DB، [System.Data.OleDb] است که در مرجع فوق در [System.Data] موجود است. پایگاه داده SQL «Server [dbarticles.mdf]» از پروژه قبلی کپی شده است. پایگاه داده [dbarticles.mdb] با استفاده از Access ایجاد شده است.
- در [2]: مانند پایگاه داده سرور SQL، پایگاه داده ACCESS دارای ویژگی [Copy to Output Directory=Copy Always] است، به طوری که به طور خودکار به پوشه زمان اجرای پروژه کپی میشود.
پایگاههای داده ACCESS و [dbarticles.mdb] به شرح زیر هستند:
![]() |
در [1] ساختار جدول [articles] تعریف شده است و در [2] محتویات آن تعریف شده است.
فایل پیکربندی [App.config] به شرح زیر است:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbArticlesOleDbAccess" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\dbarticles.mdb;"/>
<add name="dbArticlesOleDbSqlServer2005" connectionString="Provider=SQLNCLI;Server=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;" />
</connectionStrings>
</configuration>
- خط ۴: رشته اتصال برای منبع OLE DB ACCESS. این شامل پارامتر Provider است که درایور OLE DB مورد استفاده و همچنین مسیر پایگاه داده را تعریف میکند
- خط ۵: رشته اتصال برای منبع Server Express OLE DB.
برنامه [Program.cs] به جز جزئیات زیر، با نسخههای قبلی یکسان است:
System.Data.OleDb | |
OleDbConnection | |
OleDbCommand | |
OleDbDataReader |
برنامه از یکی از دو رشته اتصال تعریفشده در فایل [App.config] استفاده میکند. اجرای برنامه با رشته اتصال [dbArticlesOleDbAccess] نتایج زیر را تولید میکند:
9.4.5. اتصالدهندهٔ عمومی
معماری مورد استفاده به شرح زیر خواهد بود:
![]() |
مانند کانکتورهای ODBC، OLE و DB، کانکتور عمومی یک رابط استاندارد را برای برنامههایی که از آن استفاده میکنند فراهم میکند، در حالی که عملکرد را بدون فدا کردن انعطافپذیری بهبود میبخشد. در واقع، کانکتور عمومی بر اساس کانکتورهای اختصاصی SGBD ساخته شده است. برنامه از کلاسهای کانکتور عمومی استفاده میکند. این کلاسها به عنوان واسطه بین برنامه و کانکتور اختصاصی عمل میکنند.
به عنوان مثال، هنگامی که برنامه درخواست اتصال به کانکتور عمومی را میدهد، کانکتور مذکور یک نمونه از IDbConnection را بازمیگرداند، رابط اتصال توصیفشده در بخش 9.3.3، که بسته به ماهیت درخواست انجامشده، توسط یک کلاس MySQLConnection یا SQLConnection پیادهسازی میشود. میگویند کانکتور عمومی دارای کلاسهای نوع فابریک است: از یک کلاس فابریک برای درخواست ایجاد اشیاء و ارائه مرجع (اشارهگر) به آنها استفاده میشود. از این رو این نام را یدک میکشد (فابریک = مکانی که اشیاء در آن تولید میشوند).
هیچ کانکتور عمومی برای تمام SGBD (آوریل ۲۰۰۸) وجود ندارد. برای پی بردن به اینکه کدام یک از آنها روی یک سیستم نصب شدهاند، میتوانید از برنامه زیر استفاده کنید:
using System;
using System.Data;
using System.Data.Common;
namespace Chap7 {
class Providers {
public static void Main() {
DataTable dt = DbProviderFactories.GetFactoryClasses();
foreach (DataColumn col in dt.Columns) {
Console.Write("{0}|", col.ColumnName);
}
Console.WriteLine("\n".PadRight(40, '-'));
foreach (DataRow row in dt.Rows) {
foreach (object item in row.ItemArray) {
Console.Write("{0}|", item);
}
Console.WriteLine("\n".PadRight(40, '-'));
}
}
}
}
- خط ۸: متد استاتیک [DbProviderFactories.GetFactoryClasses()] لیست کانکتورهای عمومی نصبشده را به شکل یک جدول پایگاه داده که در حافظه ذخیره شده است (DataTable)، بازمیگرداند.
- خطوط ۹–۱۱: نمایش نام ستونهای جدول dt:
- dt.Columns فهرست ستونهای جدول است. ستون C از نوع DataColumn است.
- [DataColumn]. ColumnName نام ستون است
- خطوط ۱۳–۱۸: نمایش سطرهای جدول dt:
- dt.Rows فهرست سطرها در جدول است. یک سطر L از نوع DataRow است
- [DataRow]. ItemArray آرایهای از اشیاء است که در آن هر شیء نمایانگر یک ستون در ردیف است
خروجی اجرای این کد روی سیستم من به شرح زیر است:
- خط ۱: جدول چهار ستون دارد. سه ستون اول برای ما در اینجا بیشترین کاربرد را دارند.
خروجی زیر نشان میدهد که کانکتورهای عمومی زیر در دسترس هستند:
نام | شناسه |
System.Data.Odbc | |
System.Data.OleDb | |
System.Data.OracleClient | |
System.Data.SqlClient | |
System.Data.SqlServerCe.3.5 | |
MySql.Data.MySqlClient |
یک کانکتور عمومی را میتوان در یک برنامه C# از طریق شناسهاش فراخوانی کرد.
ما در حال بررسی مثالی هستیم که در آن برنامه از پایگاههای دادهٔ مختلفی که تاکنون ساختهایم استفاده میکند. برنامه دو پارامتر دریافت خواهد کرد:
- پارامتر اول نوع SGBD مورد استفاده را مشخص میکند تا کتابخانه کلاس مناسب به کار گرفته شود
- پارامتر دوم پایگاه داده مورد استفاده را از طریق یک رشته اتصال مشخص میکند.
پروژه جدید Visual Studio به شرح زیر است:
![]() |
- در [1]: فضای نام مورد نیاز برای کانکتورهای عمومی [System.Data.common] است که در مرجع [System.Data] موجود است.
فایل پیکربندی [App.config] به شرح زیر است:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbArticlesSqlServerCe" connectionString="Data Source=|DataDirectory|\dbarticles.sdf;Password=dbarticles;" />
<add name="dbArticlesSqlServer" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;" />
<add name="dbArticlesMySql5" connectionString="Server=localhost;Database=dbarticles;Uid=root;Pwd=root;" />
<add name="dbArticlesOdbcMySql5" connectionString="Driver={MySQL ODBC 3.51 Driver};Server=localhost;Database=dbarticles; User=root;Password=root;Option=3;" />
<add name="dbArticlesOleDbSqlServer2005" connectionString="Provider=SQLNCLI;Server=.\SQLExpress;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;" />
<add name="dbArticlesOdbcSqlServer2005" connectionString="Driver={SQL Native Client};Server=.\SQLExpress;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;" />
<add name="dbArticlesOleDbAccess" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\dbarticles.mdb;Persist Security Info=True"/>
</connectionStrings>
<appSettings>
<add key="factorySqlServerCe" value="System.Data.SqlServerCe.3.5"/>
<add key="factoryMySql" value="MySql.Data.MySqlClient"/>
<add key="factorySqlServer" value="System.Data.SqlClient"/>
<add key="factoryOdbc" value="System.Data.Odbc"/>
<add key="factoryOleDb" value="System.Data.OleDb"/>
</appSettings>
</configuration>
- خطوط ۳–۱۱: رشتههای اتصال برای پایگاههای دادهٔ مختلف مورد استفاده.
- خطوط ۱۳–۱۷: نام کانکتورهای عمومی مورد استفاده
برنامه [Program.cs] به شرح زیر است:
...
using System.Data.Common;
namespace Chap7 {
class SqlCommands {
static void Main(string[] args) {
// برنامه کنسولی – پرسوجوهای SQL را که از طریق صفحهکلید وارد شدهاند اجرا میکند
// روی یک پایگاه داده که رشته اتصال آن از یک فایل پیکربندی به همراه نام کانکتور مرتبط SGBD به دست میآید
// بررسی پارامتر
if (args.Length != 2) {
Console.WriteLine("Syntaxe : pg factory connectionString");
return;
}
//پردازش فایل پیکربندی
string factory = null;
string connectionString = null;
DbProviderFactory connecteur = null;
try {
// کارخانه
factory = ConfigurationManager.AppSettings[args[0]];
//رشته اتصال
connectionString = ConfigurationManager.ConnectionStrings[args[1]].ConnectionString;
//یک کانکتور عمومی برای SGBD بازیابی میشود
connecteur = DbProviderFactories.GetFactory(factory);
} catch (Exception e) {
Console.WriteLine("Erreur de configuration : {0}", e.Message);
return;
}
//نمایشها
Console.WriteLine("Provider factory : [{0}]\n", factory);
Console.WriteLine("Chaîne de connexion à la base : [{0}]\n", connectionString);
...
// اجرای پرسوجو
if (champs[0] == "select") {
ExecuteSelect(connecteur,connectionString, requête);
} else
ExecuteUpdate(connecteur, connectionString, requête);
}
}
// اجرای یک پرسوجوی بهروزرسانی
static void ExecuteUpdate(DbProviderFactory connecteur, string connectionString, string requête) {
// رسیدگی به هرگونه استثنا
try {
using (DbConnection connexion = connecteur.CreateConnection()) {
//پیکربندی اتصال
connexion.ConnectionString = connectionString;
// برقرار کردن اتصال
connexion.Open();
//پیکربندی فرمان
DbCommand sqlCommand = connecteur.CreateCommand();
sqlCommand.CommandText = requête;
sqlCommand.Connection = connexion;
// اجرای پرسوجو
int nbLignes = sqlCommand.ExecuteNonQuery();
// نمایش نتیجه
Console.WriteLine("Il y a eu {0} ligne(s) modifiée(s)", nbLignes);
}
} catch (Exception ex) {
// پیام خطا
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
// اجرای پرسوجوی SELECT
static void ExecuteSelect(DbProviderFactory connecteur, string connectionString, string requête) {
// پردازش هرگونه استثنا
try {
using (DbConnection connexion = connecteur.CreateConnection()) {
// پیکربندی اتصال
connexion.ConnectionString = connectionString;
// باز کردن اتصال
connexion.Open();
// پیکربندی فرمان
DbCommand sqlCommand = connecteur.CreateCommand();
sqlCommand.CommandText = requête;
sqlCommand.Connection = connexion;
//اجرای پرسوجو
DbDataReader reader = sqlCommand.ExecuteReader();
//نمایش نتایج
...
}
} catch (Exception ex) {
// پیام خطا
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
}
}
- خطوط ۱۲–۱۴: برنامه دو پارامتر دریافت میکند: نام کانکتور عمومی و رشته اتصال پایگاه داده، به صورت کلیدها از فایل [App.config].
- خطوط ۲۳ و ۲۵: نام کانکتور عمومی و رشته اتصال از فایل [App.config] استخراج میشوند.
- خط ۲۷: کانکتور عمومی ایجاد میشود. از این نقطه به بعد، با SGBD مشخص مرتبط میشود.
- خطوط ۳۹–۴۳: اجرای دستور SQL واردشده از طریق صفحهکلید به دو متد واگذار میشود که موارد زیر به آنها ارسال میشوند:
- پرسوجوی قابل اجرا
- رشته اتصال که پایگاه داده را که پرسوجو روی آن اجرا خواهد شد، مشخص میکند
- رابط عمومی که کلاسهای مورد استفاده برای ارتباط با SGBD که مدیریت پایگاه داده را بر عهده دارد، مشخص میکند.
- خطوط 50–54: یک اتصال با استفاده از متد CreateConnection (خط 50) از کانکتور عمومی برقرار میشود، سپس با رشته اتصال برای پایگاه داده مورد نظر برای مدیریت پیکربندی میشود (خط 52). سپس باز میشود (خط 54).
- خطوط 56–58: شیء Command مورد نیاز برای اجرای دستور SQL با استفاده از متد CreateCommand کانکتور عمومی ایجاد میشود. سپس با متن دستور SQL که باید اجرا شود (خط 57) و اتصال مورد نظر برای اجرای آن (خط 58) پیکربندی میشود.
- خط ۶۰: فرمان بهروزرسانی SQL اجرا میشود
- خطوط ۷۴–۸۷: کد مشابهی یافت میشود. عنصر جدید در خط ۸۴ ظاهر میشود. شیء Reader که با اجرای دستور Select به دست میآید، از نوع DbDataReader است، که به همان شیوهای که اشیاء OleDbDataReader، OdbcDataReader، … که قبلاً با آنها مواجه شدهایم.
در اینجا چند مثال از اجرا آورده شده است.
با استفاده از پایگاه داده MySQL5:
![]() |
صفحهٔ ویژگیها (Properties) پروژهٔ [1] را باز کنید و برگهٔ [Debug] [2] را انتخاب کنید. در [3]، کلید کانکتور از خط 14 از [App.config]. در [4]، کلید رشته اتصال از خط 6 [App.config]. نتایج اجرای آن به شرح زیر است:
با پایگاه دادهٔ فشردهٔ SQL سرور:
![]() |
در [1]، کلید کانکتور در خط ۱۳ از [App.config]. در [2]، کلید رشته اتصال از خط ۴ از [App.config]. نتایج اجرای آن به شرح زیر است:
از خوانندگان دعوت میشود تا سایر پایگاههای داده را آزمایش کنند.
9.4.6. کدام کانکتور را باید انتخاب کنید؟
بیایید به معماری یک برنامه مبتنی بر پایگاه داده بازگردیم:
![]() |
ما انواع مختلفی از کانکتورها را بررسی کردهایم ADO.NET:
- کانکتورهای اختصاصی بهترین عملکرد را ارائه میدهند اما لایه [dao] را به کلاسهای اختصاصی وابسته میکنند. تغییر کانکتور SGBD به معنای تغییر لایه [dao] است.
- کانکتورهای ODBC، OLE و DB به شما امکان میدهند بدون تغییر لایه [dao] با چندین پایگاه داده کار کنید. آنها نسبت به کانکتورهای اختصاصی کارایی کمتری دارند.
- کانکتور عمومی بر پایه کانکتورهای اختصاصی است و در عین حال رابط استانداردی را برای لایه [dao] فراهم میکند.
بنابراین به نظر میرسد که کانکتور عمومی انتخاب ایدهآل است. با این حال، در عمل، کانکتور عمومی قادر نیست تمام ویژگیهای خاص SGBD را پشت یک رابط استاندارد پنهان کند. در بخش بعدی، به مفهوم پرسوجوی پارامتریک میپردازیم. با سرور SQL، یک پرسوجوی پارامتریک شکل زیر را دارد:
با MySQL5، همان پرسوجو به صورت زیر نوشته میشود:
بنابراین تفاوتی در نحو وجود دارد. ویژگی رابط IDbCommand که در بند 9.3.3 در رابطه با پارامترها توصیف شده است، به شرح زیر است:
فهرست پارامترها برای یک سفارش پیکربندیشده SQL. دستور `update articles set price=price*1.1 where id=@id` دارای پارامتر @id است. |
ویژگی Parameters از نوع IDataParameterCollection، یک رابط است. این ویژگی، مجموعه پارامترها را برای سفارش SQL CommandText نشان میدهد. ویژگی Parameters دارای متدی به نام Add برای افزودن پارامترهایی از نوع IDataParameter است که آن نیز یک رابط است. این ویژگی دارای خصوصیات زیر است:
- ParameterName: نام پارامتر
- DbType: نوع SQL پارامتر
- Value: مقداری که به پارامتر اختصاص داده شده است
- ...
نوع IDataParameter برای پارامترهای مرتبه SQL بسیار مناسب است
زیرا شامل پارامترهای نامگذاری شده است. میتوان از ویژگی ParameterName استفاده کرد.
نوع IDataParameter برای سفارش SQL مناسب نیست
از آنجا که پارامترها نامگذاری نشدهاند، بنابراین ترتیب افزودن پارامترها به مجموعه [IDbCommand.Parameters] مدنظر قرار میگیرد. در این مثال، چهار پارامتر باید به ترتیب nom، prix، stockactuel و stockminimum وارد شوند. در یک پرسوجو با پارامترهای نامگذاریشده، ترتیب افزودن پارامترها بیاهمیت است. در نهایت، توسعهدهنده نمیتواند SGBD را که هنگام مقداردهی اولیه پارامترهای یک پرسوجوی پارامتریک استفاده میکند، به طور کامل نادیده بگیرد. این یکی از محدودیتهای فعلی کانکتور عمومی است.
راهحلهایی وجود دارند که این محدودیتها را برطرف کرده و همچنین قابلیتهای جدیدی را به لایه [dao] ارائه میدهند:
![]() |
چارچوب مجموعهای از کتابخانههای کلاسی است که برای تسهیل یک رویکرد خاص در معماری برنامه طراحی شده است. چندین چارچوب وجود دارند که امکان ایجاد لایههای [dao] را فراهم میکنند که هم عملکرد بالایی دارند و هم در برابر تغییرات در SGBD مقاوم هستند:
- Spring.Net [http://www.springframework.net/]، که پیش از این در این سند مورد بحث قرار گرفته است، معادل کانکتور عمومی بررسیشده را بدون محدودیتهای آن، و همچنین ویژگیهای مختلفی را که دسترسی به دادهها را ساده میکنند، ارائه میدهد. نسخه جاوایی نیز در دسترس است.
- iBatis.Net [http://ibatis.apache.org] قدیمیتر و از نظر ویژگیها غنیتر از Spring.Net است. یک نسخه جاوا در دسترس است.
- NHibernate [http://www.hibernate.org/] یک پورت از نسخهٔ جاوای Hibernate است که در دنیای جاوا بسیار شناختهشده است. NHibernate لایه [dao] را قادر میسازد تا بدون صدور دستورات SQL با SGBD ارتباط برقرار کند. لایه [dao] با اشیاء Hibernate کار میکند. زبان پرسوجوی HBL (Hibernate Query Language) امکان پرسوجو بر روی اشیاء مدیریتشده توسط Hibernate را فراهم میکند. این اشیاء هستند که دستورات SQL را صادر میکنند. هیبرنت قادر است خود را با اشیاء SQL که مالک اشیاء SGBD هستند، تطبیق دهد.
- LINQ (زبان پرسوجوی INtegrated)، که در نسخه ۳.۵ یکپارچه شده و در C# 2008 موجود است. LINQ در پی NHibernate حرکت میکند، اما در حال حاضر (مه ۲۰۰۸) تنها سرورهای SGBD و SQL پشتیبانی میشوند. احتمال دارد این وضعیت در طول زمان تغییر کند. LINQ فراتر از NHibernate میرود: زبان پرسوجوی آن به شما امکان میدهد به سه نوع مختلف از منابع داده به شیوهای استاندارد پرسوجو کنید:
- مجموعههای اشیاء (LINQ به Objects)
- یک فایل XML (LINQ به XML)
- یک پایگاه داده (LINQ به SQL)
این چارچوبها در این سند پوشش داده نخواهند شد. با این حال، استفاده از آنها در کاربردهای حرفهای قویاً توصیه میشود.
9.5. پرسوجوهای پارامتریک
ما در بخش قبلی به پرسوجوهای پارامتریک اشاره کردیم. در اینجا آنها را همراه با یک مثال برای سرور کامپکت SGBD SQL ارائه میدهیم. پروژه به شرح زیر است
![]() |
- در [1]، پروژه. فقط [App.config]، [Article.cs] و [Parametres.cs] استفاده میشوند. همچنین به سرور پایه SQL و این [dbarticles.sdf] توجه کنید.
- در [2]، پروژه برای اجرای [Parametres.cs] پیکربندی شده است
- در [3]، پروژه به [Parametres.cs] ارجاع میدهد
فایل پیکربندی [App.config] رشته اتصال به پایگاه داده را تعریف میکند:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbArticlesSqlServerCe" connectionString="Data Source=|DataDirectory|\dbarticles.sdf;Password=dbarticles;" />
</connectionStrings>
</configuration>
فایل [Article.cs] کلاس [Article] را تعریف میکند. یک شیء Article برای بستهبندی اطلاعات یک سطر در جدول ARTICLES در پایگاه داده [dbarticles.sdf] استفاده خواهد شد:
namespace Chap7 {
class Article {
// ویژگیها
public int Id { get; set; }
public string Nom { get; set; }
public decimal Prix { get; set; }
public int StockActuel { get; set; }
public int StockMinimum { get; set; }
// سازندهها
public Article() {
}
public Article(int id, string nom, decimal prix, int stockActuel, int stockMinimum) {
Id = id;
Nom = nom;
Prix = prix;
StockActuel = stockActuel;
StockMinimum = stockMinimum;
}
}
}
برنامه [Parametres.cs] پرسوجوهای پارامتریک زیر را پیادهسازی میکند:
using System;
using System.Data.SqlServerCe;
using System.Text;
using System.Data;
using System.Configuration;
namespace Chap7 {
class Parametres {
static void Main(string[] args) {
//پردازش فایل پیکربندی
string connectionString = null;
try {
// رشته اتصال
connectionString = ConfigurationManager.ConnectionStrings["dbArticlesSqlServerCe"].ConnectionString;
} catch (Exception e) {
Console.WriteLine("Erreur de configuration : {0}", e.Message);
return;
}
//نمایشها
Console.WriteLine("Chaîne de connexion à la base : [{0}]\n", connectionString);
// ایجاد جدول آیتم
Article[] articles = new Article[5];
for (int i = 1; i <= articles.Length; i++) {
articles[i-1] = new Article(0, "article" + i, i * 100, i * 10, i);
}
// پردازش هرگونه استثنا
try {
//حذف آیتمهای موجود از پایگاه داده
ExecuteUpdate(connectionString, "delete from articles");
// نمایش آیتمها در جدول
ExecuteSelect(connectionString, "select id,nom,prix,stockactuel,stockminimum from articles");
// وارد کردن جدول اقلام در پایگاه داده
InsertArticles(connectionString, articles);
// موارد را از جدول نمایش میدهد
ExecuteSelect(connectionString, "select id,nom,prix,stockactuel,stockminimum from articles");
} catch (Exception ex) {
// پیام خطا
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
// وارد کردن یک جدول اقلام
static void InsertArticles(string connectionString, Article[] articles) {
using (SqlCeConnection connexion = new SqlCeConnection(connectionString)) {
// اتصال برقرار شد
connexion.Open();
// پیکربندی سفارش
string requête = "insert into articles(nom,prix,stockactuel,stockminimum) values(@nom,@prix,@sa,@sm)";
SqlCeCommand sqlCommand = new SqlCeCommand(requête, connexion);
sqlCommand.Parameters.Add("@nom",SqlDbType.NVarChar,30);
sqlCommand.Parameters.Add("@prix", SqlDbType.Money);
sqlCommand.Parameters.Add("@sa", SqlDbType.Int);
sqlCommand.Parameters.Add("@sm", SqlDbType.Int);
// کامپایل دستور
sqlCommand.Prepare();
// وارد کردن خطوط
for (int i = 0; i < articles.Length; i++) {
// ابتداییسازی پارامتر
sqlCommand.Parameters["@nom"].Value = articles[i].Nom;
sqlCommand.Parameters["@prix"].Value = articles[i].Prix;
sqlCommand.Parameters["@sa"].Value = articles[i].StockActuel;
sqlCommand.Parameters["@sm"].Value = articles[i].StockMinimum;
// اجرای پرسوجو
sqlCommand.ExecuteNonQuery();
}
}
}
//اجرای یک پرسوجوی بهروزرسانی
static void ExecuteUpdate(string connectionString, string requête) {
...
}
//اجرای پرسوجوی SELECT
static void ExecuteSelect(string connectionString, string requête) {
...
}
//نمایش خواننده
static void AfficheReader(IDataReader reader) {
...
}
}
ویژگی جدید در مقایسه با آنچه قبلاً دیدهایم، رویه [InsertArticles] در خطوط ۵۱–۷۵ است:
- خط ۵۱: رویه دو پارامتر دریافت میکند:
- رشته اتصال connectionString که به پروسیجر امکان میدهد به پایگاه داده متصل شود
- یک آرایه از اشیاء Article که باید به جدول پایگاه داده Articles اضافه شود
- خط ۵۶: پرسوجو برای درج یک شیء [Article]. این پرسوجو چهار پارامتر دارد:
- @nom: نام آیتم
- @prix: قیمت آن
- @sa: سطح موجودی فعلی آن
- @sm: حداقل موجودی آن
سینتکس این پرسوجوی پارامتریک مختص SQL Server Compact است. در پاراگراف قبلی دیدیم که با MySQL5، سینتکس به شکل زیر خواهد بود:
در SQL Server Compact، هر پارامتر باید با کاراکتر @ پیش از آن مشخص شود. نامهای پارامتر میتوانند آزادانه انتخاب شوند.
- خطوط ۵۸–۶۱: ویژگیهای هر یک از چهار پارامتر تعریف شده و بهصورت جداگانه به فهرست پارامترهای شیء SqlCeCommand که نماینده دستور SQL است که باید اجرا شود، اضافه میشوند.
در اینجا از متد [SqlCeCommand].Parameters.Add که شش امضا دارد استفاده میکنیم. ما از دو امضای زیر استفاده میکنیم:
Add(string parameterName, SQLDbType type)
پارامتر با نام parameterName را اضافه و پیکربندی میکند. این نام باید یکی از نامهای موجود در پرسوجوی پارامتریک پیکربندیشده باشد: (@name, ...). type به نوع SQL ستونی که پارامتر به آن مربوط میشود، اشاره دارد. انواع متعددی در دسترس هستند، از جمله موارد زیر:
نوع SQL | نوع C# | توضیح |
Int64 | ||
DateTime | ||
ممیز | ||
دوگانه | ||
Integer | ||
ممیز | ||
رشتهای | رشته با طول ثابت | |
رشته | رشته با طول متغیر | |
تکی |
Add(string parameterName, SQLDbType type, int size)
پارامتر سوم، size، اندازه ستون را تعیین میکند. این اطلاعات فقط برای انواع خاصی مانند SQL یا NVarChar کاربرد دارد.
- خط ۶۳: پرسوجوی پارامتریک کامپایل میشود. به این عمل همچنین «آمادهسازی» پرسوجو گفته میشود، که از همینرو نام این متد را تشکیل میدهد. این عملیات ضروری نیست؛ بلکه برای بهبود عملکرد گنجانده شده است. هنگامی که یک SGBD یک دستور SQL را اجرا میکند، پیش از اجرای آن، کارهای بهینهسازی خاصی را انجام میدهد. یک پرسوجوی پارامتریک برای اجرا شدن چندین بار با پارامترهای مختلف در نظر گرفته شده است. خود متن پرسوجو تغییر نمیکند. بنابراین، کار بهینهسازی تنها یکبار انجام میشود. برخی از SGBDها قابلیت «آمادهسازی» یا «کامپایل» پرسوجوهای پارامتریک را دارند. سپس یک طرح اجرایی برای این پرسوجو تعریف میشود. این همان مرحله بهینهسازی است که به آن اشاره کردیم. پس از کامپایل شدن، پرسوجو بارها اجرا میشود، هر بار با پارامترهای واقعی جدید اما با همان طرح اجرایی.
کامپایل کردن تنها مزیت پرسوجوهای پارامتریک نیست. بیایید دوباره به پرسوجویی که در حال بررسی آن هستیم نگاه کنیم:
ممکن است بخواهیم متن پرسوجو را بهصورت برنامهای بسازیم:
string requête="insert into articles(nom,prix,stockactuel,stockminimum) values('"+nom+"',"+prix+","+sa+","+sm+")";
در مثال بالا، اگر (name, price, sa, sm) برابر با ("article1", 100, 10, 1) باشد، پرسوجوی قبلی به این صورت درمیآید:
string requête="insert into articles(nom,prix,stockactuel,stockminimum) values('article1',100,10,1)";
حال، اگر (name, price, sa, sm) برابر با ("item1", 100, 10, 1) باشد، پرسوجوی قبلی به این صورت درمیآید:
string requête="insert into articles(nom,prix,stockactuel,stockminimum) values('l'article1',100,10,1)";
و به دلیل آپاستروف در نام l'article1 از نظر دستوری نادرست میشود. اگر nom نتیجه ورودی کاربر باشد، این بدان معناست که باید بررسی کنیم که آیا ورودی حاوی آپاستروف است یا خیر و در صورت وجود، آنها را خنثی کنیم. این حذف به SGBD بستگی دارد. مزیت پرسوجوی آماده این است که این کار را خود انجام میدهد. همین سهولت به تنهایی استفاده از پرسوجوی آماده را توجیه میکند.
- خطوط ۶۵–۷۳: رکوردهای جدول یکی یکی درج میشوند
- خطوط ۶۷–۷۰: به هر یک از چهار پارامتر پرسوجو از طریق خاصیت Value مقدار اختصاص داده میشود.
- خط ۷۲: پرسوجوی درج، که اکنون کامل شده است، به روش معمول اجرا میشود.
در اینجا مثالی از اجرا آورده شده است:
- خط ۳: پیامی که پس از حذف تمام سطرهای جدول نمایش داده میشود
- خطوط ۵–۷: نشان میدهد که جدول خالی است
- خطوط ۱۰–۱۸: نمایش جدول پس از درج ۵ آیتم
9.6. Transactions
9.6.1. اطلاعات عمومی
یک تراکنش توالیای از دستورات SQL است که بهصورت «اتمی» اجرا میشود:
- یا همه عملیات موفق میشوند
- یا یکی از آنها با شکست مواجه میشود که در این صورت تمام عملیاتهای قبلی لغو میشوند
در نهایت، عملیات درون یک تراکنش یا همگی با موفقیت اعمال میشوند، یا هیچکدام اعمال نمیشوند. هنگامی که کاربر کنترل تراکنش را در دست دارد، آن را با استفاده از یک دستور COMMIT نهایی میکند یا با استفاده از یک دستور ROLLBACK آن را لغو میکند.
در مثالهای قبلی ما، از یک تراکنش استفاده نکردیم. با این حال، تراکنشهایی وجود داشتند، زیرا در یک SGBD، یک دستور SQL همیشه در داخل یک تراکنش اجرا میشود. اگر کلاینت .NET خود یک تراکنش صریح را آغاز نکند، SGBD از یک تراکنش ضمنی استفاده میکند. در این صورت دو سناریوی رایج وجود دارد:
- هر دستور SQL به تنهایی بخشی از یک تراکنش است که توسط SGBD قبل از دستور آغاز شده و پس از آن بسته میشود. به این حالت «حالت اتوکامیت» گفته میشود. بنابراین همه چیز طوری پیش میرود که گویی کلاینت .NET برای هر سفارش SQL تراکنشهایی را انجام میدهد.
- SGBD در حالت autocommit نیست و با اولین دستور SQL یک تراکنش ضمنی را آغاز میکند، که مشتری .NET آن را خارج از یک تراکنش صادر میکند، و به مشتری اجازه میدهد آن را ببندد. تمام دستورات SQL که توسط کلاینت .NET صادر میشوند، سپس بخشی از تراکنش ضمنی هستند. این تراکنش ممکن است به دلایل مختلف پایان یابد: مشتری اتصال را میبندد، یک تراکنش جدید را آغاز میکند و غیره، اما این امر منجر به وضعیتی وابسته به SGBD میشود. این حالت باید اجتناب شود.
حالت پیشفرض معمولاً از طریق پیکربندی SGBD تنظیم میشود. برخی از SGBD بهطور پیشفرض در حالت autocommit هستند، در حالی که برخی دیگر نیستند. SQLServer Compact بهطور پیشفرض در حالت autocommit قرار دارد.
دستورات کاربران مختلف بهطور همزمان در تراکنشهای موازی اجرا میشوند. عملیات انجامشده توسط یک تراکنش ممکن است بر عملیات انجامشده توسط تراکنش دیگر تأثیر بگذارد. چهار سطح ایزولاسیون بین تراکنشهای کاربران مختلف وجود دارد:
- خواندن تعهدنشده
- خواندن تعهدشده
- خواندن قابل تکرار
- قابل سریالیسازی
خواندن تعهدنشده
این سطح ایزولاسیون به «خواندن کثیف» (Dirty Read) نیز معروف است. در اینجا مثالی از آنچه ممکن است در این حالت رخ دهد آورده شده است:
- کاربری به نام U1 تراکنشی را روی جدول T آغاز میکند
- کاربری به نام U2 تراکنشی را روی همان جدول T آغاز میکند
- کاربر U1 سطرهای جدول T را تغییر میدهد اما هنوز تغییرات را commit نکرده است
- کاربر U2 این تغییرات را «میبیند» و بر اساس آنچه میبیند تصمیمگیری میکند
- کاربر تراکنش خود را با استفاده از ROLLBACK رولبک میکند
میتوانیم ببینیم که در مرحله ۴، کاربر U2 بر اساس دادههایی که بعداً نادرست از آب درآمد، تصمیمی اتخاذ کرد.
خواندن تعهدشده
این سطح ایزولهسازی از تلهٔ توصیفشده در بالا جلوگیری میکند. در این حالت، کاربر U2 در مرحلهٔ ۴ تغییرات اعمالشده توسط کاربر U1 را در جدول T «نخواهد دید». آنها تنها پس از اینکه U1 تراکنش خود را commit کند، آنها را خواهند دید.
در این حالت، که به «خواندن غیرقابل تکرار» نیز معروف است، با این حال ممکن است حالات زیر پیش آید:
- کاربری به نام U1 یک تراکنش روی جدول T آغاز میکند
- کاربری به نام U2 یک تراکنش روی همان جدول T را آغاز میکند
- کاربر U2 یک SELECT را برای محاسبه میانگین ستون C برای ردیفهایی در T که یک شرط خاص را برآورده میکنند، اجرا میکند
- کاربر U1 مقادیر خاصی را در ستون C جدول T (UPDATE) تغییر میدهد و تغییرات را (COMMIT) commit میکند
- کاربر U2 همان SELECT را در مرحله ۳ تکرار میکند. آنها متوجه خواهند شد که میانگین ستون C به دلیل اصلاحات انجامشده توسط U1 تغییر کرده است.
اکنون، کاربر U2 تنها تغییراتی را میبیند که توسط U1 «commit» شدهاند. با این حال، در حالی که در همان تراکنش باقی میماند، دو عملیات یکسان (۳ و ۵) نتایج متفاوتی تولید میکنند. اصطلاح «خواندن غیرقابل تکرار» به این وضعیت اشاره دارد. این وضعیت برای هر کسی که میخواهد دیدی سازگار از جدول T داشته باشد، مشکلساز است.
خواندن قابل تکرار
در این سطح ایزولهسازی، به کاربر تضمین میشود که تا زمانی که در همان تراکنش باقی بماند، نتایج یکسانی از خواندنهای پایگاه داده خود به دست آورد. آنها روی یک اسنپشات کار میکنند که در آن تغییرات انجامشده توسط تراکنشهای دیگر—حتی آنهایی که کامیت شدهاند—هرگز منعکس نمیشوند. آنها تنها پس از تکمیل تراکنش خود با استفاده از COMMIT یا ROLLBACK، این تغییرات را مشاهده خواهند کرد.
با این حال، این سطح جداسازی هنوز کامل نیست. پس از عملیات ۳ فوق، سطرهای استعلامشده توسط کاربر U2 قفل میشوند. در طول عملیات ۴، کاربر U1 قادر نخواهد بود مقادیر ستون C این سطرها را (UPDATE) تغییر دهد. با این حال، آنها میتوانند سطرهای جدیدی (INSERT) اضافه کنند. اگر هر یک از سطرهای اضافه شده، شرط مورد آزمایش در مرحله ۳ را برآورده کنند، مرحله ۵ به دلیل سطرهای اضافه شده، میانگین متفاوتی نسبت به آنچه در مرحله ۳ به دست آمده است، تولید خواهد کرد. به این سطرها گاهی اوقات «سطرهای شبح» (ghost rows) گفته میشود.
برای حل این مشکل جدید، باید به ایزولهسازی «قابل سریسازی» (Serializable) سوئیچ کنید.
قابل سریالیسازی
در این سطح ایزولاسیون، تراکنشها به طور کامل از یکدیگر ایزوله هستند. این سطح تضمین میکند که نتیجه دو تراکنش که به طور همزمان انجام میشوند، همانند حالتی خواهد بود که یکی پس از دیگری انجام شده باشند. برای دستیابی به این هدف، در طول عملیات ۴ - جایی که کاربر U1 تلاش میکند ردیفهایی را اضافه کند که نتیجه تراکنش کاربر U1 را تغییر میدهد - از انجام این کار جلوگیری خواهد شد. یک پیام خطا به آنها اطلاع میدهد که درج امکانپذیر نیست. این کار پس از اینکه کاربر U2 تراکنش خود را commit کند، امکانپذیر خواهد شد.
چهار سطح ایزولاسیون تراکنش در همه موارد در دسترس نیستند. سطح جداسازی پیشفرض معمولاً «خواندن تعهدشده» است. سطح جداسازی موردنظر برای یک تراکنش را میتوان بهطور صریح زمانی که یک مشتری یک تراکنش صریح ایجاد میکند، مشخص کرد.NET.
9.6.2. مدیریت تراکنش API
یک اتصال رابط IDbConnection را که در بخش 9.3.3 توصیف شده است، پیادهسازی میکند. این رابط دارای متد زیر است:
M | شروع یک تراکنش. |
این متد دو امضا دارد:
- IDbTransaction BeginTransaction(): یک تراکنش را آغاز میکند و شیء IDbTransaction را که برای کنترل آن استفاده میشود، بازمیگرداند
- IDbTransaction BeginTransaction(IsolationLevel level): سطح ایزولهسازی مورد نظر برای تراکنش را بیشتر مشخص میکند. level مقادیر خود را از شمارش زیر میگیرد:
ممکن است تراکنش دادههایی را که توسط تراکنش دیگری نوشته شده و هنوز کمیت نشدهاند، بخواند – باید از این امر اجتناب کرد | |
این تراکنش نمیتواند دادههایی را که توسط تراکنش دیگری نوشته شده و هنوز commit نشده است، بخواند. با این حال، دادههایی که در طول تراکنش دو بار پیاپی خوانده میشوند ممکن است تغییر کنند (خوانشهای غیرقابل تکرار)، زیرا ممکن است تراکنش دیگری در این فاصله زمانی آن را اصلاح کرده باشد (ردههایی که خوانده میشوند قفل نمیشوند – فقط ردههایی که در حال بهروزرسانی هستند قفل میشوند). علاوه بر این، تراکنش دیگری ممکن است ردیفهایی (ردیفهای شبح) اضافه کرده باشد که در خوانش دوم گنجانده خواهند شد. | |
ردههایی که توسط تراکنش خوانده میشوند، به همان شیوهای قفل میشوند که ردههای در حال بهروزرسانی قفل میشوند. این کار مانع از آن میشود که تراکنش دیگری آنها را تغییر دهد. این امر مانع از افزودن ردیفها نمیشود. | |
جدولهای مورد استفاده توسط تراکنش قفل میشوند و مانع از افزودن ردیفهای جدید توسط تراکنش دیگر میشوند. گویی تراکنش بهتنهایی در حال اجراست. این امر عملکرد را کاهش میدهد زیرا تراکنشها دیگر بهصورت موازی اجرا نمیشوند. | |
این تراکنش روی کپیای از دادهها که در زمان T گرفته شده است، کار میکند. زمانی استفاده میشود که تراکنش فقط-خواندنی (read-only) باشد. نتیجهای مشابه serializable تولید میکند، در حالی که از هزینه آن اجتناب میکند. |
پس از شروع تراکنش، این تراکنش توسط ابجکت از نوع IDbTransaction کنترل میشود، که ما از این رابط، ویژگیهای P و متدهای M زیر را استفاده خواهیم کرد:
نام | نوع | نقش |
P | ارتباط IDbConnection که از تراکنش پشتیبانی میکند | |
M | مبادله را اعتبارسنجی میکند – نتایج دستورات SQL صادرشده در مبادله به پایگاه داده کپی میشوند. | |
M تأیید تراکنش را صادر میکند – نتایج دستورات QZXW2HTMLP004133ZQ | معامله را باطل میکند – نتایج دستورات SQL صادر شده در معامله به پایگاه داده کپی نمیشوند. |
9.6.3. برنامهٔ نمونه
به پروژه قبلی بازمیگردیم تا اکنون به برنامه [Transactions.cs] نگاهی بیندازیم:
![]() |
- در [1]، پروژه.
- در [2]، پروژه برای اجرای [Transactions.cs] پیکربندی شده است
کد برای [Transactions.cs] به شرح زیر است:
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlServerCe;
using System.Text;
namespace Chap7 {
class Transactions {
static void Main(string[] args) {
//پردازش فایل پیکربندی
string connectionString = null;
try {
// رشته اتصال
connectionString = ConfigurationManager.ConnectionStrings["dbArticlesSqlServerCe"].ConnectionString;
} catch (Exception e) {
Console.WriteLine("Erreur de configuration : {0}", e.Message);
return;
}
//نمایشها
Console.WriteLine("Chaîne de connexion à la base : [{0}]\n", connectionString);
//ایجاد آرایهای با دو عنصر و نام یکسان
Article[] articles = new Article[2];
for (int i = 1; i <= articles.Length; i++) {
articles[i - 1] = new Article(0, "article", i * 100, i * 10, i);
}
// پردازش هرگونه استثنا
try {
Console.WriteLine("Insertion sans transaction...");
//ابتدا جدول اقلام را بدون تراکنش در پایگاه داده درج میکنیم
ExecuteUpdate(connectionString, "delete from articles");
try {
InsertArticlesOutOfTransaction(connectionString, articles);
} catch (Exception ex) {
// پیام خطا
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
ExecuteSelect(connectionString, "select id,nom,prix,stockactuel,stockminimum from articles");
// ما همان فرایند را تکرار میکنیم، اما این بار درون یک تراکنش
Console.WriteLine("\n\nInsertion dans une transaction...");
ExecuteUpdate(connectionString, "delete from articles");
InsertArticlesInTransaction(connectionString, articles);
ExecuteSelect(connectionString, "select id,nom,prix,stockactuel,stockminimum from articles");
} catch (Exception ex) {
// پیام خطا
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
// وارد کردن جدول اقلام بدون تراکنش
static void InsertArticlesOutOfTransaction(string connectionString, Article[] articles) {
....
}
//درج جدولی از آیتمها در داخل یک تراکنش
static void InsertArticlesInTransaction(string connectionString, Article[] articles) {
....
}
//اجرای یک پرسوجوی بهروزرسانی
static void ExecuteUpdate(string connectionString, string requête) {
....
}
// اجرای پرسوجوی SELECT
static void ExecuteSelect(string connectionString, string requête) {
...
}
//نمایشدهندهٔ خواننده
static void AfficheReader(IDataReader reader) {
...
}
}
}
}
- خطوط ۱۲–۱۹: رشته اتصال به پایگاه داده SQLServer از [App.config] خوانده میشود
- خطوط ۲۵–۲۸: یک آرایه از دو شیء Article ایجاد میشود. این دو آیتم نام یکسانی دارند، «item». با این حال، پایگاه داده [dbarticles.sdf] برای ستون [nom] خود محدودیت یکتایی دارد (به بخش 9.3.1 مراجعه کنید). بنابراین، این دو مورد نمیتوانند همزمان در پایگاه داده وجود داشته باشند. دو آیتم با نام «article» به جدول articles اضافه میشوند. این امر باعث ایجاد مشکل در c.a.d خواهد شد. یک استثنا توسط SGBD پرتاب شده و از طریق کانکتور آن، ADO.NET، منتقل میشود. برای نمایش اثر تراکنش، این دو آیتم در دو محیط مختلف درج خواهند شد:
- اول، خارج از هر تراکنش. در اینجا مهم است به یاد داشته باشیم که در این مورد، SQLServer Compact در حالت autocommit (تأیید خودکار) عمل میکند، c.a.d. این ابزار هر دستور SQL را در یک تراکنش ضمنی درج میکند. رکورد اول درج خواهد شد. رکورد دوم درج نخواهد شد.
- سپس، در داخل یک تراکنش صریح که هر دو درج را در بر میگیرد. از آنجا که درج دوم با شکست مواجه میشود، درج اول نیز برگشت داده میشود. در نهایت، هیچ درجگذاری انجام نخواهد شد.
- خط ۳۳: جدول articles خالی میشود
- خط ۳۵: درج دو آیتم بدون یک تراکنش صریح. از آنجایی که میدانیم درج دوم یک استثنا (exception) ایجاد خواهد کرد، این مورد توسط یک بلوک try/catch مدیریت میشود
- خط ۴۶: جدول articles نمایش داده میشود
- خطوط ۴۴–۴۶: همین توالی تکرار میشود، اما این بار از یک تراکنش صریح برای انجام درجها استفاده میشود. استثنای پیشآمده در اینجا توسط متد InsertArticlesInTransaction مدیریت میشود.
- خطوط ۵۴–۵۶: متد InsertArticlesOutOfTransaction مشابه متد InsertArticles از برنامه [Parametres.cs] بررسیشده در بالا است.
- خطوط ۶۴–۶۶: متد ExecuteUpdate مشابه مورد قبل است. دستور SQL در یک تراکنش ضمنی اجرا میشود. این امر در اینجا ممکن است زیرا میدانیم که در این مورد، SQLServer Compact در حالت autocommit عمل میکند.
- خطوط ۶۹–۷۱: همین موضوع در مورد متد ExecuteSelect نیز صدق میکند.
متد InsertArticlesInTransaction به شرح زیر است:
// وارد کردن جدول مقالات در یک تراکنش
static void InsertArticlesInTransaction(string connectionString, Article[] articles) {
using (SqlCeConnection connexion = new SqlCeConnection(connectionString)) {
// باز کردن یک اتصال
connexion.Open();
//پیکربندی یک دستور
string requête = "insert into articles(nom,prix,stockactuel,stockminimum) values(@nom,@prix,@sa,@sm)";
SqlCeCommand sqlCommand = new SqlCeCommand(requête, connexion);
sqlCommand.Parameters.Add("@nom", SqlDbType.NVarChar, 30);
sqlCommand.Parameters.Add("@prix", SqlDbType.Money);
sqlCommand.Parameters.Add("@sa", SqlDbType.Int);
sqlCommand.Parameters.Add("@sm", SqlDbType.Int);
// کامپایل کردن دستور
sqlCommand.Prepare();
// معامله
SqlCeTransaction transaction = null;
try {
// شروع تراکنش
transaction = connexion.BeginTransaction(IsolationLevel.ReadCommitted);
// دستور SQL باید در این تراکنش اجرا شود
sqlCommand.Transaction = transaction;
// افزودن سطرها
for (int i = 0; i < articles.Length; i++) {
// ابتدایسازی پارامتر
sqlCommand.Parameters["@nom"].Value = articles[i].Nom;
sqlCommand.Parameters["@prix"].Value = articles[i].Prix;
sqlCommand.Parameters["@sa"].Value = articles[i].StockActuel;
sqlCommand.Parameters["@sm"].Value = articles[i].StockMinimum;
// اجرای پرسوجو
sqlCommand.ExecuteNonQuery();
}
// تأیید تراکنش
transaction.Commit();
Console.WriteLine("transaction validée...");
} catch {
// برگشت تراکنش
if (transaction != null)transaction.Rollback();
Console.WriteLine("transaction invalidée...");
}
}
}
ما تنها به تشریح تفاوتهای این روش و روش InsertArticles از برنامه [Parametres.cs] که قبلاً بررسی شده است، میپردازیم:
- خط 16: یک تراکنش SqlCeTransaction اعلام میشود.
- خطوط 17 و 35: بلوک try/catch برای رسیدگی به استثنایی که پس از دومین درج رخ خواهد داد.
- خط ۱۹: تراکنش ایجاد میشود. این تراکنش متعلق به اتصال جاری است.
- خط ۲۱: دستور پیکربندیشده SQL در داخل تراکنش قرار میگیرد
- خطوط ۲۳–۳۱: درجها انجام میشوند
- خط ۳۳: همه چیز به خوبی پیش رفت – تراکنش کامیت شد – درجها به طور دائم به پایگاه داده اضافه خواهند شد.
- خط ۳۷: مشکلی رخ داده است. اگر تراکنش وجود داشته باشد، برگشت داده میشود.
اجرا نتایج زیر را تولید میکند:
- خط ۴: نمایش داده شده توسط ExecuteUpdate("delete from articles") – هیچ ردیفی در جدول وجود نداشت
- خط ۵: استثنایی که توسط دومین درج ایجاد شد. پیام نشان میدهد که محدودیت UQ__ARTICLES__0000000000000010 بررسی نشده است. میتوانید با مشاهده ویژگیهای پایگاه داده اطلاعات بیشتری به دست آورید:
![]() |
- در [1] در داخل ویوی [Database Explorer] در ویژوال استودیو، یک اتصال به نام [2] به پایگاه داده [dbarticles.sdf] ایجاد شده است. این اتصال دارای یک نمایه با نام UQ__ARTICLES__0000000000000010 است. با کلیک راست روی این نمایه، میتوانید به ویژگیهای آن (ویژگیهای نمایه) دسترسی پیدا کنید.
- در [3,4]، میتوانیم ببینیم که ایندکس UQ__ARTICLES__0000000000000010 با یک محدودیت یکتا بر روی ستون [NOM] مطابقت دارد
- خطوط ۷–۱۱: نمایش جدول articles پس از دو درج. این جدول خالی نیست: اولین مقاله درج شده است.
- خط ۱۵: نمایش داده شده توسط ExecuteUpdate («حذف از مقالات») – یک سطر در جدول وجود داشت
- خط ۱۶: پیامی که توسط InsertArticlesInTransaction هنگام شکست تراکنش نمایش داده میشود.
- خطوط ۱۸–۲۰: نشان میدهند که هیچ درجدادهای انجام نشده است. متد Rollback برای تراکنش، اولین درج را لغو کرد.
9.7. روش ExecuteScalar
9.7.1. در میان متدهای رابط IDbCommand که در بخش 9.3.3 توصیف شدهاند، متد زیر وجود داشت:
M | برای اجرای دستوری SQL Select که تنها یک نتیجه بازمیگرداند، مانند: select count(*) from articles. |
در اینجا مثالی از نحوه استفاده از این روش را نشان میدهیم. بیایید به پروژه بازگردیم:
![]() |
- در [1]، پروژه.
- در [2]، پروژه برای اجرای [ExecuteScalar.cs] پیکربندی شده است
برنامه [ExecuteScalar.cs] به شرح زیر است:
...
namespace Chap7 {
class Scalar {
static void Main(string[] args) {
//پردازش فایل پیکربندی
string connectionString = null;
...
//نمایشها
Console.WriteLine("Chaîne de connexion à la base : [{0}]\n", connectionString);
// ایجاد یک جدول با ۵ آیتم
Article[] articles = new Article[5];
for (int i = 1; i <= articles.Length; i++) {
articles[i - 1] = new Article(0, "article" + i, i * 100, i * 10, i);
}
// پردازش هرگونه استثنا
try {
// وارد کردن جدول اقلام در یک تراکنش
ExecuteUpdate(connectionString, "delete from articles");
InsertArticlesInTransaction(connectionString, articles);
ExecuteSelect(connectionString, "select id,nom,prix,stockactuel,stockminimum from articles");
//محاسبه میانگین قیمت اقلام
decimal prixMoyen = (decimal)ExecuteScalar(connectionString, "select avg(prix) from articles");
Console.WriteLine("Prix moyen des articles={0}", prixMoyen);
// یا تعداد اقلام
int nbArticles = (int)ExecuteScalar(connectionString, "select count(id) from articles");
Console.WriteLine("Nombre d'articles={0}", nbArticles);
} catch (Exception ex) {
// پیام خطا
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
// وارد کردن جدول آیتم در یک تراکنش
static void InsertArticlesInTransaction(string connectionString, Article[] articles) {
...
}
// اجرای یک پرسوجوی بهروزرسانی
static object ExecuteScalar(string connectionString, string requête) {
using (SqlCeConnection connexion = new SqlCeConnection(connectionString)) {
// باز کردن یک اتصال
connexion.Open();
// اجرای یک پرسوجو
return new SqlCeCommand(requête, connexion).ExecuteScalar();
}
}
//اجرای یک پرسوجوی بهروزرسانی
static void ExecuteUpdate(string connectionString, string requête) {
...
}
// اجرای پرسوجوی SELECT
static void ExecuteSelect(string connectionString, string requête) {
...
}
//نمایش خواننده
static void AfficheReader(IDataReader reader) {
...
}
}
}
- خطوط 14–17: ایجاد یک آرایه با 5 عنصر
- خط ۲۲: جدول articles پاک میشود
- خط ۲۳: این آرایه با ۵ عنصر پر میشود
- خط ۲۴: جدول نمایش داده میشود
- خط ۲۶: میانگین قیمت اقلام را محاسبه میکند
- خط ۲۹: تعداد آیتمها را بازیابی میکند
- خط ۴۹: از متد [IDbCommand].ExecuteScalar() برای محاسبه هر یک از این مقادیر استفاده میکند.
نتایج اجرای برنامه به شرح زیر است:
خطوط ۱۵ و ۱۶ دو مقداری را که توسط متد ExecuteScalar بازگردانده شدهاند، نشان میدهند.
9.8. برنامهٔ نمونه – نسخهٔ ۷
به برنامهٔ مثال IMPOTS بازمیگردیم. آخرین نسخه در بخش 7.6 مورد بحث قرار گرفت. این برنامهٔ سهلایهٔ زیر بود:
![]() |
- لایه [ui] یک رابط کاربری گرافیکی ([A]) بود و لایه [dao] دادههای خود را از یک فایل متنی ([B]) بازیابی میکرد.
- Spring مسئول ایجاد نمونههای لایهها و یکپارچهسازی آنها در برنامه بود.
![]() |
ما در حال اصلاح لایه [dao] هستیم تا دادههای خود را از یک پایگاه داده بازیابی کند.
9.8.1. toرا پایگاه داده کنید
محتویات فایل متنی قبلی [B] در یک پایگاه داده MySQL5 ذخیره میشوند. ما نشان میدهیم چگونه این کار را انجام دهیم:
![]() |
- [1]: MySQL مدیر راهاندازی شده است
- [2,3]: در فیلد [Schemata]، کلیک راست کرده و گزینه [Create Schema] را برای ایجاد یک پایگاه داده جدید انتخاب کنید
- [4]: پایگاه داده با نام [bdimpots] نامگذاری خواهد شد
- [5]: به پایگاههای داده در ناحیه [Schemata] اضافه شده است.
![]() |
- [6,7]: روی جدول کلیک راست کرده و گزینه [Create New Table] را برای ایجاد جدول انتخاب کنید
- [8]: جدول با نام [tranches] نامگذاری خواهد شد. این جدول شامل ستونهای [id, limite, coeffR, coeffN] خواهد بود.
- [9,10]: [id] کلید اصلی از نوع INTEGER است و دارای ویژگی AUTO_INCREMENT [10]: این SGBD است که هنگام افزودن سطرها این ستون را پر میکند.
- ستونهای [limite, coeffR, coeffN] از نوع DOUBLE هستند.
- [11,12]: جدول جدید در برگه [Schema Tables] پایگاه داده ظاهر میشود.
![]() |
- [13,14]: برای وارد کردن دادهها به جدول
- [15]: [Query Browser] اجرا شده است
- [16]: دادهها برای ستونهای [limite, coeffR, coeffN] وارد و اعتبارسنجی شدهاند. ستون [id] توسط SGBD پر شده است. اعتبارسنجی با استفاده از [17] انجام شد.
![]() |
- درون [Query Browser] و [18]، [20] پرسوجوی [19] را اجرا میکند. این کار یک کاربر به نام 'admimpots' با رمز عبور 'mdpimpots' ایجاد میکند و به او تمام امتیازات را بر روی تمام اشیاء در پایگاه داده bdimpots (بر روی bdimpots.*). اعطا میکند. این کار به ما اجازه میدهد تا به جای حساب کاربری مدیر [root]، با حساب کاربری [admimpots] روی پایگاه داده [bdimpots] کار کنیم.
9.8.2. راه حل ویژوال استودیو
![]() |
ما رویکرد تشریحشده برای نسخهٔ ۵ از برنامهٔ نمونه را دنبال خواهیم کرد (به بخش ۶.۴ مراجعه کنید). بهتدریج راهحل ویژوال استودیوی زیر را خواهیم ساخت:
![]() |
- در [1]: راهحل ImpotsV7 شامل سه پروژه است، یکی برای هر یک از سه لایهٔ برنامه
- تا [2]: پروژه [dao] در لایه [dao] که اکنون از یک پایگاه داده استفاده خواهد کرد
- در [3]: پروژه [metier] در لایه [metier]. در اینجا ما از لایه [metier] نسخه ۵ که در بخش ۶.۴.۴ توصیف شده است، مجدداً استفاده میکنیم.
- در [4]: پروژه [ui] از لایه [ui]. در اینجا ما از لایه [ui] از نسخه 6، که در بخش 7.6 توصیف شده است، مجدداً استفاده میکنیم.
ما بر پایهٔ کارهای موجود، دو لایهٔ [ui] و [metier] را که پیشتر نوشته شدهاند، مجدداً استفاده میکنیم. این امر با معماری لایهبندیشدهٔ انتخابی امکانپذیر شده است. با این حال، ما به کد منبع لایههای [ui] و [metier] نیاز خواهیم داشت. در واقع، نمیتوان تنها با لایههای DLL کار را پیش برد. زمانی که در نسخه ۵، لایه DLL از لایه [metier] ایجاد شد، به لایه DLL از لایه [dao] وابسته بود. این وابستگی بهصورت کد سخت در لایه DLL از لایه [metier] گنجانده شده بود (نام لایه DLL از لایه [dao]، نسخه، توکن هویت و غیره). در نتیجه، DLL از نسخه 5 ([ImpotsV5-metier.dll]) فقط با DLL و [ImpotsV5-dao.dll] که با آنها کامپایل شده است، کار خواهد کرد. اگر DLL لایه [dao] تغییر کند، لایه [metier] باید دوباره کامپایل شود تا یک DLL جدید تولید شود. همین امر در مورد لایه [ui] نیز صدق میکند. لایههای [ui] و [metier] بنابراین تغییر نخواهند کرد، اما برای کار با لایه DLL از لایه جدید [dao] مجدداً کامپایل خواهند شد.
9.8.3. لایه [dao]
![]() |
![]() |
مراجع پروژه (به [1] در پروژه مراجعه کنید)
- nunit.framework: برای آزمایش NUnit
- System.Configuration: برای پردازش فایل پیکربندی [App.config]
- System.Data: زیرا ما از یک پایگاه داده استفاده میکنیم.
اشیاء (به [2] در پروژه مراجعه کنید)
کلاسهای [TrancheImpot] و [ImpotException] مربوط به نسخههای قبلی هستند.
لایه [dao] (به [3] در پروژه مراجعه کنید)
رابط [IImpotDao] تغییر نکرده است:
using Entites;
namespace Dao {
public interface IImpotDao {
// محدودههای مالیاتی
TrancheImpot[] TranchesImpot{get;}
}
}
کلاس پیادهسازی [DataBaseImpot] برای این رابط به شرح زیر است:
using System;
using System.Collections.Generic;
using System.Data.Common;
using Entites;
namespace Dao {
public class DataBaseImpot : IImpotDao {
// باندهای مالیاتی
private TrancheImpot[] tranchesImpot;
public TrancheImpot[] TranchesImpot { get { return tranchesImpot; } }
// تولیدکننده
public DataBaseImpot(string factory, string connectionString, string requête) {
// کارخانه: کارخانه هدف SGBD
//connectionString: رشته اتصال به پایگاه دادهٔ ردههای مالیاتی
// هر استثنا رسیدگی میشود
try {
//بازیابی یک کانکتور عمومی برای SGBD
DbProviderFactory connecteur = DbProviderFactories.GetFactory(factory);
using (DbConnection connexion = connecteur.CreateConnection()) {
//پیکربندی اتصال
connexion.ConnectionString = connectionString;
// اتصال را باز کنید
connexion.Open();
//پیکربندی فرمان
DbCommand sqlCommand = connecteur.CreateCommand();
sqlCommand.CommandText = requête;
sqlCommand.Connection = connexion;
// اجرای پرسوجو
List<TrancheImpot> listTrancheImpot = new List<TrancheImpot>();
using (DbDataReader reader = sqlCommand.ExecuteReader()) {
while (reader.Read()) {
// ایجاد یک رده مالیاتی جدید
listTrancheImpot.Add(new TrancheImpot() { Limite = reader.GetDecimal(0), CoeffR = reader.GetDecimal(1), CoeffN = reader.GetDecimal(2) });
}
}
// محدودههای مالیاتی به نمونه اضافه میشوند
tranchesImpot = listTrancheImpot.ToArray();
}
} catch (Exception ex) {
// استثناء را در نوع ImpotException محصور میکند
throw new ImpotException("Erreur de lecture des tranches d'impôt", ex) { Code = 101 };
}
}
}
}
- خط ۷: کلاس [DataBaseImpot] رابط [IImpotDao] را پیادهسازی میکند.
- خط ۱۰: پیادهسازی متد [TranchesImpot] این رابط. این متد به سادگی یک مرجع به آرایهٔ ردههای مالیاتی از خط ۹ بازمیگرداند. این آرایه توسط سازندهٔ کلاس ساخته خواهد شد.
- خط ۱۳: سازنده. این سازنده از یک کانکتور عمومی (به بخش ۹.۴.۵ مراجعه کنید) برای پرسوجو از پایگاه دادهٔ ردههای مالیاتی استفاده میکند. سازنده سه پارامتر میگیرد:
- نام «کارخانه» (factory) که از آن درخواست خواهد کرد تا کلاسها را برای اتصال به پایگاه داده، صدور دستورات SQL و پردازش نتیجه یک پرسوجوی SELECT به کار گیرد.
- رشته اتصال که باید برای اتصال به پایگاه داده استفاده کند
- دستور SELECT با شناسه SQL که باید برای بازیابی سطوح مالیاتی اجرا شود.
- خط ۱۹: یک کانکتور را از «کارخانه» درخواست میکند
- خط ۲۰: با استفاده از این کانکتور یک اتصال ایجاد میکند. اتصال ایجاد شده اما هنوز عملیاتی نیست
- خط ۲۲: رشته اتصال برای اتصال مقداردهی اولیه میشود. اکنون میتوانیم متصل شویم.
- خط ۲۴: متصل میکند
- خط ۲۶: از کانکتور یک شیء [DbCommand] درخواست میکند تا یک دستور SQL را اجرا کند
- خط ۲۷: دستور SQL را که باید اجرا شود مشخص میکند
- خط ۲۸: اتصال مورد نظر برای اجرا مشخص میشود
- خط ۳۰: یک لیست خالی [listTrancheImpot] از اشیاء نوع [TrancheImpot] ایجاد میشود.
- خط ۳۱: دستور SELECT SQL اجرا میشود
- خطوط ۳۲–۳۵: شیء [DbDataReader] حاصل از Select پردازش میشود. هر سطر از جدول نتایج Select برای نمونهسازی یک شیء از نوع [TrancheImpot] استفاده میشود که به لیست [listTrancheImpot] اضافه میگردد.
- خط ۳۸: لیست اشیاء از نوع [TrancheImpot] به آرایه در خط ۹ منتقل میشود.
- خطوط ۴۰–۴۳: هر استثنا در نوع [ImpotException] بستهبندی شده و کد خطای ۱۰۱ (دلخواه) به آن اختصاص داده میشود.
آزمون [Test1] (به [4] در پروژه مراجعه کنید)
کلاس [Test1] صرفاً محدودههای مالیاتی را روی صفحه نمایش میدهد. این همان کلاسی است که پیشتر در نسخهٔ ۵ (بخش ۶.۴.۳) استفاده شده است، با این تفاوت که عبارت مربوط به ایجاد لایهٔ [dao] (خط ۱۴) حذف شده است.
using System;
using Dao;
using Entites;
using System.Configuration;
namespace Tests {
class Test1 {
static void Main() {
//لایه ایجاد میشود [dao]
IImpotDao dao = null;
try {
// ایجاد لایه [dao]
dao = new DataBaseImpot(ConfigurationManager.AppSettings["factoryMySql5"], ConfigurationManager.ConnectionStrings["dbImpotsMySql5"].ConnectionString, ConfigurationManager.AppSettings["requete"]);
} catch (ImpotException e) {
// پیام خطا نمایش داده شد
string msg = e.InnerException == null ? null : String.Format(", Exception d'origine : {0}", e.InnerException.Message);
Console.WriteLine("L'erreur suivante s'est produite : [Code={0},Message={1}{2}]", e.Code, e.Message, msg == null ? "" : msg);
// برنامه خاتمه مییابد
Environment.Exit(1);
}
// محدودههای مالیاتی نمایش داده میشوند
TrancheImpot[] tranchesImpot = dao.TranchesImpot;
foreach (TrancheImpot t in tranchesImpot) {
Console.WriteLine("{0}:{1}:{2}", t.Limite, t.CoeffR, t.CoeffN);
}
}
}
}
خط ۱۴ از فایل پیکربندی زیر، [App.config]، استفاده میکند:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbImpotsMySql5" connectionString="Server=localhost;Database=bdimpots;Uid=admimpots;Pwd=mdpimpots;" />
</connectionStrings>
<appSettings>
<add key="requete" value="select limite, coeffr, coeffn from tranches"/>
<add key="factoryMySql5" value="MySql.Data.MySqlClient"/>
</appSettings>
</configuration>
- خط ۴: رشته اتصال به پایگاه داده MySQL5. توجه داشته باشید که کاربر [admimpots] است که اتصال را برقرار میکند.
- خط ۸: «فابریک» برای کار با SGBD MySQL5
- خط ۷: پرسوجوی SELECT SQL برای بازیابی سطوح مالیاتی.
پروژه برای اجرای [Test1.cs] پیکربندی شده است:

اجرای تست نتایج زیر را تولید میکند:
آزمون NUnit [NUnit1] (به [4] در پروژه مراجعه کنید)
تست واحد [NUnit1] همان تستی است که در نسخه ۵ (بخش ۶.۴.۳) استفاده شده است، به جز دستور ایجاد لایه [dao] (خط ۱۶).
using System;
using System.Configuration;
using Dao;
using Entites;
using NUnit.Framework;
namespace Tests {
[TestFixture]
public class NUnit1 : AssertionHelper{
// [dao] لایهای برای آزمایش
private IImpotDao dao;
// سازنده
public NUnit1() {
// ابتداییسازی لایه [dao]
dao = new DataBaseImpot(ConfigurationManager.AppSettings["factoryMySql5"], ConfigurationManager.ConnectionStrings["dbImpotsMySql5"].ConnectionString, ConfigurationManager.AppSettings["requete"]);
}
// آزمایش
[Test]
public void ShowTranchesImpot(){
// محدودههای مالیاتی نمایش داده میشوند
TrancheImpot[] tranchesImpot = dao.TranchesImpot;
foreach (TrancheImpot t in tranchesImpot) {
Console.WriteLine("{0}:{1}:{2}", t.Limite, t.CoeffR, t.CoeffN);
}
// برخی آزمایشها
Expect(tranchesImpot.Length,EqualTo(7));
Expect(tranchesImpot[2].Limite,EqualTo(14753).Within(1e-6));
Expect(tranchesImpot[2].CoeffR, EqualTo(0.191).Within(1e-6));
Expect(tranchesImpot[2].CoeffN, EqualTo(1322.92).Within(1e-6));
}
}
}
برای اجرای این تست واحد، پروژه باید از نوع [Class Library] باشد:
![]() |
- به [1]: نوع پروژه تغییر کرده است
- به [2]: DLL تولیدشده به [ImpotsV7-dao.dll] نامگذاری خواهد شد
- در [3]: پس از ایجاد پروژه (F6)، پوشه [dao/bin/Release] حاوی DLL و [ImpotsV7-dao.dll] است. همچنین شامل فایل پیکربندی [App.config] است که به [nom DLL].config تغییر نام داده شده است. این در ویژوال استودیو استاندارد است.
سپس فایلهای DLL و [ImpotsV7-dao.dll] در فریمورک NUnit بارگذاری و اجرا میشوند:
![]() |
- در [1]: تستها با موفقیت انجام شدند. ما اکنون لایه [dao] را عملیاتی میدانیم. DLL آن حاوی تمام کلاسهای پروژه، از جمله کلاسهای تست است. اینها غیرضروری هستند. ما در حال بازسازی DLL هستیم تا کلاسهای تست را حذف کنیم.
- به [2]: پوشه [tests] از پروژه حذف شده است
- به عنوان [3]: پروژه جدید. این توسط F6 برای تولید یک DLL جدید بازتولید میشود. این DLL است که توسط لایههای [metier] و [ui] اپلیکیشن استفاده خواهد شد.
9.8.4. لایه [metier]
![]() |
![]() |
- در [1]، پروژه [metier] به پروژه فعال در راهحل تبدیل شده است
- به [2]: ارجاعات پروژه. توجه کنید به ارجاع به DLL در لایهٔ قبلاً ایجادشده [dao]. این رویه برای افزودن یک ارجاع در نسخهٔ ۵، در بخش ۶.۴.۴ توضیح داده شده است.
- در [3]: لایه [metier]. این همان مورد نسخه 5 است که در بند 6.4.4 توضیح داده شده است.
پروژه [metier] برای تولید DLL پیکربندی شده است:
![]() |
- [1]: پروژه از نوع «کتابخانه کلاسی» است
- [2]: تولید پروژه، فایلهای DLL، [ImpotsV7-metier.dll] و [3] را تولید خواهد کرد.
پروژه تولید شده است (F6).
9.8.5. لایه [ui]
![]() |
![]() |
- در [1]، پروژه [ui] به پروژه فعال در راهحل تبدیل شده است
- در [2]: ارجاعات پروژه. توجه کنید به ارجاعات در DLL به لایههای [dao] و [metier].
- در [3]: لایه [ui]. این همان لایهای است که در نسخهٔ ۶ در بند ۷.۶ توصیف شده است.
- در [4]، فایل پیکربندی [App.config] مشابه نسخهٔ ۶ است. تنها در نحوهٔ نمونهسازی لایهٔ [dao] توسط Spring تفاوت دارد:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object name="dao" type="Dao.DataBaseImpot, ImpotsV7-dao">
<constructor-arg index="0" value="MySql.Data.MySqlClient"/>
<constructor-arg index="1" value="Server=localhost;Database=bdimpots;Uid=admimpots;Pwd=mdpimpots;"/>
<constructor-arg index="2" value="select limite, coeffr, coeffn from tranches"/>
</object>
<object name="metier" type="Metier.ImpotMetier, ImpotsV7-metier">
<constructor-arg index="0" ref="dao"/>
</object>
</objects>
</spring>
</configuration>
- خطوط ۱۱–۲۵: پیکربندی Spring
- خطوط ۱۵–۲۴: اشیایی که توسط Spring ایجاد شدهاند
- خطوط ۱۶–۲۰: نمونهسازی لایه [dao]
- خط ۱۶: لایه [dao] توسط کلاس [Dao.DataBaseImpot] که در داخل DLL [ImpotsV7-Dao] قرار دارد، نمونه سازی میشود.
- خطوط 17–19: سه پارامتر (کارخانه SGBD مورد استفاده، رشته اتصال، پرسوجوی SQL) که باید به سازنده کلاس [Dao.DataBaseImpot] ارائه شوند
- خطوط ۲۱–۲۳: نمونهسازی لایه [metier]. این همان پیکربندی نسخه ۶ است.
آزمایشها
پروژه [ui] به شرح زیر پیکربندی شده است:
![]() |
- [1]: پروژه از نوع «Windows Application» است
- [2]: ساخت این پروژه فایل اجرایی [ImpotsV7-ui.exe] را تولید میکند
یک مثال از اجرای برنامه در [3] ارائه شده است.
9.8.6. تغییر پایگاه داده
![]() |
لایهٔ بالایی [dao] با استفاده از یک کانکتور عمومی و یک پایهٔ MySQL5 نوشته شده است. در اینجا پیشنهاد میکنیم به یک پایهٔ SQL Server Compact منتقل شویم تا نشان دهیم تنها پیکربندی تغییر خواهد کرد.
پایگاه داده SQL Server Compact به شرح زیر خواهد بود:
![]() |
- [1]: پایگاه داده [dbimpots.sdf] در نمای [DataBase Explorer] از Visual Studio [2]. این پایگاه داده بدون رمز عبور ایجاد شده است.
- [3]: جدول [data] که حاوی دادهها است. ما عمداً نامهای متفاوتی را برای جدول و ستونها نسبت به آنچه در پایگاه داده MySQL5 استفاده میشود، انتخاب کردهایم تا بار دیگر مزایای درج این نوع جزئیات در فایل پیکربندی به جای کد را تأکید کنیم.
- [4]: ستون [id] کلید اصلی است و دارای ویژگی Identity است: این SGBD است که مقادیر آن را تعیین خواهد کرد.
- [5]: محتویات جدول [data].
![]() |
- [6]: پایگاه داده [dbimpots.sdf] در پوشه پروژه [ui] قرار گرفته و در این پروژه ادغام شده است.
- [7]: پایگاه داده [dbimpots.sdf] به پوشه زمان اجرای پروژه کپی خواهد شد.
فایل پیکربندی [App.config] برای پایگاه داده جدید به شرح زیر است:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<!--
<object name="dao" type="Dao.DataBaseImpot, ImpotsV7-dao">
<constructor-arg index="0" value="MySql.Data.MySqlClient"/>
<constructor-arg index="1" value="Server=localhost;Database=bdimpots;Uid=admimpots;Pwd=mdpimpots;"/>
<constructor-arg index="2" value="select limite, coeffr, coeffn from tranches"/>
</object>
-->
<object name="dao" type="Dao.DataBaseImpot, ImpotsV7-dao">
<constructor-arg index="0" value="System.Data.SqlServerCe.3.5"/>
<constructor-arg index="1" value="Data Source=|DataDirectory|\dbimpots.sdf;" />
<constructor-arg index="2" value="select data1, data2, data3 from data"/>
</object>
<object name="metier" type="Metier.ImpotMetier, ImpotsV7-metier">
<constructor-arg index="0" ref="dao"/>
</object>
</objects>
</spring>
</configuration>
- خطوط ۲۳–۲۷: پیکربندی لایه [dao] برای استفاده از پایگاه داده [dbimpots.sdf].
نتایج اجرای آن با موارد قبلی یکسان است. شایان ذکر است که استفاده از یک کانکتور عمومی باعث میشود لایه [dao] از تغییر در SGBD تأثیر نپذیرد. با این حال، دیدهایم که این کانکتور برای همه موقعیتها، بهویژه مواردی که از پرسوجوهای پارامتریک استفاده میشود، مناسب نیست. بنابراین راهحلهای دیگری نیز وجود دارد، مانند راهحلی که ذکر شد، یعنی فریمورکهای دسترسی به داده شخص ثالث (Spring, iBatis, NHibernate, LINQ, ...).
9.9. برای کسب اطلاعات بیشتر...
- LINQ در نشریات متعددی، بهویژه در کتاب *C# 3.0 in a Nutshell* اثر جوزف و بن البهاری، منتشرشده توسط O'Reilly، که پیشتر در مقدمه این سند به آن اشاره شده بود، معرفی شده است.
- iBatis در کتاب *iBatis in Action* اثر کلینتون بِگین، منتشر شده توسط منینگ، معرفی شده است.
- کتاب *Nhibernate in Action*، منتشر شده توسط منینگ، قرار است در ژوئیه ۲۰۰۸ منتشر شود.
Spring، iBatis و NHibernate دارای راهنماهای مرجع هستند که در وبسایتهای این فریمورکهای مختلف موجود است.
































































