فصل ۱۴ — مدلهای شیء Access
مدلهای شیء Access هستهٔ استفاده از VBA در Access هستند. همین مدلها برنامهنویسی Access را از برنامهنویسی در دیگر برنامههای VBA متمایز میکنند؛ زیرا فرمانهای اضافی برای دستکاری Tableها، Queryها، Formها و Reportها و نیز تقریباً تمام قابلیتهایی را فراهم میکنند که کاربر معمولاً از ساختار منوهای Access دریافت میکند. برای مقایسه، مدل شیء Microsoft Excel شامل Objectها و Commandهایی است که مستقیماً به Workbook و Worksheet مربوطاند و امکان دستکاری Range و Cell را میدهند. در Access، چند مدل شیء وجود دارد و کل برنامه حول ساختار Table، Query، Form و Report سازمان یافته است.
مدل شیء نیز متناسب با همین ساختار طراحی شده است. Access را میتوان عملاً یک برنامهٔ سهلایه دانست: لایهٔ خدمات کاربر (Client Services Tier)، مدل شیء، و لایهٔ خدمات داده (Data Services Layer). رابط معمول Access که کاربر میبیند همان Client Services Tier است و عموماً با کاربر ارتباط برقرار میکند.
زیر این لایه، مدلهای شیء مختلف قرار گرفتهاند. مهمترین آنها Access، DAO یا Data Access Objects و ADO یا ActiveX Data Objects هستند. هر بار که در Database عملی انجام میدهید، در واقع از طریق یکی از این مدلها فرمانی صادر میشود. برای مثال، هنگام ایجاد Query جدید، کد زیرین از Collection به نام QueryDefs و Object به نام QueryDef در مدل DAO برای نگهداری جزئیات Query استفاده میکند.
به همین ترتیب میتوانید از Object به نام CurrentDb برای بهدستآوردن نام فایل و مسیر Database استفاده کنید:
MsgBox CurrentDb.Name
CurrentDb بخشی از مدل شیء Access است.
با استفاده از مدلهای شیء و یک زبان برنامهنویسی مانند Visual Basic، ساخت یک Front End اختصاصی برای Access با تقریباً همان قابلیتهای Front End خود Microsoft Access دشوار نیست. هر فرمان منو و کلید تابعی در رابط Access در مدلهای شیء نمایندهای دارد. منظور این نیست که دقیقاً همین Objectها در داخل خود Access استفاده شدهاند، بلکه Microsoft مجموعهای از Objectها و Methodها را در اختیار برنامهنویس قرار داده تا هر کاری که از منوهای Access ممکن است با کد نیز انجام شود و، همانطور که مثالهای عملی بعدی نشان میدهند، کارهای بسیار بیشتری نیز امکانپذیر باشد.
جالب اینجاست که اگر بخواهید Front End خودتان را بنویسید، حجم کد لزوماً زیاد نیست؛ زیرا بخش عمدهٔ Functionality از قبل در Object Model وجود دارد. زیر Object Modelها لایهٔ Data Services قرار دارد که دادهٔ Tableها را نگه میدارد و با Commandهای مدلهای شیء تغییر میکند.
مدلهای شیء تعداد زیادی Object دارند؛ مانند CurrentDb، TableDef، QueryDef، Forms و Reports. این Objectها موجودیتهای مستقل برنامهنویسی هستند که بخشهای مختلف قابلیتهای تحلیل و مدیریت داده را فراهم میکنند و مهمتر از همه، از طریق کد قابل کنترلاند.
هنگام برنامهنویسی Access با VBA، از فرمانها و Functionهای استاندارد VBA مانند For..Next، If..Then..Else و MsgBox استفاده میکنید؛ اما برای ارتباط با برنامهٔ Access، Propertyها و Methodهای Objectهای مختلفی مانند TableDef یا QueryDef را از طریق Object Model دستکاری میکنید.
Object ساختاری برنامهنویسی است که داده و Functionality را در یک واحد تعریف و کپسوله میکند و دسترسی عمومی به آن از طریق Interfaceهای همان ساختار انجام میشود.
Object بخشی از برنامهٔ Access است و Objectها بهصورت سلسلهمراتبی سازمان یافتهاند. در بالاترین سطح Object Modelِ Access، Object به نام Application قرار دارد که خود Access را نمایندگی میکند. زیر آن، برای مثال CurrentDb قرار میگیرد؛ داخل CurrentDb، Objectهای TableDef هستند و داخل هر TableDef نیز Objectهای Field قرار دارند و این سلسلهمراتب ادامه مییابد.
هر Object میتواند تنظیماتی به نام Property و عملهایی که روی آن قابل اجرا هستند به نام Method داشته باشد. برای مثال، اگر بخواهید SQL یک Query خاص را ببینید یا ویرایش کنید، میتوانید Property به نام SQL را از Query مربوط در Collectionِ QueryDefs بخوانید:
MsgBox CurrentDb.QueryDefs("MyQuery").SQL
همچنین اگر Property فقطخواندنی نباشد میتوانید در آن مقدار بنویسید و تعریف Query را عوض کنید:
CurrentDb.QueryDefs("MyQuery").SQL = "select * from MyTable"
توضیح Propertyها و Methodها
همهٔ Objectهای مدلهای شیء دارای Property، Method یا هر دو هستند. بعضی Objectها تعداد کمی و بعضی، بسته به پیچیدگیشان، تعداد زیادی Property و Method دارند. Property ویژگی اسکالر یک Object است که پارامترهای مختلف آن را تعریف میکند. برای مثال، Propertyِ Visible در یک Form باز میتواند True (-1) یا False (0) باشد و مشخص میکند Form برای کاربر نمایش داده شود یا مخفی بماند. در اینجا از Constantهای داخلی VBA برای True و False استفاده میشود و مقدار عددی آنها در پرانتز آمده است.
Objectِ Forms دارای Property به نام Count است که تعداد Formهای باز در برنامهٔ Access را تعیین میکند. Propertyها پارامترهای تعریفکنندهٔ Object را نگه میدارند. نوع مقدارشان نیز به Object وابسته است و میتواند متنی یا عددی باشد.
Methodها راهی برای اجرای Action بر اساس یک Object مشخصاند و مانند میانبری برای انجام عملی روی آن Object عمل میکنند. برای نمونه، اگر بخواهید در زمان اجرا Fieldای را از Table حذف کنید، میتوانید Methodِ Delete را روی Collectionِ Fieldهای همان TableDef به کار ببرید. به همین شکل، Methodِ TransferSpreadsheet در Objectِ DoCmd داده را بین Database و Spreadsheet جابهجا میکند. بخش دشوار کار از قبل پیادهسازی شده و شما فقط Method را در کد فراخوانی میکنید.
برای درک مفهوم، رایانهٔ خود را یک Object در نظر بگیرید. میتوان Propertyها و Methodهایی برای آن تعریف کرد. Property جنبهای قابل توصیف یا اندازهگیری از Object است. نمونه:
| Property | مقدار |
| Make | Compaq |
| Year | 2009 |
| RAM | 8GB |
| Hard Disk | 320GB |
| Processor | Intel |
همهٔ این Propertyها عددی نیستند. نباید تصور کرد Property همیشه چیزی مستقیماً قابل اندازهگیری مانند Height است و بنابراین باید Numeric باشد. Property میتواند Text، Boolean یعنی True/False، Graphic مانند Picture یا Icon، یا مقداری Enumerated از یک فهرست مشخص باشد. در واقع Property میتواند هر Data Type مناسبی داشته باشد.
Methodها کلماتی هستند که Actionهای قابل انجام توسط Object را نمایندگی میکنند. برای رایانه میتوان Methodهایی مانند اینها در نظر گرفت:
- Cold boot
- Warm boot
- Shut down
- Set up
اگر این مفهوم را به Access منتقل کنیم و CurrentProject را مثال بزنیم، نمونههایی از Propertyهای آن عبارتاند از:
| Property | توضیح |
Path | مسیر فایل Access |
FullName | نام کامل به همراه مسیر |
AccessConnection | Connection String مربوط به این Database؛ میتوان از آن برای اتصال از Database دیگری در Access استفاده کرد؛ رجوع کنید به فصل ۱۹. |
Name | نام فایل Database در Access |
Propertyهای دیگری نیز وجود دارند، اما این موارد تصویری از ماهیت Propertyها میدهند.
نمونههایی از Methodهای Objectِ CurrentDb:
| Method | توضیح |
CreateQueryDef | یک Query جدید بر اساس Tableهای Database ایجاد میکند. |
CreateTableDef | یک Table جدید در Database ایجاد میکند. |
Execute | یک عبارت SQL را روی Database اجرا میکند؛ Method بسیار کاربردی. |
NewPassword | Passwordِ Database را تغییر میدهد یا اگر Password قبلی وجود نداشته باشد Password جدید تعیین میکند. |
Property میتواند Read-only یا Read/Write باشد. در حالت Read-only میتوانید مقدار Property را بخوانید اما نمیتوانید آن را تغییر دهید. در حالت Read/Write هم خواندن و هم تغییر مقدار ممکن است. این موضوع در برنامه اثر مهمی دارد، زیرا ممکن است کدی بنویسید که سعی کند در Property فقطخواندنی مقدار بنویسد.
گاهی برای حفظ یکپارچگی Object Model لازم است Property فقطخواندنی باقی بماند. برای مثال، Count در هر Collection همیشه Read-only است. اگر امکان تغییر Count در Collectionِ TableDefs وجود داشت، نتیجه میتوانست بسیار غیرقابلپیشبینی باشد؛ فرض کنید ده Table در Database وجود دارد و Count را به پنج تغییر دهید، در آن صورت پنج Table باید ناپدید شوند.
Methodها عملاً شبیه Subroutine یا میانبری برای Actionها هستند که میتوان آنها را از کد فراخوانی کرد؛ مانند تعیین Password برای Database یا بازکردن Recordset بر پایهٔ یک Table. نوشتن مستقل کدی برای تعیین Password تقریباً غیرعملی است، زیرا باید جزئیات سیستم Encryption و محل نگهداری Password در ساختار فایل Access را بدانید. بدون VBA نیازمند شناخت جزئیات C و Source Code مربوط بودید. Microsoft این کار پیچیده را انجام داده است، بنابراین شما تنها یک خط کد برای فراخوانی NewPassword نیاز دارید. Methodها معمولاً Parameter نیز میگیرند؛ برای مثال NewPassword باید Password قبلی و Password جدید را دریافت کند.
برخی Parameterها هنگام فراخوانی Method اختیاریاند و معمولاً در راهنمای Syntax با کروشه [ ] نمایش داده میشوند.
دستکاری Propertyها
اگر Property از نوع Read/Write باشد میتوان آن را تغییر داد؛ یعنی با قرار دادن مقدارهای دیگر، متناسب با Object و Property رفتار متفاوتی ایجاد کرد. برای نمونه میتوانید SQL یک Query را با نوشتن SQL جدید در Propertyِ SQL مربوط به QueryDef آن Query تغییر دهید.
Propertyها معمولاً در Runtime، یعنی هنگام اجرای برنامه، به کمک کد تغییر میکنند؛ اما بعضی Propertyها در Design Time نیز در دسترساند و میتوان آنها را از پنجرهٔ Properties در VBE تغییر داد. Design Time زمانی است که Code Window را میبینید و در حال طراحی یا اصلاح کد هستید.
برای یک مثال، Report جدیدی باز یا ایجاد کنید و فرض کنید با نام MyReport ذخیره شده است. در Design View با ALT+ENTER Property Sheet را باز کنید و به Propertyِ Caption بروید. این Property متنی را تعیین میکند که در Control Bar بالای Report ظاهر میشود. همین Propertyها را میتوان در پاسخ به Actionهای کاربر با کد نیز تغییر داد.
نمونهٔ Syntax برای خواندن Property:
MsgBox Reports("MyReport").Caption
توجه کنید Report باید باز باشد تا عضو Collectionِ Reports محسوب شود.
تمام Collectionها Index دارند که Objectهای منفرد داخل Collection را مشخص میکند. عبارت "MyReport" داخل پرانتز تعیین میکند که منظور Report با همین نام از Collectionِ Reports است. ممکن است چند Report همزمان باز باشند و VBA از این راه میان آنها تمایز ایجاد میکند.
برخی Objectها در Object یا Collection دیگری گروهبندی میشوند. برای مثال Access میتواند چند Form یا Report را همزمان باز داشته باشد. هر Form یا Report یک Object است، و تمام Reportهای باز در Collectionِ Reports قرار میگیرند. برای دسترسی به عضو منفرد Collection میتوانید موقعیت عددی یا نام آن، در صورت داشتن نام، را مشخص کنید.
بین Object و Property از نقطه استفاده میشود؛ مشابه استفاده از Backslash برای مشخصکردن مسیر فایل. ممکن است بیش از یک نقطه وجود داشته باشد چون Object میتواند Sub-object داشته باشد و Property نیز زیرساختار داشته باشد. برای نمونه، یک TableDef دارای Collectionِ Fields است. برای ارجاع به یک Field:
MsgBox CurrentDb.TableDefs("MyTable").Fields(0).Name
این کد نام نخستین Field در Tableِ MyTable را نشان میدهد. Indexِ Fields از صفر آغاز میشود. میتوان Propertyهای دیگری مانند Type و Size را نیز خواند.
اگر Property فقطخواندنی نباشد میتوان آن را تغییر داد:
CurrentDb.TableDefs("MyTable").Fields(1).Name = "NewTest"
این کد نام دومین Field را به NewTest تغییر میدهد. چنین تغییرهایی نیازمند دقتاند، زیرا تغییر نام Field در VBA تقریباً حتماً روی Queryها، Formها و Reportهایی که از آن Table استفاده میکنند اثر میگذارد. Access صرفاً به دلیل تغییر نام Field، همهٔ Objectهای وابسته را خودکار اصلاح نمیکند؛ بنابراین برای حفظ سازگاری Database ممکن است به کد گستردهای جهت اعمال تغییر در تمام Objectهای مرتبط نیاز داشته باشید.
فراخوانی Methodها
همانطور که گفته شد Methodها عملاً Subroutineهایی مبتنی بر Object هستند که Action خاصی انجام میدهند و گاهی رفتار آنها به Parameterهای ورودی وابسته است. Method میانبری برای Action است، اما برای اینکه VBA دقیقاً بداند چه کاری باید انجام دهد ممکن است لازم باشد Parameterها را مشخص کنید.
نمونهٔ مناسب، Methodِ TransferSpreadsheet در Objectِ DoCmd است که در فصل ۱۶ با جزئیات بیشتری بررسی میشود. باید Parameterهایی مانند TransferType، TableName و FileName را بدهید تا VBA بداند چه چیزی را منتقل کند:
DoCmd.TransferSpreadsheet acExport, , "MyTable", "C:\temp\MyTable"
C:\temp\MyTable محل فایل Export را تعیین میکند و برای این Method اجباری است. acExport میگوید داده Export شود و MyTable Source داده است. Parameterهای اختیاری دیگری مانند Spreadsheet Type و HasFieldNames نیز قابل ارائهاند. Parameterهای اختیاری در Tip Text با کروشه، مانند [HasFieldNames]، نشان داده میشوند. هنگام تایپ Statement در VBA، کادر زردرنگ Tip Text فهرست Parameterهای موجود را نشان میدهد.
برخی Methodها اصلاً Argument لازم ندارند. برای مثال اگر از DoCmd برای بستن Form یا Report استفاده کنید و کد از داخل همان Form یا Report اجرا شود، Object Type و Object Name از قبل معلوماند:
DoCmd.Close
این دستور همان Form یا Report جاری را میبندد، درست مانند کلیک روی علامت «x» گوشهٔ Window.
در مثال Export به Spreadsheet، Argument دوم که Spreadsheet Type بود خالی گذاشته شد؛ زیرا مقدار پیشفرض به نسخهٔ فعلی Excel نصبشده روی سیستم اشاره میکند:
DoCmd.TransferSpreadsheet acExport, , "MyTable", "C:\temp\MyTable"
این روش Passing by Order نام دارد، زیرا Parameterها با Comma و دقیقاً به ترتیبی که Function یا Method تعریف کرده است ارسال میشوند. هنگام ورود Argumentها، فهرست Parameterها ظاهر میشود و مورد جاری Bold میشود. باید همان ترتیب را رعایت کنید. اگر Function نتیجهای را به Variable اختصاص میدهد، در بعضی حالتها نباید پرانتز اضافه کنید و استفادهٔ نادرست از آن میتواند Error بدهد.
Methodهایی مانند TransferSpreadsheet Parameterهای زیادی دارند و بسیاری اختیاریاند. Passing by Order با Parameterهای اختیاری پیچیده میشود؛ زیرا ممکن است Function ده Parameter داشته باشد اما فقط دو مورد برای شما لازم باشند. نمونهٔ Import از Spreadsheet، با فرض وجود فایل MyTable در Root درایو C:
DoCmd.TransferSpreadsheet acImport, , "MyTable1", "C:\MyTable", ,"A1:G10"
در این مثال چند Parameter ارسال شده و جای Parameterهای استفادهنشده با فاصلهٔ خالی میان Commaها حفظ شده است. Spreadsheet Type داده نشده چون Default همان نسخهٔ فعلی Excel است؛ HasFieldNames نیز داده نشده و Default آن False است. اجباری یا اختیاریبودن Parameter به طراحی Method در Object Library بستگی دارد و موارد اختیاری با [ ] نشان داده میشوند.
کدی با Commaهای خالی میتواند برای خواننده گیجکننده باشد. در Application حرفهای بهتر است کد برای شخص دیگری که احتمالاً در آینده Maintenance انجام میدهد واضح باشد. اگر نویسندهٔ اصلی در دسترس نباشد، توسعهدهندهٔ دیگر باید بتواند سریع بفهمد هر Argument چه معنایی دارد.
راه دیگر Passing by Name است. در این روش نام Parameterها صریحاً در کد میآید و لازم نیست برای Argumentهای اختیاری استفادهنشده جای خالی ایجاد کنید:
DoCmd.TransferSpreadsheet TransferType:=acImport,TableName:="MyTable", _
FileName:="C:\MyTable",Range:="A1:G10"
هر Parameter با نامش و سپس := مشخص میشود. برخلاف Passing by Order، در Passing by Name میتوانید Parameterها را با ترتیب دلخواه بنویسید.
Collectionها
در برنامههای Object-oriented درک Collectionها مهم است. Collection یک Object است که گروهی از Objectهای همنوع را در خود نگه میدارد. برای مثال Collectionِ TableDefs شامل تمام Objectهای Table برای یک Database است. اعضای TableDefs از یک نوعاند و Propertyها و Methodهای مشابه دارند. Objectی مانند Query Propertyها و Methodهای متفاوتی دارد، بنابراین عضو TableDefs نیست و در QueryDefs قرار میگیرد.
در Access، Objectها یا منفردند و با Name ارجاع میشوند، یا عضو Collection هستند و با Index یا Name شناسایی میشوند. خود Collectionها علاوه بر Objectهای داخلشان Property و Method دارند. Collectionها همیشه Propertyِ Count برای تعداد اعضا دارند و بعضی Collectionها Methodی مانند Add برای افزودن Object جدید ارائه میکنند.
Objectهای عضو Collection نیز Property و Method خود را دارند و حتی میتوانند Collectionهای دیگری را در خود جای دهند. Collectionِ TableDefs مجموعهای از TableDefهاست که همهٔ Tableهای Access را نمایندگی میکند. این Collection دارای Count است و با Append میتوان TableDef جدید اضافه کرد. هر TableDef Propertyهایی مانند DateCreated و Methodهایی مانند CreateField یا CreateIndex دارد. درون هر TableDef نیز Collection دیگری به نام Fields قرار دارد.
هر Field در Collectionِ Fields دارای Index و Name است و Index از صفر آغاز میشود. همین الگو برای TableDefs نیز برقرار است؛ هر TableDef در Collection دارای Index و Name است. Collectionهای دیگری مانند QueryDefs، Forms و Reports نیز وجود دارند، اما TableDef/TableDefs نمونهٔ مناسبی برای توضیح مفهوم Collection است.
میتوان در Collectionها پیمایش کرد. Loop از نوع For Each..Next برای این کار مناسب است: «برای هر Object داخل Collection». در هر دور میتوانید Propertyهای Object را بخوانید یا تغییر دهید، یا Method آن را فراخوانی کنید. برای نمونه:
CurrentDb.TableDefs("MyTable").CreateField("NewField")
کد زیر را در یک Module وارد کنید و با F5 اجرا کنید؛ نام Tableها یکبهیک در Message Box نمایش داده میشود:
Sub ShowName()
Dim w As TableDef
For Each w In CurrentDb.TableDefs
MsgBox w.Name
Next w
End Sub
شکل ۱۴-۱ — Message Box نمایشدهندهٔ نام Table Definitionها
Variable به نام w بهصورت TableDef تعریف میشود و در هر دور Loop به Table Definition جاری اشاره میکند. Loop در Collectionِ TableDefs حرکت میکند، Propertyِ Name را میخواند و نمایش میدهد.
Dim مخفف Dimension است و برای Variable فضایی در Memory ایجاد میکند. میتوان Routine را بدون Dim نیز اجرا کرد، اما تعریف Type مزیت مهمی دارد: VBE هنگام تایپ Propertyها و Methodهای مناسب را بهصورت خودکار پیشنهاد میدهد. پس از Dim w As فهرست Typeها ظاهر میشود و هنگام تایپ w. نیز فهرست Property و Methodهای TableDef نمایش داده میشود. این قابلیت در برنامهنویسی بسیار کمککننده است.
شکل ۱۴-۲ — نمایش نام تمام Table Definitionهای Collection
ساختار Object Model بسیار پیچیده است و انتظار نمیرود همهٔ Objectها، Collectionها، Propertyها و Methodها را حفظ کنید. بدون List Boxهای خودکار VBE، مجبور میشوید پیوسته به Object Browser مراجعه کنید و احتمال اشتباه و Error نیز بیشتر میشود.
استفاده از Object Browser
Object Browser ابزار مفیدی برای مشاهدهٔ Propertyها، Methodها و Constantهای Objectهاست؛ در اینجا بهویژه Objectهای Access و DAO. برای بازکردن آن از منوی VBE گزینهٔ View | Object Browser را انتخاب کنید یا F2 را بزنید. از Pull-down با عنوان <All Libraries>، Object Library موردنظر را انتخاب کنید. سپس Classها، Propertyها، Methodها و همچنین رابطه و Hierarchy آنها نمایش داده میشود.
شکل ۱۴-۳ — مدل شیء Access در Object Browser
Object Browser بهطور پیشفرض دو مدل شیء اصلی را نمایش میدهد: Access و DAO. مدل Access خود Application را پوشش میدهد و Collectionهایی مانند Forms و Reports دارد؛ مدل DAO بر داده متمرکز است و Objectهایی مانند TableDefs و QueryDefs را در اختیار میگذارد.
برای جستوجو، String موردنظر را در کادر زیر <All Libraries> وارد کنید و روی آیکن Binoculars کلیک کنید یا Enter بزنید. برای نمونه با جستوجوی QueryDef تمام Referenceهایی که این عبارت را دارند نمایش داده میشوند. این روش بسیار سریعتر از بررسی دستی تکتک شاخههای Hierarchy است.
با کلیک روی یک Class، Propertyها، Methodها و Collectionهای زیرمجموعه در Members Pane سمت راست ظاهر میشوند. برای مثال پس از جستوجوی QueryDef، روی Classِ QueryDefs که Collection تمام Queryهاست کلیک کنید. Methodها با Icon سبز و Propertyها با Icon خاکستری شبیه دستی که چیزی را نگه داشته مشخص میشوند.
با انتخاب Property میتوانید ببینید Read-only است یا Write نیز میپذیرد و کدام Object مالک آن است. Pane پایین Object Browser Typeِ Property و وضعیت Read-only را نشان میدهد. با انتخاب Method نیز Syntaxِ Parameterها و اجباری/اختیاریبودن آنها نشان داده میشود؛ Parameterهای اختیاری در Syntax با [ ] مشخصاند.
ارتباط با Tableها
یکی از استفادههای اصلی VBA در Access ارتباط با Tableها و دستکاری دادهٔ داخل آنهاست. برای این کار از Objectِ Recordset استفاده میشود که بخشی از DAO است و در ADO نیز مفهومی متناظر دارد. مثال زیر از Tableِ Employees در Database نمونهٔ Northwind استفاده میکند.
Sub ViewData()
Dim RecSet As Recordset
Set RecSet = CurrentDb.OpenRecordset("Employees")
Do Until RecSet.EOF
MsgBox RecSet![Last Name]
MsgBox RecSet![First Name]
RecSet.MoveNext
Loop
RecSet.Close
Set RecSet = Nothing
End Sub
ابتدا Objectِ Recordset با نام RecSet با Dim ساخته میشود. سپس با Methodِ OpenRecordset از CurrentDb به Tableِ Employees متصل میشود. به جای Table میتوان نام Query یا SQL Select Statement نیز داد؛ البته SQL باید قبلاً درست آزمایش شده باشد تا Error غیرمنتظره ایجاد نکند.
Loop از نوع Do Until ... Loop تا رسیدن به EOF یا End Of File در Recordset حرکت میکند و Last Name و First Name هر Employee را نمایش میدهد. MoveNext بسیار مهم است؛ بدون آن Record جاری تغییر نمیکند و Loop هیچگاه تمام نمیشود.
برای خواندن Field از علامت تعجب ! بهجای نقطه استفاده شده، زیرا نام Fieldها مستقیماً جزء Propertyهای تعریفشدهٔ Recordset Object Model نیستند و باید از آنها متمایز شوند. چون Last Name و First Name فاصله دارند، نام Field داخل [ ] قرار گرفته است.
برای تغییر داده میتوان از Methodهای Edit و Update استفاده کرد:
Sub EditData()
Dim RecSet As Recordset
Set RecSet = CurrentDb.OpenRecordset("Employees")
Do Until RecSet.EOF
If RecSet![First Name] = "Nancy" Then
RecSet.Edit
RecSet![Company] = "Northwind"
RecSet.Update
End If
RecSet.MoveNext
Loop
RecSet.Close
Set RecSet = Nothing
End Sub
در این مثال اگر First Name برابر Nancy باشد، Record وارد Edit Mode میشود، Company به Northwind تغییر میکند و Update تغییر را در Database ثبت میکند. هر تغییر باید Rulesِ Table را رعایت کند؛ نمیتوان Text در Numeric Field گذاشت، نمیتوان در Required Field مقدار Null قرار داد و Table نیز نباید در Design Mode باز باشد.
برای افزودن Record جدید:
Sub AddData()
Dim RecSet As Recordset
Set RecSet = CurrentDb.OpenRecordset("Employees")
RecSet.AddNew
RecSet!Company = "MyCompany"
RecSet![Last Name] = "Shepherd"
RecSet![First Name] = "Richard"
RecSet.Update
RecSet.Close
Set RecSet = Nothing
End Sub
این بار نیاز به Loop نیست. AddNew Record جدیدی میسازد، سه Field مقداردهی میشوند و Update Record را اضافه میکند. لازم نیست ID را مقدار دهید، زیرا ID از نوع AutoNumber است و Access آن را خودکار تولید میکند.
در بسیاری از کدها باید مسیر طولانی «درخت Objectها» نوشته شود: Application ← CurrentDb ← TableDef از Collectionِ TableDefs ← Field. Application ریشه، CurrentDb و TableDef شاخهها و Fieldها برگها هستند. برای مثال:
MsgBox CurrentDb.TableDefs("Employees").Fields.Count
این Reference کار میکند، اما در کدهای طولانی تکرار دائمی چنین Hierarchyای خستهکننده است. میتوان با Dim یک Object مانند TableDef را در Memory ایجاد کرد و Reference کوتاهتری داشت.
استفاده از Transaction
هنگام پردازش داده با VBA ممکن است Error غیرمنتظرهای مانند ناسازگاری Data Type رخ دهد، یا حتی قطع برق برنامه را متوقف کند. در این وضعیت ممکن است بخشی از Recordها پردازش شده باشند و بخشی نه؛ بنابراین نمیدانید فرایند دقیقاً کجا قطع شده و یکپارچگی داده به خطر میافتد.
برای Processهای بزرگ استفاده از Transaction مناسب است. کد میتواند Recordset را تغییر دهد، اما تغییرها تا اجرای Commit در Table نهایی نمیشوند. اگر Procedure در میانه شکست بخورد، بدون Commit داده باید به وضعیت قبلی بازگردد.
Sub EditData()
Dim RecSet As Recordset, WrkSp as Workspace
Set RecSet = CurrentDb.OpenRecordset("Employees")
Set WrkSp = DBEngine.Workspaces(0)
WrkSp.BeginTrans
Do Until RecSet.EOF
If RecSet![First Name] = "Nancy" Then
RecSet.Edit
RecSet![Company] = "Northwind"
RecSet.Update
End If
RecSet.MoveNext
Loop
If MsgBox("Save all changes?", vbQuestion + vbYesNo) = vbYes Then
WrkSp.CommitTrans
Else
WrkSp.Rollback
End If
RecSet.Close
WrkSp.close
Set RecSet = Nothing
Set WrkSp = Nothing
End Sub
در این مثال Object جدیدی از نوع Workspace با نام WrkSp به Workspace جاری اشاره میکند. پیش از ویرایش Recordset، BeginTrans آغاز Transaction را مشخص میکند. پس از پیمایش Recordset، Message Box از کاربر میپرسد تغییرها ذخیره شوند یا نه. با Yes، CommitTrans تمام تغییرها را ذخیره میکند؛ با No، Rollback آنها را لغو میکند.
ایجاد Object در Memory
این روش برای Objectهای مختلف قابل استفاده است؛ مثال کتاب با TableDefs و Fields کار میکند. هنگام ایجاد TableDef در Memory، Variableای با Dim تعریف میکنید تا نمایندهٔ آن Table باشد. نام Variable میتواند دلخواه باشد، به شرط آنکه قبلاً در کد استفاده نشده و Reserved Word نباشد.
مزیت ساخت TableDef Object این است که با Set آن را به Table مشخصی متصل میکنید و سپس همان Variable را برای Reference به Table به کار میبرید؛ در عین حال List Boxهای خودکار VBE برای Propertyها، Methodها و Collectionهای زیرمجموعه همچنان کار میکنند. بدون Set باید Hierarchy کامل را در هر خط بنویسید و مزیت Auto-completion را نیز از دست میدهید.
Dim Db As Database
Dim MyTable As TableDef, MyField As Field
Set Db = CurrentDb
Set MyTable = Db.TableDefs("Employees")
Set MyField = MyTable.Fields("Company")
MsgBox MyField.Size
سه Variable ساخته میشود: Db از نوع Database، MyTable از نوع TableDef و MyField از نوع Field. ابتدا Db به CurrentDb اشاره میکند. کتاب توضیح میدهد که در این Context و با ساختار DAO، برای Object Assignment از این Object میانی استفاده میشود تا به Collectionِ TableDefs دسترسی داده شود. سپس MyTable به Employees و MyField به Fieldِ Company اشاره میکنند.
اکنون MyField نمایندهٔ Fieldِ Company است و میتوان Propertyِ Size را نمایش داد. هنگام نوشتن MyField. فهرست Propertyها و Methodهای معتبر همان Object ظاهر میشود و میتوانید مورد موردنظر را انتخاب کنید.
Hierarchy
مدلهای شیء دارای Hierarchy هستند و فهم آن برای Reference درست Objectها اهمیت دارد. کتاب آن را به ساختار فرماندهی در نیروهای مسلح تشبیه میکند: Generalها در بالا، سپس Colonel، Major، Captain و Lieutenant. دستور از سطوح بالاتر به پایینتر جریان پیدا میکند.
در این تشبیه، Application مانند General چهارستاره در رأس است؛ CurrentDb میتواند Colonel، TableDefs مانند Major و Fieldها در سطح Captain در نظر گرفته شوند. ترتیب Command باید با Hierarchy سازگار باشد. Object پاییندست Property و Methodهای Object بالاتر را مالک نیست. بنابراین عبارت زیر نادرست است:
Fields("Company").TableDefs("Employees").CreateField
Field داخل TableDef قرار دارد، نه برعکس؛ در نتیجه این Reference قواعد Hierarchy را میشکند و Error میدهد.
یک راه دسترسی رو به بالا Propertyِ Parent است. کتاب این حالت را با Majorی تشبیه میکند که General پدر اوست و میتواند از طریق آن رابطه پیامش را به سطح بالاتر برساند. در VBA، Parent امکان دسترسی به Object والد را میدهد.
Database نمونهٔ Northwind را باز کنید. Form با نام Order Subform for Order Details را در Design View باز کنید و در Eventِ Form Current این کد را قرار دهید:
MsgBox Me.Parent.Name
Form را ذخیره و ببندید. سپس Formِ Order Details را باز کنید. Message Box نام Parent Form یعنی Order Details را نمایش میدهد. با بازشدن Parent، Subform نیز باز میشود و Eventِ Form Current اجرا میشود؛ کد با Me.Parent.Name Propertyِ Name از Parent را میخواند.
بالاترین Object در Hierarchyِ Access، Application است و خود Access را نمایندگی میکند. مدل Access با Formها، Reportها، Current Database، Objectِ DoCmd و Functionهای مختلف سروکار دارد. DAO، همانطور که نام Data Access Object نشان میدهد، برای دسترسی و دستکاری داده، ساخت Table و Query و Recordset استفاده میشود. برای Databaseها و Tableهای خارجی میتوان از ADO یا ActiveX Data Objects استفاده کرد که در فصل ۱۹ بیشتر توضیح داده میشود.
ساختار Object Model در فصل ۱۵ با جزئیات بیشتری بررسی میشود. برای تسلط بر آن، Object Browser را با F2 باز کنید و در Moduleهای آزمایشی Objectهای مختلف را بررسی و تجربه کنید.
صفحهٔ پایانی این بازه در نسخهٔ اصلی عمداً خالی است.