entity.HasOne(d => d.Reply) .WithMany(p => p.Children) .HasForeignKey(d => d.ReplyId);
وقتی بخوایم از 2 جدول متفاوت جستجو کنیم به نظر شما اطلاعات هر جدول را جداگانه ایندکس کنیم(منظورم همان کاری که متد CreateFullTextIndex شما انجام میده) یا خیر؟
البته منظور دو جدولی که با هم رابطه دارند.
چندتا لینک در مورد لوسین:
http://www.codeproject.com/Articles/272309/Lucene-Search-Programming
http://www.codeproject.com/Articles/320219/Lucene-Net-ultra-fast-search-for-MVC-or-WebForms
- در مورد سوئیچ startup-project در مطلب « شروع به کار با EF Core 1.0 - قسمت 3 - انتقال مهاجرتها به یک اسمبلی دیگر» بحث شدهاست.
- این پروژه از فایل _01-add_migrations.cmd برای افزودن مهاجرتها استفاده میکند و از فایل _02-update_db.cmd برای به روز رسانی ساختار بانک اطلاعاتی.
روش TPH
در این روش، ارث بری از طریق فقط یک جدول ایجاد میشود و زیر مجموعهها بر اساس مقدار یک فیلد از یکدیگر متمایز میشوند. پس اگر جدولی دارید که برای متمایز کردن رکوردهای آن از یک فیلد استفاده میکنید، روش TPH مناسب شما است. با روش TPH نیز میتوانید به همان مدلی که در روش TPT دارید برسید، تنها تفاوت این هست که در روش TPH، تمامی دادهها در یک جدول قرار دارند و یک فیلد برای متمایز کردن رکوردها استفاده میشود. همه چیز با مثال عملی واضحتر است. پس کار خود را با یک مثال ادامه میدهیم. جدول مثال ما در شکل زیر مشخص است.
به نظر میرسد که این جدول با جداول قسمت قبل شباهتی دارد. بله! فیلدهای جداول مثال قبل در این جدول آمده اند.
- فیلدهای FirstName و LastName از جدول Persons
- فیلد HireDate از جدول Instructors
- فیلد EnrollmentDate، Credits و Degree از جدول Students
- فیلد AdminDate از جدول Admins
- فیلدهای BusinessCredits و Discipline از جدول BusinessStudents
یک فیلد با نام PersonCategory نیز اضافه شده است که «مقداری عددی» میپذیرد و برای «متمایز کردن رکوردها» استفاده میشود:
- 1 ، نمایانگر Student
- 2 ، نمایانگر Instructor
- 3 ، نمایانگر Admin
- 4 ، نمایانگر BusinessStudent
از این جدول میخواهیم به مدل قسمت اول برسیم اما این بار با استفاده از روش TPH. در شکل زیر، جدول Persons به صورت مدل شده در برنامه نشان داده شده است.
حال باید خصیصههای موجودیت Person را به موجودیتهای مشتق شده منتقل کرد. بدین منظور، هر خصیصه از موجودیت Person را انتخاب، کلیدهای Ctrl+X را فشار دهید، سپس بر روی قسمت Properties موجودیت مشتق شدهی مورد نظر رفته و کلیدهای Ctrl+V را فشار دهید. نتیجه در شکل زیر نشان داده شده است.
اکنون زمان آن رسیده است تا جدول متناظر با هر یک از موجودیتهای مشتق شده را معرفی کنیم. تمامی موجودیتهای مشتق شده از جدول Persons استفاده میکنند. بر روی هر یک از آنها کلیک راست کرده و گزینهی Table Mapping را انتخاب کنید. پنجره ی Mapping Details نشان داده میشود. ابتدا بر روی عبارت Add a Table or View و سپس بر روی نشانگر رو به پایینی که کنار آن ظاهر میشود کلیک کنید (شکل زیر).
آیتم Persons را انتخاب کنید. اکنون باید فیلد تفکیک کنندهی رکوردها را مشخص کنیم. برای این حالت باید یک شرط ایجاد نمود. در همان پنجرهی Mapping Details، عبارتی با عنوان Add a Condition وجود دارد. بر روی آن کلیک و در لیستی که ظاهر میشود، آیتم PersonCategory را انتخاب کنید (شکل زیر).
سپس در ستون Value/Property، مقدار آن را "1" قرار دهید (شکل زیر).
تناظر میان موجودیت و جدول Persons و مقداردهی مناسب به فیلد متمایز کننده را برای تمامی موجودیتهای مشتق شده انجام دهید. دلیل این کار این است که EF بداند هر رکورد در چه زمانی باید به چه موجودیتی تبدیل شود. دقت کنید که پیشتر به مقدار فیلد متمایز کننده برای هر موجودیت اشاره کردیم. نکتهی مهم اینکه یک شرط نیز باید برای موجودیت Person ایجاد و مقدار فیلد متمایز کنندهی آن را "صفر" تعریف کنید.
مثال ما آماده است. آن را امتحان میکنیم.
using (PersonDbEntities context = new PersonDbEntities()) { var people = from p in context.Persons select p; foreach (Person person in people) { Console.WriteLine("{0}, {1}", person.LastName, person.FirstName); if (person is Student) Console.WriteLine(" Degree: {0}", ((Student)person).Degree); if (person is BusinessStudent) Console.WriteLine(" Discipline: {0}", ((BusinessStudent)person).Discipline); } Console.ReadLine(); }
مزایای روش TPH
- سرعت بالای عملیات CRUD، به دلیل وجود تمامی دادهها در یک جدول
- تعداد جداول در پایگاه داده، کم و مدیریت آنها آسانتر است
معایب روش TPH
- افزونگی داده ها. مقادیر برخی ستونها برای بعضی از رکوردها، حاوی مقدار NULL است و تعداد این ستونها به تعداد زیر مجموعهها ارتباط دارد
- عیب اول، باعث میشود تا در صورتی که دادهها به صورت دستی تغییر پیدا کنند، جامعیت دادهها از بین برود
- افزایش بی دلیل حجم داده ها
- اضافه و حذف کردن موجودیتها به مدل، عملی زمانبر و پیچیده است
بررسی ساختار WebAPI مقدماتی مثال این سری
این پروژهی مقدماتی که هنوز قسمتهای اعتبارسنجی به آن اضافه نشدهاند، از دو قسمت WebApi و MvcClient تشکیل میشود.
کار قسمت WebApi، ارائهی یک Restful-API برای کار با گالری تصاویر است. برای اجرای آن وارد پوشهی src\WebApi\ImageGallery.WebApi.WebApp شده و ابتدا فایل restore.bat و سپس dotnet_run.bat را اجرا کنید.
در این حالت برنامه بر روی پورت 7001 در دسترس خواهد بود:
این پورت نیز در فایل Properties\launchSettings.json تنظیم شدهاست تا با پورت کلاینت MVC تهیه شده، تداخل نکند.
کار این سرویس، ارائهی ImagesController است که توسط آن میتوان لیستی از تصاویر موجود، اطلاعات یک تصویر و همچنین کار ثبت، ویرایش و حذف تصاویر را انجام داد.
در این Solution، رکوردهای تصاویری که در بانک اطلاعاتی ذخیره میشوند، یک چنین ساختاری را دارند:
using System; using System.ComponentModel.DataAnnotations; namespace ImageGallery.WebApi.DomainClasses { public class Image { [Key] public Guid Id { get; set; } [Required] [MaxLength(150)] public string Title { get; set; } [Required] [MaxLength(200)] public string FileName { get; set; } [Required] [MaxLength(50)] public string OwnerId { get; set; } } }
در این قسمت توسط کلاس ImageConfiguration کار مقدار دهی اولیهی بانک اطلاعاتی به کمک متد HasData مربوط به EF Core 2.1 صورت گرفتهاست و به این ترتیب میتوان برنامه را برای نمایش مقدماتی جاری، بدون داشتن سیستم اعتبارسنجی و مفاهیم کاربران، نمایش داد.
این قسمت از Solution، به نحو زیر تشکیل شدهاست:
- ImageGallery.WebApi.DataLayer
در اینجا کار تشکیل DbContext برنامه و همچنین مقدار دهی اولیهی بانک اطلاعاتی و تنظیمات Migrations قرار گرفتهاند.
- ImageGallery.WebApi.DomainClasses
در این پروژه کلاسهای موجودیتهای متناظر با جداول بانک اطلاعاتی قرار میگیرند.
- ImageGallery.WebApi.Mappings
این پروژه کار تهیه نگاشتهای AutoMapper برنامه را انجام میدهد؛ نگاشتهایی بین Models و DomainClasses که در ImagesController از آنها استفاده میشود.
- ImageGallery.WebApi.Models
در این پروژه همان DTO's پروژه قرار گرفتهاند. جهت رعایت مسایل امنیتی نباید کلاس موجودیت Image فوق را مستقیما در معرض دید API عمومی قرار داد. به همین جهت تعدادی Model در اینجا تعریف شدهاند که هم برای ثبت، ویرایش و حذف اطلاعات بکار میروند و هم جهت گزارشگیری از رکوردهای موجود جدول تصاویر.
- ImageGallery.WebApi.Services
در این پروژه کار با DbContext انجام شده و توسط آن اطلاعات تصاویر به بانک اطلاعاتی اضافه شده و یا واکشی میشوند.
- ImageGallery.WebApi.WebApp
این پروژه، همان پروژهی اصلی است که سایر قسمتهای یاد شده را در کنار هم قرار داده و به صورت یک Restful-API ارائه میدهد.
بررسی ساختار MvcClient مقدماتی مثال این سری
پس از اجرای WebAPI و آماده بودن آن جهت استفاده، اکنون یک کلاینت ASP.NET Core MVC را جهت کار با امکانات ImagesController آن، تدارک دیدهایم.
برای اجرای آن وارد پوشهی src\MvcClient\ImageGallery.MvcClient.WebApp شده و ابتدا فایل restore.bat و سپس dotnet_run.bat را اجرا کنید.
در این حالت برنامه بر روی پورت 5001 در دسترس خواهد بود:
این پورت نیز در فایل Properties\launchSettings.json تنظیم شدهاست.
در اینجا نمایی از اجرای این برنامه را مشاهده میکنید که لیستی از تصاویر را توسط GalleryController، از سرویس ImagesController مربوط به WebAPI، دریافت کرده و سپس نمایش میدهد. در این لیست تصاویر تمام کاربران با هم نمایش داده شدهاند و هنوز امکان فیلتر کردن آنها بر اساس کاربران وارد شدهی به سیستم را نداریم که در قسمتهای بعدی آنها را تکمیل خواهیم کرد.
این قسمت از Solution به نحو زیر تشکیل شدهاست:
- ImageGallery.MvcClient.Services
در اینجا یک Typed HTTP Client مخصوص NET Core 2.1. را تهیه کردهایم. این سرویس جهت دسترسی به آدرس https://localhost:7001 که WebAPI برنامه در آن قرار دارد، تشکیل شدهاست. روش ثبت مخصوص آنرا نیز در فایل آغازین پروژهی MvcClient.WebApp توسط متد services.AddHttpClient ملاحظه میکنید.
- ImageGallery.MvcClient.ViewModels
مدلهای متناظر با ساختار Viewهای Razor برنامهی وب، در اینجا قرار میگیرند.
- ImageGallery.MvcClient.WebApp
این پروژه، همان پروژهی اصلی است که سایر قسمتهای یاد شده را در کنار هم قرار داده و به صورت یک برنامهی MVC قابل مرور در مرورگر، ارائه میدهد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
برای اجرای آن ابتدا باید پروژهی WebApi.WebApp را اجرا کنید و سپس پروژهی MvcClient.WebApp.
با افزایش حجم بانکهای اطلاعاتی دسترسی سریع به دادههای مطلوب به یک معضل تبدیل میشود. بهمین دلیل نیاز به مکانیزم هایی برای بازیابی سریع دادهها احساس میشود. یکی از این مکانیزمها اندیس گذاری (indexing) است. اندیس گذاری مکانیزمی است که به ما امکان دسترسی مستقیم (direct access) را به دادههای بانک اطلاعاتی میدهد.
عمل اندیس گذاری وظیفه طراح بانک اطلاعاتی است که با توجه به دسترسی هایی که در آینده به بانک اطلاعاتی وجود دارد مشخص میکند که بر روی چه ستون هایی میخواهد اندیس داشته باشد. بعنوان مثال با تعیین کلید اصلی اعلام میکند که بیشتر دسترسیهای آینده من بر اساس این کلید اصلی است و بنابراین بانک اطلاعاتی بر روی کلید اصلی اندیس گذاری را انجام میدهد. علاوه بر کلید اصلی میتوان بر روی هر ستون دیگری از جدول نیز اندیس گذاشت که همانطور که گفته شد این مسئله بستگی به تعداد دسترسی آینده ما از طریق آن ستونها دارد.
پس از اندیس گذاری بر روی یک ستون بسته به نوع اندیس فایلی در پایگاه اطلاعاتی ما ایجاد میشود که به آن فایل اندیس (index file) گفته میشود. این فایل یک فایل مبتنی بر رکورد (record-based) است که هر رکورد آن محتوی زوج کلید جستجو – اشاره گر می باشد. کلید جستجو را مقدار ستون مورد نظر و اشاره گر را اشاره گری به رکورد مربوط به ان میتواند در نظر گرفت.
توجه داشته باشید که اندیس گذاری و مدیریت اندیس ها، همانطور که در این مقاله آموزشی گفته خواهد شد سر بار هایی ( از نظر حافظه و پردازش) را بر سیستم تحمیل مینمایند. بعنوان مثال با اندیس گذاری بر روی هر ستونی یک فایل اندیس نیز ایجاد میشود بنابراین اگر اندیسهای ما بسیار زیاد باشد حجم زیادی از بانک اطلاعاتی ما را خواهند گرفت. مدیریت و بروز نگهداری فایلهای اندیس نیز خود مسئله ایست که سربار پردازشی را بدنبال دارد. بنابراین توصیه میشود در هنگام اندیس گذاری حتما بررسیها و تحلیلهای لازم را انجام دهید و تنها بر روی ستون هایی اندیس بگذرید که در آینده بیشتر دسترسیهای شما از طریق ان ستونها خواهد بود.
عموما در بانکهای اطلاعاتی دو نوع اندیس میتواند بکار گیری شود که عبارتند از :
- اندیسهای مرتب (ordered indices) : در این نوع کلیدهای جستجو (search-key) بصورت مرتب نگداری میشوند.
- اندیسهای هش (Hash indices) : در این نوع از اندیسها کلیدهای جستجو در فایل اندیس مرتب نیستند. بلکه توسط یک تابع هش (hash function) توزیع میشوند.
در این مقاله قصد داریم به اندیسهای مرتب بپردازیم و بخشی از مفاهیم مطرح در این باره را پوشش دهیم.
اندیسهای متراکم ( dense index ):
اولین و سادهترین نوع از اندیسهای مرتب اندیسهای متراکم ( dense ) هستند. در این نوع از اندیسها وقتی بر روی ستونی میخواهیم عمل اندیس گذاری را انجام دهیم میبایست به ازای هر کلید – جست و جو (search-key) غیر تکراری در ستون مورد نظر، یک رکورد در فایل اندیس مربوط به ان ستون اضافه کنیم. برای روشن شدن بیشتر موضوع به شکل زیر توجه کنید.
شکل 1 – اندیس متراکم (sparse index)
همانطور که در تصوری مشاهده میکنید بر روی ستون دوم از این جدول (جدول سمت راست)، اندیس متراکم (dense) گذاشته شده است. بر همین اساس به ازای هر کدام از اسامی خیابانها یک رکورد در فایل اندیس (جدول سمت چپ) آورده شده است. در فایل اندیس میبینید که در کنار کلید جستجو یک اشاره گر نیز به جدول اصلی وجود دارد که در هنگام دسترسی مستقیم (direct access) از این اشاره گر استفاده خواهد شد. دقت کنید که کلیدهای جستجو در فایل اندیس بصورت مرتب نگهداری شده اند که نکته ای کلیدی در اندیسهای مرتب میباشد.
مرتب بودن فایل اندیس موجب میشود که ما در هنگام جستجوی کلید مورد نظرمان در جدول اندیس بتوانیم از روشهای جستجویی نظری جست و جوی دو دویی استفاده کنیم و در نتیجه سریعتر کلید مورد نظر را پیدا کنیم. این مسئله باعث ببهبود کارایی میشود. بعنوان مثال فرض کنید در فایل اندیس یک ملیون رکورد داریم. در این صورت برای یافتن کلید مورد نظرمان در جدول اندیس بروش جست و جوی دو دویی تنها کافی است 20 عمل مقایسه انجام دهیم. بنابراین میبینید که مرتب نگهداشتن جدول اندیس چقدر در سرعت بازیابی، تاثیر دارد.
نکته مهمی که در اندیسهای متراکم باید به آن دقت شود اینست که ما به ازای کلیدهای جستجوی غیر تکراری یک رکورد در جدول اندیس نگهداری میکنیم. برای مثال در شکل بالا در ستون مورد نظر ما دو رکورد برای Downtown و سه رکورد برای Perryridge وجود دارد. این در حالی است که در فایل اندیس فقط یک Downtown و Perryridge داریم.
در اندیسهای متراکم ما امکان دو نوع دسترسی را داریم :
- دسترسی مستقیم (direct access)
- دسترسی ترتیبی (sequential access)
دسترسی مستقیم :
توجه داشته باشید که در هنگام کار با یک جدول، فایلهای اندیس آن به حافظه اصلی آورده میشوند (البته ممکن است که بخشی از فایلهای اندیس به حافظه اصلی نیایند). این در حالی است که فایل اصلی جدول در حافظه جانبی قرار دارد. بنابراین در هنگام بازیابی یک رکورد از برای یافتن محل ان رکورد نیازی به مراجعه زیاد به حافظه جانبی نیست. بلکه در حافظه اصلی بسرعت با یک عمل جستجو اشاره گر مربوط به رکورد مورد نظر در حافظه جانبی پیدا شده و مستقیما به آدرس همان رکورد میرویم و آن را میخوانیم. به این دسترسی، دسترسی مستقیم (direct access) می گوییم.
دسترسی ترتیبی :
در برخی از روشهای اندیس گذاری علاوه بر دسترسی مستقیم امکان دسترسی بصورت ترتیبی نیز وجود دارد. در دسترسی ترتیبی این امکان وجود دارد که از یک رکورد خاص در جدول اصلی بتوانیم رکوردهای بعد از آن را به ترتیبی منطقی پیمایش کنیم. برای روشنتر شدن موضوع به شکل شماره 1 توجه کنید. در انتهای هر رکورد اشاره گری به رکورد منطقی بعدی مشاهده میکنید. این اشاره گرها امکان پیمایش و دسترسی ترتیبی را به ما میدهند. بعنوان مثال فرض کنید قصد داریم تمامی رکوردهای حاوی کلید Perryridge را بازیابی نماییم. از آنجایی که در جدول اندیس تنها برای یکی از رکوردهای حاوی این کلید اندیس داریم، برای بازیابی باقی رکوردها چه باید کرد؟ در چنین شرایطی ابتدا با دسترسی مستقیم اولین رکورد حاوی Perryridge را پیدا کرده و آن را بازیابی میکنیم. سپس از طریق اشاره گر انتهای آن رکورد، میتوان به رکورد بعدی آن دست یافت و به همین ترتیب میتوان یک به یک به رکوردهای دیگر دسترسی ترتیبی پیدا نمود.
دقت کنید که رکوردهای جدول ما بصورت فیزیکی مرتب نیستند. اما اشاره گرهای انتهای رکوردها طوری مقدار دهی شده اند که بتوان آنها را بصورت مرتب شده پیمایش نمود.
اندیس اولیه (primary index) و اندیس ثانویه (secondary index) :
بر روی ستونهای یک جدول میتوان چندین اندیس را تعریف نمود. اولین اندیسی که بر روی یک ستون از یک جدول گذاشته میشود اندیس اولیه (primary index) نامیده میشود. عموما این اندیس به کلید اصلی نسبت داده میشود، چراکه اولین اندیسی است که بر روی جدول زده میشود. توجه داشته باشید که رکوردهای جدول اصلی بر اساس کلیدهای جستجوی اندیس اولیه بصورت منطقی (با استفاده اشاره گرهای انتهای رکورد که توضیح داده شد) مرتب هستند. بنابراین امکان دسترسی بصورت ترتیبی وجود دارد. وقتی پس از اندیس اولیه اقدام به اندیس گذاریهای دیگری میکنیم، اندیسهای ثانویه را ایجاد میکنیم که اندکی با اندیسهای اولیه متفاوت میباشند. در اندیسهای ثانویه دیگر امکان پیمایش و دسترسی ترتیبی وجود ندارد چراکه اشاره گرهای انتهای رکوردها بر اساس اندیس اصلی (اولیه) مرتب شده اند. بنابراین ما در اندیسهای ثانویه تنها دسترسی مستقیم خواهیم داشت. شکر زیر نمونه ای از یک اندیس ثانویه را نشان میدهد.
شکل 2 – اندیس ثانویه
همانطور که مشاهده میکنید علاوه بر اندیس اصلی (بر روی ستون 2) بر روی سومین ستون این جدول اندیس ثانویه متراکم زده شده است. دقت کنید که هر اشاره گر از جدول اندیس به یک باکت (bucket) اشاره دارد. در هر باکت اشاره گر هایی وجود دارد که به رکورد هایی از جدول اصلی اشاره میکنند. فلسفه وجود باکتها اینست که در اندیسهای ثانویه امکان دسترسی ترتیبی وجود ندارد. بنابراین برای مقادیری تکراری در جدول (مثلا عدد 700) نمیتوان از اشاره گرهای انتهای رکوردها استفاده نمود. در چنین شرایطی در باکتها اشاره گر مربوط به تمامی رکوردهای حاوی مقادیر تکراری یک کلید را نگهداری میکنیم تا بتوان به انها دسترسی مستقیم داشت. همانطور که مشاهده میکنید برای بازیابی رکوردهای حاوی مقدار 700 ابتدا از جدول اندیس (که مرتب است) باکت مربوطه را پیدا کرده و سپس از طریق اشاره گرهای موجود در این باکت به رکوردهای حاوی مقدار 700 دستیابی پیدا میکنیم.
اندیسهای تنک (sparse index) :
در این نوع از اندیسها بر خلاف اندیسهای متراکم، تنها به ازای برخی از کلیدهای جستجو در جدول اندیس اشاره گر نگهداری میکنیم. بهمین دلیل فایل اندیس ما کوچکتر خواهد بود (نسبت به اندیس متراکم). در مورد اندیسهای تنک نیز امکان دسترسی ترتیبی وجود دارد. در شکل زیر نمونه از اندیس تنک (sparse) را مشاهده میکنید.
شکل 3 – اندیس تنک (sparse index)
همانند شکل 1، در این شکل نیز اندیس اولیه بر روی ستون دوم زده شده است. اما این بار از اندیس تنک استفاده گردیده است. مشاهده میکنید که از میان مقادیر مختلف این ستون تنها برای سه کلید Brighton، Perryridge و Redwood در جدول اندیس رکورد درج شده است. بنابراین برای دست یابی به کلیدهای دیگر باید ابتدا محل تقریبی آن را با جستجو بر روی جدول اندیس پیدا نمود و سپس از طریق پیمایش ترتیبی به رکورد مورد نظر دست یافت. بعنوان مثال برای بازیابی رکورد حاوی مقدار Mianus ابتدا در جدول اندیس کلیدی که از Mianus کوچکتر باشد (یعنی Brighton ) را پیدا میکنیم. سپس به رکورد حاولی Brighton می رویم و از آنجا با استفاده از اشاره گرهای انتهایی رکوردها به سمت رکورد حاوی Mianus حرکت میکنیم تا به آن برسیم.
نکته بسیار مهمی که در مورد اندیسهای تنک مطرح میشود اینست که سیستم چگونه باید تشخیص دهد که کدام کلیدها را در جدول اندیس نگهداری کند. این تصمیم به مفهوم بلاکهای حافظه و اندازه انها باز میگردد. میدانیم که واحد خواندن اطلاعات از حافظه بر اساس بلاکها میباشد. این بدان معنی است که در هنگام خواندن رکوردهای جداول بانک اطلاعاتی، عمل خواندن بصورت بلاکی انجام میشود. هنگامی که بر روی یک جدول میخواهیم اندیس تنک بزنیم ابتدا باید ببینیم این جدول چند بلاک از حافظه را اشغال کرده است. سپس رکوردهای اول هر بلاک را پیدا کرده و به ازای هر بلاک آدرس و کلید جستجوی رکورد اول آن را در جدول اندیس نگهداری کنیم. بدین ترتیب ما به ازای هر بلاک از جدول یک رکورد در فایل اندیس خواهیم داشت و با تخصیص بلاکهای جدید به ان، طبیعی است که اندیسهای جدید نیز در فایل اندیس ذخیره خواهند شد.
اندیسهای چند سطحی (multi-level index)
در دنیایی واقعی معمولا تعداد رکوردهای جداول مورد استفاده بسیار بزرگ است و این اندازه دائما در حال زیاد شدن میباشد. افزایش اندازه جداول باعث میشود که اندازه فایلهای اندیس نیز رفته رفته زیاد شود. گفتیم برای کارایی هرچه بیشتر باید جدول اندیس مورد استفاده به حافظه اصلی آورده شود تا تعداد دسترسیهای ما به حافظه جانبی تا حد امکان کاهش یابد. اما اگر اندازه فایل اندیس ما بسیار بزرگ باشد ممکن است حجم زیادی از حافظه اصلی را بگیرد یا اینکه در حافظه اصلی فضای کافی برای ان وجود نداشته باشد. در چنین شرایطی از اندیسهای چند سطحی استفاده میشود. به بیان دیگر بر روی جدول اندیس نیز اندیس زده میشود. تعداد سطوح اندیس ما بستگی به اندازه جدول اصلی دارد و هر چه این اندازه بزرگتر شود، ممکن است باعث افزایش تعداد سطوح اندیس شود. در شکل زیر ساختار یک اندیس دو سطحی را مشاهده میکنید.
نکته مهم در مورد اندیسهای چند سطحی اینست که اندیسهای سطوح خارجی (outer index) از نوع تنک هستند. این مسئله به این دلیل است که اندازه اندیسها کوچکتر شود. چراکه اگر اندیس خارجی از نوع متراکم باشد به این معناست که به ازای هر رکورد غیر تکراری باید یک رکورد در فایل اندیس نیز آورده شود و این مسئله باعث بزرگ شدن اندیس میشود. بهمین دلیل سطوح خارجی را در اندیسهای چند سطحی از نوع تنک میگیرند. تنها آخرین سطحی که مستقیما به جدول اصلی اشاره میکند از نوع متراکم است. به این سطح از اندیس، اندیس داخلی (inner index) گفته میشود.
بروز نگهداشتن اندیسها :
با انجام عملیات درج و حذف بروی جداول، جداول اندیس مربوطه نیز باید بروز رسانی شوند. در این بخش قصد داریم به نحوه بروز رسانی جداول اندیس در زمان حذف و درج رکورد بپردازیم.
بروز رسانی در زمان حذف :
اندیس متراکم :
هنگامی که رکوردی از جدول اصلی حذف میشود، در صورتی که بر روی ستونهای آن اندیسهای متراکم داشته باشیم، پس از حذف رکورد اصلی باید ابتدا کلید جستجوی ستون مربوط را در جدول اندیس پیدا کنیم. در صورتی که از این کلید تنها یک مقدار در جدول اصلی وجود داشته باشد، اندیس آن را از فایل اندیس حذف کرده و اشاره گرهای انتهای رکوردها را بروز رسانی میکنیم. اما اگر از کلید مورد نظر چندین مورد وجود داشته باشد نباید رکورد مورد نظر در جدول اندیس پاک شود. بلکه تنها ممکن است نیاز به ویرایش اشاره گر اندیس باشد. ویرایش در زمانی رخ میدهد که اشاره گر جدول اندیس مستقیما به رکوردی اشاره کند که حذف شده باشد، در این صورت باید اشاره گر اندیس را ویراش نمود تا به رکورد بعدی اشاره نماید.
اندیس تنک :
همانند روش قبل ابتدا رکورد اصلی را از جدول حذف میکنیم. سپس در فایل اندیس بدنبال کلید جستجوی مربوط به رکورد حذف شده میگردیم. در صورتی که کلید مورد نظر در جدول اندیس پیدا شد کلید جستجوی رکورد بعدی در جدول اصلی را جایگزین آن میکنیم. چنانچه کلید مربوط به رکورد بعدی در جدول اندیس وجود داشته باشد نیازی به جایگزینی نیست و باید فقط عمل حذف اندیس را انجام داد.
اگر کلید مورد جستجو در جدول اندیس وجود نداشته باشد نیاز به انجام هیچ عملی نیست. در پایان باید اشاره گرهای انتهای رکوردها را ویرایش نمود تا ترتیب منطقی برای پیمایش ترتیبی حفظ شود.
بروز رسانی در زمان درج:
اندیس متراکم:
در هنگام درج یک رکورد جدید، ابتدا باید کلید موجود در رکورد جدید را در جدول اندیس جستجو نمود. در صورتی که کلید مورد نظر در جدول اندیس یافت نشد، باید رکوردی جدیدی در فایل اندیس درج کرد و اشاره گر آن طوری مقدار دهی نمود تا به رکورد جدید اشاره نماید. اگر کلید مورد نظر در جدول اندیس وجود داشته باشد دیگر نیازی بروز رسانی اندیسها نیست و تنها کافی است اشاره گرهای انتهای رکوردها بروز رسانی شوند.
اندیس تنک :
در مورد اندیسهای تنک کمی پیچیدگی وجود دارد. در صورتی که رکورد جدید باعث تخصیص بلاک (block) جدیدی از حافظه به جدول شود، باید به ازای آن بلاک یک اندیس در جدول اندیسها ایجاد شود و آدر آن بلاک را (که در واقع آدرس رکورد جدید نیز میشود) در اشاره گرد اندیس قرار داد. اما درغیز این صورت ( در صورتی که رکورد در بلاکهای موجود ذخیره شود) نیازی به بروز رسانی جدول اندیسها وجود ندارد.
نوع دیگری از اندیسهای مرتب نیز وجود دارد که اندیس های B-Tree هستند که در سیستمهای اطلاعاتی دنیای واقعی بیشتر از آنها استفاده میشود. به امید خدا در مطالب بعدی این اندیسها را نیز مورد بررسی قرار خواهیم داد.
موفق و پیروز باشید.
تهیه یک بانک اطلاعاتی نمونه
برای نمایش امکانات کار با روش Database first، نیاز است یک بانک اطلاعاتی را به صورت مستقل و متداولی ایجاد کنیم. به همین جهت اسکریپت SQL ذیل را توسط Management studio اجرا کنید تا بانک اطلاعاتی BloggingCore2016، به همراه دو جدول به هم وابسته، در آن ایجاد شوند:
CREATE DATABASE [BloggingCore2016] GO USE [BloggingCore2016] GO CREATE TABLE [Blog] ( [BlogId] int NOT NULL IDENTITY, [Url] nvarchar(max) NOT NULL, CONSTRAINT [PK_Blog] PRIMARY KEY ([BlogId]) ); GO CREATE TABLE [Post] ( [PostId] int NOT NULL IDENTITY, [BlogId] int NOT NULL, [Content] nvarchar(max), [Title] nvarchar(max), CONSTRAINT [PK_Post] PRIMARY KEY ([PostId]), CONSTRAINT [FK_Post_Blog_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blog] ([BlogId]) ON DELETE CASCADE ); GO INSERT INTO [Blog] (Url) VALUES ('https://www.dntips.ir/'), ('http://blogs.msdn.com/dotnet'), ('http://blogs.msdn.com/webdev'), ('http://blogs.msdn.com/visualstudio') GO
پیشنیازهای مهندسی معکوس ساختار بانک اطلاعاتی در EF Core
در قسمت اول در حین بررسی «برپایی تنظیمات اولیهی EF Core 1.0 در یک برنامهی ASP.NET Core 1.0»، چهار مدخل جدید را به فایل project.json برنامه اضافه کردیم. مدخل جدید Microsoft.EntityFrameworkCore.Tools که به قسمت tools آن اضافه شد، پیشنیاز اصلی کار با EF Core Migrations است. همچنین وجود مدخل Microsoft.EntityFrameworkCore.SqlServer.Design برای تدارک امکانات مهندسی معکوس ساختار یک بانک اطلاعاتی SQL Server ضروری است.
تبدیل ساختار دیتابیس BloggingCore2016 به کدهای معادل EF Core آن
پس از فعال سازی ابزارهای خط فرمان EF Core، به پوشهی اصلی پروژه مراجعه کرده، کلید shift را نگه دارید. سپس کلیک راست کرده و گزینهی Open command window here را انتخاب کنید تا خط فرمان از این پوشه آغاز شود. در ادامه دستور ذیل را صادر کنید:
dotnet ef dbcontext scaffold "Data Source=(local);Initial Catalog=BloggingCore2016;Integrated Security = true" Microsoft.EntityFrameworkCore.SqlServer -o Entities --context MyDBDataContext --verbose
اجرا این دستور سبب اتصال به رشتهی اتصالی ذکر شده که به بانک اطلاعاتی BloggingCore2016 اشاره میکند، میشود. سپس پروایدر مدنظر ذکر شدهاست. سوئیچ o محل درج فایلهای نهایی را مشخص میکند. برای مثال در اینجا فایلهای نهایی مهندسی معکوس شده در پوشهی Entities درج میشوند (تصویر فوق). همچنین در اینجا امکان ذکر فایل context تولیدی نیز وجود دارد. اگر علاقمند باشید تا تمام ریز جزئیات این عملیات را نیز مشاهده کنید، میتوانید پارامتر اختیاری verbose را نیز به انتهای دستور اضافه نمائید.
بقیه مراحل کار با این فایلهای تولید شده، با نکاتی که تاکنون عنوان شدهاند یکی است. برای مثال اگر میخواهید رشتهی اتصالی پیش فرض را از این Context تولید شده خارج کنید:
public partial class MyDBDataContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Data Source=(local);Initial Catalog=BloggingCore2016;Integrated Security = true"); }
بررسی پارامترهای دیگر ابزار مهندسی معکوس به Code First
اگر دستور dotnet ef dbcontext scaffold --help را صادر کنیم، خروجی راهنمای ذیل را میتوان مشاهده کرد:
Usage: dotnet ef dbcontext scaffold [arguments] [options] Arguments: [connection] The connection string of the database [provider] The provider to use. For example, Microsoft.EntityFrameworkCore.SqlServer Options: -a|--data-annotations Use DataAnnotation attributes to configure the model where possible. If omitted, the output code will use only the fluent API. -c|--context <name> Name of the generated DbContext class. -f|--force Force scaffolding to overwrite existing files. Otherwise, the code will only proceed if no output files would be overwritten. -o|--output-dir <path> Directory of the project where the classes should be output. If omitted, the top-level project directory is used. --schema <schema> Selects a schema for which to generate classes. -t|--table <schema.table> Selects a table for which to generate classes. -e|--environment <environment> The environment to use. If omitted, "Development" is used. -h|--help Show help information -v|--verbose Enable verbose output
- حالت پیش فرض تنظیمات روابط مدلها در این روش، حالت استفاده از Fluent API است. اگر میخواهید آنرا به حالت استفادهی از Data Annotations تغییر دهید، پارامتر a- و یا data-annotations-- را در دستور نهایی ذکر کنید.
- حالت پیش فرض تولید فایلهای نهایی این روش، عدم بازنویسی فایلهای موجود است. اگر میخواهید پس از تغییر بانک اطلاعاتی، مجددا این فایلها را از صفر تولید کنید، پارامتر f- و یا force- را در دستور نهایی ذکر کنید.
بنابراین اگر میخواهید هربار فایلهای نهایی را بازنویسی کنید و همچنین روش کار با Data Annotations را ترجیح میدهید، دستور نهایی، شکل زیر را پیدا خواهد کرد:
dotnet ef dbcontext scaffold "Data Source=(local);Initial Catalog=BloggingCore2016;Integrated Security = true" Microsoft.EntityFrameworkCore.SqlServer -o Entities --context MyDBDataContext --verbose --force --data-annotations
کار با یک بانک اطلاعاتی موجود، با روش مهاجرتهای Code First
فرض کنید میخواهید از یک بانک اطلاعاتی از پیش موجود EF 6.x (یا هر بانک اطلاعاتی از پیش موجود دیگری)، به روش پیش فرض EF Core استفاده کنید. برای این منظور:
- ابتدا جدول migration history قدیمی آنرا حذف کنید؛ چون ساختار آن با EF Core یکی نیست.
- سپس با استفاده از دستور dotnet ef dbcontext scaffold فوق، معادل کلاسها، روابط و Context سازگار با EF Core آنرا تولید کنید.
- در ادامه رشتهی اتصالی پیش فرض آنرا از کلاس Context تولیدی خارج کرده و از یکی از روشهای مطرح شدهی در مطلب «شروع به کار با EF Core 1.0 - قسمت 1 - برپایی تنظیمات اولیه» استفاده کنید.
- سپس نیاز است این Context جدید را توسط متد services.AddDbContext به لیست سرویسهای برنامه اضافه کنید. این مورد نیز در قسمت اول بررسی شدهاست.
- مرحلهی بعد، افزودن جدول __EFMigrationsHistory جدید EF Core، به این بانک اطلاعاتی است. برای این منظور به روش متداول فعال کردن مهاجرتها، دستور ذیل را صادر کنید:
dotnet ef migrations add InitialDatabase
using Microsoft.EntityFrameworkCore.Migrations; namespace Core1RtmEmptyTest.DataLayer.Migrations { public partial class InitialDatabase : Migration { protected override void Up(MigrationBuilder migrationBuilder) { } protected override void Down(MigrationBuilder migrationBuilder) { } } }
سپس دستور به روز رسانی بانک اطلاعاتی را صادر کنید:
dotnet ef database update
پس از این مرحله، روش کار، Code first خواهد بود. برای مثال خاصیتی را به کلاسی اضافه میکنید و سپس دو دستور ذیل را صادر خواهید کرد که در آن v2 یک نام دلخواه است:
dotnet ef migrations add v2 dotnet ef database update
ASP.NET MVC #17
function addToken(data) { data.__RequestVerificationToken = $("input[name=__RequestVerificationToken]").val(); return data; } $.ajax({ // ..... data: addToken({ postId: postId }), // اضافه کردن توکن dataType: "html", // نوع داده مهم است // ..... });
- data type حتما باید در این حالت html باشد.
- در قسمت data متد addToken کار افزودن خودکار آنتی فورجریتوکن صفحه را انجام میدهد (محل آن مهم نیست که داخل فرم باشد یا خارج از آن).
- سطر contentType نباید ذکر شود.
تا اینجا در قسمت سوم، نحوهی قراردادن یک کامپوننت را در کامپوننتی دیگر، توسط مقدار دهی خاصیت directives مزین کنندهی Component بررسی کردیم. همینقدر که یک کامپوننت دارای selector باشد، قابلیت قرارگرفتن در یک کامپوننت دیگر را دارد. اما چگونه باید بین این کامپوننتها ارتباط برقرار کرد؟
تهیه کامپوننت نمایش ستارهای امتیازهای محصولات
مثال نمایش لیست محصولات سری جاری، دارای ستون «5Star Rating» است. در این قسمت میخواهیم بجای نمایش عددی این امتیازها، کامپوننتی را طراحی کنیم که نماش ستارهای آنها را سبب شود. این کامپوننت باید بتواند یک مقدار ورودی، یا همان عدد امتیاز محصول را از کامپوننت دربرگیرندهی آن دریافت کند. همچنین میخواهیم اگر کاربر بر روی این ستارهها کلیک کرد، کامپوننت در برگیرنده را نیز مطلع سازیم.
در این مثال در فایل product-list.component.html چنین سطری تعریف شدهاست:
<td>{{ product.starRating }}</td>
با توجه به اینکه کامپوننت نمایش ستارهای امتیازها، قابلیت استفادهی مجدد را دارد و الزامی ندارد که حتما در لیست محصولات، بکار گرفته شود، بهتر است محل تعریف آنرا به خارج از پوشهی products فعلی منتقل کنیم. برای مثال میتوان پوشهی app\shared را برای آن و تمامی کامپوننتهای با قابلیت استفادهی مجدد ایجاد کرد.
برای شروع، فایل جدید App\shared\star.component.ts را اضافه کنید؛ با کدهای کامل ذیل:
import { Component, OnChanges, Input, Output, EventEmitter } from 'angular2/core'; @Component({ selector: 'ai-star', templateUrl: 'app/shared/star.component.html', styleUrls: ['app/shared/star.component.css'] }) export class StarComponent implements OnChanges { @Input() rating: number; starWidth: number; @Output() ratingClicked: EventEmitter<string> = new EventEmitter<string>(); ngOnChanges(): void { this.starWidth = this.rating * 86 / 5; } onClick() { this.ratingClicked.emit(`The rating ${this.rating} was clicked!`); } }
سپس مسیر template و مسیر فایل css ویژهی آن، در تزئین کنندهی Component مشخص شدهاند. محتوای کامل این دو فایل را در ذیل مشاهده میکنید:
الف) محتوای فایل App\shared\star.component.html
<div class="crop" [style.width.px]="starWidth" [title]="rating" (click)='onClick()'> <div style="width: 86px"> <span class="glyphicon glyphicon-star"></span> <span class="glyphicon glyphicon-star"></span> <span class="glyphicon glyphicon-star"></span> <span class="glyphicon glyphicon-star"></span> <span class="glyphicon glyphicon-star"></span> </div> </div>
.crop { overflow: hidden; } div { cursor: pointer; }
معرفی مقدماتی life cycle hooks در قسمت قبل صورت گرفت. در اینجا چون نیاز است به ازای هر بار رندر شدن این کامپوننت، عرض آن متفاوت باشد، بنابراین نیاز است راهی را پیدا کنیم تا بتوان مقدار خاصیت starWidth را متغیر کرد. به همین منظور از hook مخصوص این تغییرات یا همان OnChanges استفاده میشود. بنابراین باید کلاس این کامپوننت، اینترفیس OnChanges را پیاده سازی کند. پس از آن، importهای لازم جهت تعریف OnChanges به ابتدای فایل اضافه شده و همچنین متد ngOnChanges نیز جهت تکمیل کار پیاده سازی اینترفیس OnChanges، به کلاس جاری اضافه میشود.
کار متد ngOnChanges، تبدیل عدد امتیاز یک محصول، به عرض div نمایش ستارهها است.
مکانیزم کار رخداد ngOnChanges و دریافت اطلاعات از والد
متد ngOnChanges، تنها به خواص ویژهای به نام «input properties» واکنش نشان میدهد. اگر یک کامپوننت تو در توی قرار گرفتهی در یک کامپوننت دیگر، بخواهد اطلاعاتی را از والد خود دریافت کند، باید خاصیتی را در معرض دید آن دربرگیرنده قرار دهد. این کار توسط decorator ویژهای به نام ()Input@ انجام میشود.
به همین جهت است که پیش از خاصیت rating در کلاس StarComponent، شاهد درج مزین کنندهی ویژهی ()Input@ هستیم:
export class StarComponent implements OnChanges { @Input() rating: number;
پس از آن، کامپوننت دربرگیرنده یا والد، این خاصیت ورودی ویژه را از طریق روش property binding متداول، مقدار دهی میکند:
[rating]='product.starRating'
بدیهی است در اینجا چون خاصیت starWidth از نوع ورودی تعریف نشدهاست، قابلیت property binging فوق را در کامپوننت والد، ندارد.
اکنون به ازای هر بار نمایش این کامپوننت فرزند، خاصیت rating ورودی آن مقدار دهی شده و مقدار آن در رخداد ngOnChanges قابل دسترسی و استفاده خواهد بود. اینجا است که میتوان از این مقدار تغییر یافته، جهت ترجمهی آن به عرض div نمایش ستارهها، استفاده کرد.
ارسال دادهها از کامپوننت فرزند به کامپوننت والد
تا اینجا با استفاده از «خواص ورودی» امکان دسترسی به مقادیر ارسالی از طرف والد را در کامپوننت فرزند، پیدا کردیم. عکس آن نیز امکان پذیر است؛ اما توسط رخدادها.
کامپوننت فرزند، با استفاده از decorator ویژهی دیگری به نام ()Output@ امکان ارسال رخدادها را به کامپوننت والد پیدا میکند:
export class StarComponent implements OnChanges { @Input() rating: number; starWidth: number; @Output() ratingClicked: EventEmitter<string> = new EventEmitter<string>();
در مثال جاری اگر کاربر بر روی div ستارههای نمایش داده شده کلیک کند، اتصال به آن از طریق event binging متداول انجام میشود (متد جدید onClick به رخداد click متصل شدهاست):
<div class="crop" [style.width.px]="starWidth" [title]="rating" (click)='onClick()'>
onClick() { this.ratingClicked.emit(`The rating ${this.rating} was clicked!`); }
تا اینجا مرحلهی تنظیمات رخدادها در کامپوننت فرزند صورت گرفت. ابتدا خاصیتی از نوع Output تعریف شد. سپس در کدهای قالب این کامپوننت جدید، متد onClick به رخداد click متصل گردید و سپس در کدهای مدیریت کنندهی این متد، متد ratingClicked.emit جهت ارسال اطلاعات نهایی به والد، فراخوانی گردید.
اکنون در کامپوننت والد، باید این مراحل برای دریافت اطلاعات از کامپوننت فرزند خود، طی شوند:
الف) ابتدا نام خاصیت مزین شدهی با Output، به عنوان مقصد event binding مشخص میشود و سپس متدی در کلاس کامپوننت والد، به آن متصل میگردد:
(ratingClicked)='onRatingClicked($event)'
ب) در ادامه، تعریف این متد جدید متصل شده را به کلاس ProductListComponent اضافه میکنیم:
onRatingClicked(message: string): void { this.pageTitle = 'Product List: ' + message; }
به این ترتیب با کلیک بر روی div هر کامپوننت نمایش ستارهای امتیازها، خاصیت pageTitle درج شدهی در صفحه تغییر میکند.
استفاده از کامپوننت نمایش ستارهای امتیازها
نکات کلی افزودن این کامپوننت جدید، تفاوتی با مطالب عنوان شدهی در قسمت سوم، در حین بررسی مراحل افزودن دایرکتیو نمایش لیست محصولات، به کامپوننت ریشهی سایت ندارد و یکی هستند.
برای افزودن و استفاده از این کامپوننت جدید، ابتدا قالب product-list.component.html را گشوده و سپس سطر نمایش عددی امتیاز یک محصول را به نحو ذیل تغییر میدهیم:
<td> <ai-star [rating]='product.starRating' (ratingClicked)='onRatingClicked($event)'> </ai-star> </td>
سپس باید به کلاس کامپوننت لیست محصولات (کامپوننت در برگیرنده) اعلام کرد که این کامپوننت جدید را باید از کجا پیدا کند. برای این منظور فایل product-list.component.ts را گشوده و خاصیت directives این کامپوننت را مقدار دهی میکنیم:
import { Component, OnInit } from 'angular2/core'; import { IProduct } from './product'; import { ProductFilterPipe } from './product-filter.pipe'; import { StarComponent } from '../shared/star.component'; @Component({ selector: 'pm-products', templateUrl: 'app/products/product-list.component.html', styleUrls: ['app/products/product-list.component.css'], pipes: [ProductFilterPipe], directives: [StarComponent] })
نمونهای از اجرای برنامه را در تصویر ذیل مشاهده میکنید:
در اینجا ستون امتیازهای محصولات با کامپوننت نمایش ستارهای این امتیازها جایگزین شدهاست و همچنین با کلیک بر روی یکی از آنها، عنوان panel جاری تغییر کردهاست.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MVC5Angular2.part6.zip
خلاصهی بحث
در اینجا نحوهی طراحی API عمومی یک کامپوننت را بررسی کردیم. تا زمانیکه خواص کلاس یک کامپوننت به نحو متداولی تعریف میشوند، میدان دید آنها محدود است به قالب تعریف شدهی متناظر با آنها. اگر نیاز است خاصیتی خارج از این قالب و به صورت عمومی در کامپوننت دربرگیرندهی دیگری در دسترس قرار گیرد، آنرا با مزین کنندهی ()Input@ مشخص میکنیم و اگر قرار است این کامپوننت فرزند، اطلاعاتی را به کامپوننت والد ارسال کند، اینکار را توسط رخدادها و با تعریف ویژگی ()Output@ و EventEmitter انجام میدهد. نوع آرگومان جنریک EventEmitter، تعیین کنندهی نوع اطلاعاتی است که قرار است به کامپوننت دربرگیرنده ارسال شوند.
پس از تعریف کامپوننت فرزند، برای تعریف آن در کامپوننت والد، از نام selector آن به عنوان یک المان جدید HTML استفاده میشود و سپس با استفاده از property binding، اطلاعات لازم، به خاصیت از نوع ()Input@ کامپوننت فرزند ارسال میگردد. از event binding برای دریافت رخدادها از کامپوننت فرزند استفاده میشود. در اینجا هر رخدادی که توسط مزین کنندهی ()Output@ تعریف شده باشد، میتواند به عنوان مقصد event binding تعریف شود و اگر نیاز است به رخدادهای property binding از والد به فرزند، گوش فرا داد، میتوان اینترفیس OnChanges را در کلاس کامپوننت فرزند پیاده سازی کرد.
برای شروع کار ابتدا دو دیتابیس به اسمهای databasefrm و databaseto میسازیم. دیتابیس databasefrm شامل یک جدول به اسم emp با سه فیلد ID,Name,Address میباشد. قصد داریم جدول tmp از دیتابیس databasefrm را به دیتابیس databaseto انتقال دهیم. برای انجام این کار، یکی از روشهای زیر را استفاده خواهیم کرد:
روش 1 : استفاده از کوئری
ساختار کلی انجام این عمل به صورت زیر خواهد بود:
Select * into DestinationDB.dbo.tableName from SourceDB.dbo.SourceTable
select * into databaseto.dbo.emp from databasefrm.dbo.Emp
حال اگر بخواهیم یک کپی از جدول را در دیتابیس جاری ایجاد کنیم، ساختار آن به صورت زیر خواهد بود :
select * into newtable from SourceTable
select * into emp1 from emp
میتوانیم فقط فیلدهایی مشخص را به جدول دیگر کپی کنیم. برای انجا این کار کافیست به جای * اسم فیلدهای مورد نیاز را نوشت که ساختار دستوری آن به صورت زیر است :
select col1, col2 into <destination_table> from <source_table>
select Id,Name into databaseto.dbo.emp1 from databasefrm.dbo.Emp
بعد از اجرای کوئری فوق نتیجه به صورت زیر خواهد بود :
کد فوق باعث کپی کردن فیلدهای Id,Name شده است.
اگر بخواهیم فقط ساختار جدول را کپی کنیم روند کار به صورت زیر خواهد بود :
select *into <destination_database.dbo.destination table> from _ <source_database.dbo.source table> where 1 = 2
select * into databaseto.dbo.emp from databasefrm.dbo.emp where 1 = 2
نکته: هر وقت نیاز بود که فقط فیلدهای یک جدول را دریافت کنید، میتواند از کدی همانند فوق استفاده کنید؛ با یک شرط که همیشه false برگرداند. ولی راه بهتری که توصیه میکنم استفاده از Top در دستور Select میباشد. نمونهای از دستور فوق:
select top(0) * into databaseto.dbo.emp from databasefrm.dbo.emp
روش 2: ویزارد
جهت تهیه کارهای فوق به صورت ویزارد، به صورت خلاصه فقط به روند انجام کار بسنده میکنیم:
1- SSMS را باز کنید.
2- بر روی دیتابیس مورد نظر کلیک راست کرده و از منوی ظاهر شده Task را انتخاب نموده و در کادر بازشو Export data را انتخاب کنید.
3- در پنجرهی ظاهر شده بر روی دکمه next کلیک کرده و در پنجره بعدی، نوع اعتبار سنجی را انتخاب کرده و دیتابیس مورد نظر را انتخاب نمایید (databasefrm).
4- همانند مرحله 3 است با این تفاوت که اینبار دیتابیس مقصد را انتخاب میکنیم (databaseto).
5- در پنجرهی بعدی گزینه اول را انتخاب کرده (copy data from ...) و بعد از کلیک بر روی next در پنجره ظاهر شده، جدول یا جداول مورد نظر را انتخاب کنید.
روش 3 : تولید اسکریپت
با استفاده از دو روش فوق فقط میتوانستیم ساختار جداول و دادههای آن را انتقال بدهیم. برای انتقال کامل جداول مثل تریگرها، قیدها و ... میبایست از جدول یا جداول اسکریپت تولید و در نهایست اسکریپت را اجرا نماییم.
انتخاب دیتابیس مورد نظر و بعد انتخاب مواردی که قصد داریم از آنها اسکریپت ایجاد کنیم و در پایان اسکریپت مورد نظر را بر روی دیتابیس مقصد (databaseto) اجرا میکنیم.
و در پایان نهایت تشکر را از تمام عزیزان و دوستان نویسندهی سایت دارم. امیدوارم در سال 94 شاهد موفقیتهای خوبی در حوزهی نرم افزار باشیم.