🔸 الگوهای طراحی راه
حلهای معمولی برای مشکلات رایج در طراحی نرمافزار هستند. آنها مانند
نقشههای از پیش ساخته شدهای هستند که میتوانید آنها را برای حل یک مشکل
طراحی تکراری در کد خود سفارشی کنید.
🔸اینطور نیست که مثلا یک الگو
را پیدا کنید و آن را در برنامه خود کپی کنید. الگو، یک قطعه کد خاص نیست،
بلکه یک مفهوم کلی برای حل یک مشکل خاص است. شما میتوانید جزئیات الگو را
دنبال کنید و راه حلی متناسب با واقعیتهای برنامه خود را پیادهسازی
کنید.
🔸 الگوها اغلب با الگوریتمها اشتباه گرفته میشوند، زیرا هر
دو مفهوم راه حلهای معمولی برای برخی از مسائل شناخته شده را توصیف
میکنند. در حالی که یک الگوریتم همیشه مجموعه واضحی از اقدامات را تعریف
میکند که میتواند به هدفی دست یابد، یک الگو راه حلهای سطح بالا برای
مسائل سطح بالا هستند. کد یک الگوی اعمال شده برای دو برنامه مختلف ممکن
است متفاوت باشد.
🔸 همیشه منظور هر الگو را در ذهن خود مرور کنید و هنگام برخورد با یک مسئله به دنبال مناسبترین الگو بگردید.
🔸 شما نمیتوانید یک الگو را به کار بگیرید مگر آنکه آن را به خوبی فهمیده باشید. بنابراین در اولین گام باید اصول و الگوهای طراحی را هم به شکل انتزاعی و هم به شکل واقعی خوانده و تمرین کنید. دقت کنید که
یک الگو را به شکلهای مختلفی میتوان پیادهسازی کرد. هر چه پیاده
سازیهای بیشتری ببینید، به هدف و چگونگی استفاده از آن بهتر مسلط
میشوید.
راهنمای شروع سریع برای مطالعه الگوهای طراحی 👇🏻
PDF Cards: designpatternscard.pdf
DesignPatterns-online.pdf
در این مقاله آموزشی که یکی دیگر از سری مقالات آموزشی اصول و مبانی پایگاه داده پیشرفته میباشد، قصد داریم به یکی دیگر از مقولههای مهم در طراحی سیستمهای مدیریت پایگاه داده (DBMS) بپردازیم. همانطور که در مباحث قبلی بیان کردیم یکی از وظایف سیستم مدیریت پایگاه داده، حفظ سازگاری(consistency) دادهها میباشد. برای مثال یکی از راهکار هایی که برای این منظور ارائه میدهد انجام عملیات در قالب تراکنش هاست که در مبحث مربوط به تراکنش ها مفصل در مورد آن بحث کردیم. با این حال گاهی خطاها و شکست هایی (failure) در حین عملیات ممکن است پیش بیاید که منجر به خروج سیستم از وضعیت سازگار خود گردد. بعنوان مثال ممکن است سخت افزار سیستم دچار مشکل شود، مثلا دیسک از کار بیفتد (disk crash) یا آنکه برق قطع شود. خطاهای نرم افزاری نیز میتوانند جزو موارد شکست و خرابی بحساب آیند که خطای منطق برنامه (logic) از این نمونه میباشد. در چنین شرایطی بحثی مطرح میشود تحت عنوان بازیابی (recovery) و ترمیم پایگاه داده که در این مقاله قصد داریم در مورد آن صحبت کنیم. بنا به تعریف بازیابی به معنای بازگرداندن یک پایگاه داده به وضعیت سازگار گذشته خود، بعد از وقوع یک شکست یا خرابی است. توجه داشته باشید که اهمیت بازیابی و ترمیم پایگاه داده تا آنجایی است که حدود 10 درصد از سیستمهای مدیریت پایگاه داده را به خود اختصاص میدهند.
آنچه که در اینجا در مورد آن صحبت خواهیم کرد بازیابی بصورت نرم افزاری است که از آن تحت عنوان fail soft نام برده میشود. دقت داشته باشید در بیشتر مواقع میتوان از طریق نرم افزاری عمل بازیابی را انجام داد، اما در کنار راهکارهای نرم افزاری باید حتما اقدامات سخت افزاری ضروری نیز پیش بینی شود. بعنوان مثال گرفتن نسخههای پشتیبان یک امر ضروری در سیستمهای اطلاعاتی است. چرا که گاهی اوقات خرابیهای فیزیکی باعث از دست رفتن تمامی اطلاعات میگردند که در این صورت نسخههای پشتیبان میتوانند به کمک آیند و با کمک آنها سیستم را مجدد بازیابی کرد. در شکل زیر نمونه ای از روشهای پشتیبان گیری بنام mirroring نشان داده شده است که روش رایجی در سیستمهای بانک اطلاعاتی بشمار میرود. همانطور که در شکل نشان داده شده است در کنار نسخه اصلی (DISK)، نسخه(MIRROR) آن قرار داده شده است. این دو نسخه کاملا مشابه یکدیگرند و هر عملی که در DICK انجام میشود در MIRROR ان نیز اعمال میشود تا در مواقع خرابی DISK بتوان از نسخه MIRROR استفاده نمود.
در شکل زیر نمونه بسیار ساده از نحوه لاگ کردن در حین اجرای تراکنشها را مشاهده میکنید.
نیازمندیهای اصلی در بازیابی پایگاه داده
برای آنکه وارد بحث اصلی شویم باید بگویم در یک نگاه کلی میتوان گفت که ساختار زیر سیستم بازیابی پایگاه داده بر پایه سه عملیات استوار است که عبارتند از log ، redo و undo . برای آنکه بتوان در هنگام رخ دادن خطا عمل ترمیم و بازیابی را انجام داد، سیستم پایگاه داده با استفاده از مکانیزم لاگ کردن(logging) خود تمامی عملیاتی را که در پایگاه داده رخ میدهد و بنحوی منجر به تغییر وضعیت ان میگردد را در جایی ثبت و نگهداری میکند. اهمیت لاگ کردن وقایع بسیار بالاست، چرا که پس از رخ دادن شکست در سیستم ملاک ما برای بازیابی و ترمیم فایلهای لاگ (log files) می باشند.
سیستم دقیقا خط به خط این لاگها را میخواند و بر اساس وقایعی که رخ داده است تصمیمات لازم را برای بازیابی اتخاذ میکند. در حین خواندن فایلهای لاگ، سیستم برخی از وقایع را باید بی اثر کند. یعنی عمل عکس آنها را انجام دهد تا اثر آنها بر روی پایگاه داده از بین برود. به این عمل undo کردن میگوییم که همانطور که در بالا گفته شد یکی از عملیات اصلی در بازیابی است. عمل دیگری وجود دارد بنام انجام مجدد یا redo کردن که در برخی از مواقع باید صورت بگیرد. انجام مجدد همانطور که از اسمش پیداست به این معنی است که عملی که از لاگ فایل خوانده شده است باید مجدد انجام گیرد. بعنوان مثال در فایل لاگ به تراکنشی برخورد میکنیم و سیستم تصیم میگیرد که آن را مجدد از ابتدا به اجرا در آورد. دقت داشته باشید که سیستم بر اساس قوانین و قواعدی تصمیم میگیرد که تراکنشی را redo و یا undo نماید که در ادامه این بحث آن قوانین را باز خواهیم کرد.
در کنار لاگ فایل ها، که مبنای کار در بازیابی هستند، فایل دیگری نیز در سیستم وجود دارد که به DBMS در بازیابی کمک میکند. این فایل raster file نام دارد که در بخشهای بعدی این مقاله در مورد آن و کارایی آن بیشتر صحبت خواهیم نمود.
Recovery Manager
مسئولیت انجام بازیابی بصورت نرم افزاری (fail soft) بر عهده زیر سیستمی از DBMS بنام مدیر بازیابی (recovery manager) می باشد و همانطور که اشاره شد این زیر سیستم چیزی در حدود 10 در صد DBMSرا به خود اختصاص میدهد. برای آنکه این زیر سیستم بتواند مسئولیت خود را بنحو احسن انجام دهد بطوری که عمل بازیابی بدون نقص و قابل اعتماد باشد، باید به نکاتی توجه نمود. اولین نکته اینست که در لاگ کردن و همچنین خواندن لاگ فایل به جهت بازیابی و ترمیم پایگاه داده هیچ تراکنشی نباید از قلم بیفتد. تمامی تراکنشها در طول حیات سیستم باید لاگ شود تا بازیابی ما قابل اعتماد و بدون نقص باشد. نکته دوم اینست که اگر تصمیم به اجرای مجدد (redo) تراکنشی گرفته شد، طوری باید عمل Redo انجام شود که بلحاظ منطقی آن تراکنش یک بار انجام شود و تاثیرش یکبار بر دیتابیس اعمال گردد. بعنوان مثال فرض کنید که در طی یک تراکنش مبلغ یک میلیون تومان به حساب شخصی واریز میشود. مدتی بعد از اجرای و تمکیل تراکنش سیستم دچار مشکل میشود و مجبور به انجام بازیابی میشویم. در حین عمل بازیابی سیستم مدیریت بازیابی و ترمیم تصمیم به اجرای مجدد تراکنش مذکور میگیرد. در اینجا سیستم نباید مجدد یک میلیون تومان دیگر به حساب ان شخص واریز کند. چرا که در این صورت موجودی حساب فرد دو میلیون تومان خواهد شد که این اشتباه است. سیستم باید طوری عمل کند که پس از انجام مجدد تراکنش باز هم موجودی همان یک میلیون تومان باشد. یعنی مثلا ابتدا یک میلیون کسر و سپس یک میلیون به آن اضافه کند. این مسئله نکته بسیار مهمی است که طراحان DBMS باید حتما آن را مد نظر قرار دهند.
لاگ کردن:
همانطور که گفته شد هر تغییری که در پایگاه داده رخ میدهد باید لاگ شود. لاگ کردن به این معنی است که هر گونه عملیاتی که در پایگاه داده انجام میشود در فایل هایی به نام فایل لاگ (log file) ذخیره شود. توجه داشته باشید لاگ فایلها در بسیاری از سیستمهای نرم افزاری دیگر نیز استفاده میشود. بعنوان مثال در سیستم عامل ما انواع مختلفی فایل لاگ داریم. بعنوان نمونه یک فراخوانی سیستمی (system call) که در سیستم عامل توسط کاربر انجام میشود در فایلی مخصوص لاگ میشود. یکی از کاربرد این لاگ فایل شناسایی کاربران بد و خرابکار (malicious users) می تواند باشد که کارهای تحقیقاتی زیادی هم در این رابطه انجام شده و میشود. بدین صورت که میتوان با بررسی این فایل لاگ و آنالیز فراخوانیهای یک کاربر بدنبال فراخوانی هایی غیر عادی گشت و از این طریق تشخیص داد که کاربر بدنبال خرابکاری بوده یا خیر. مشابه چنین فایل هایی در DBMS نیز وجود دارد که هدف نهایی تمامی انها حفظ صحت، سازگاری و امنیت اطلاعات میباشد.
حال ببینیم در لاگ فایل مربوط به بازیابی اطلاعات چه چیز هایی نوشته میشود. در طول حیات پایگاه داده عملیات بسیار گوناگونی انجام میگیرد که جزئیات تمامی آنها باید لاگ شود. بعنوان مثال هنگامی که رکوردی درج میشود در لاگ فایل باید مشخص شود که در چه زمانی، توسط چه کاربری چه رکوردی، با چه شناسه ای به کدام جدول از دیتابیس اضافه شد. یا اینکه در موقع حذف باید مشخص شود چه رکوردی از چه جدولی حذف شده است. در هنگام بروز رسانی (update) باید علاوه بر مواردی که در درج لاگ میکنیم نام فیلد ویرایش شده، مقدار قبلی و مقدار جدید آن نیز مشخص شود. تمامی عملیات ریز لاگ میشوند و هیچ عملی نباید از قلم بیفتد. بنابراین فایل لاگ با سرعت زیاد بزرگ خواهد و اندازه دیتابیس نیز افزایش خواهد یافت. این افزایش اندازه مشکل ساز میتواند باشد. چراکه معمولا فضایی که ما بر روی دیسک به دیتابیس اختصاص میدهیم فضایی محدود است. بهمین دلیل به لحاظ فیزیکی نمیتوان فایل لاگی با اندازه نامحدود داشت. این در حالی است که چنین فایل هایی باید نامحدود باشند تا همه چیز را در خود ثبت نمایند. برای پیاده سازی ظرفیت نامحدود به لحاظ منطقی یکی از روشها پیاده سازی فایلهای حلقه ای(circular) است. بدین صورت که هنگامی که سیستم به انتهای فایل لاگ میرسد مجددا به ابتدا آن بر میگردد و از ابتدا شروع به نوشتن میکند. البته چنین ساختار هایی بدون اشکال نیستند. چرا که پس از رسیدن به انتهای فایل و شروع مجدد از ابتدا ما برخی از تراکنشهای گذشته را از دست خواهیم داد. این مسئله یکی از دلایلی است که بر اساس آن پیشنهاد میشود تا جایی که امکان دارد تراکنشها را کوچک پیاده سازی کنیم. گاهی اوقات بر روی لاگ فایل عمل فشرده سازی را نیز انجام میدهند. البته فشرده سازی بمعنای رایج ان مطرح نیست. بلکه منظور از فشرده سازی آنست که رکورد هایی که غیر ضروری هستند را حذف کنیم. بعنوان مثال فرض کنید رکوردی را از 50 به 60 تغییر داده ایم. مجددا همان رکورد را از 60 به 70 تغییر میدهیم. در این صورت برای این عملیات دو رکورد در فایل لاگ ثبت شده است که در هنگام فشرده سازی در صورت امکان میتوان ان دو را به یک رکورد تبدیل نمود (تغییر از 50 به 70 را بجای ان دو لاگ کرد). بعنوان مثال دیگر فرض کنید تراکنشی در گذشته دور انجام شده است و با موفقیت کامیت شده است. میتوان رکوردهای لاگ مربوط به این تراکنش را نیز بنا به شرایط حذف کرد.
دقت داشته باشید که ما عملیاتی مانند عملیات محاسباتی را در این لاگ فایل ثبت نمیکنیم. بعنوان مثال اگر دو فیلد با هم باید جمع شوند و نتیجه در فیلدی باید بروز گردد، جمع دو فیل را در سیستم لاگ نمیکنیم بلکه تنها مقدار نهایی ویرایش شده را ثبت میکنیم. چرا که عملیات محاسباتی در بازیابی ضروری نیستند و ثبت انها تنها باعث بزرگ شدن فایل میشود.
در برخی از سیستمهای حساس، ممکن است برای فایلهای لاگ هم یک کپی تهیه کنند تا در صورت بروز خطا در لاگ فایل بتوان آن را نیز بازیابی نمود.
انواع رکوردهای لاگ فایل :
در فایل لاگ رکوردهای مختلفی ممکن است درج شود که در این جا به چند نمونه از انها اشاره میکنیم:
- [start-transaction, T]
- [write-item, T, X, old-value, new-value]
- [read-item, T, X]
- [commit, T]
در شکل زیر نمونه بسیار ساده از نحوه لاگ کردن در حین اجرای تراکنشها را مشاهده میکنید.
در این شکل نکته ای وجود دارد که به آن اشاره ای میکنیم. همانطور که میبینید در شکل از اصطلاحimmediate update استفاده شده است. در برخی از سیستمها تغییرات تراکنشها بصورت فوری اعمال میشوند که اصطلاحا میگوییم immediate updates دارند. در مقابل این اصطلاح ما deffered را داریم. در این مدل تغییرات در انتهای کار اعمال میشوند (در زمان commit).
Write-Ahead Log (WAL) :
بر اساس آنچه تابحال گفته شد هر تغییری در پایگاه داده شامل دو عمل میشود. یکی انجام تغییر (اجرای تراکنش) و دیگری ثبت آن در لاگ فایل. حال سوالی که ممکن است مطرح شود اینست که کدامیک از این دو کار بر دیگری تقدم دارد؟ آیا اول تراکنش را باید اجرا کرد و سپس لاگ آن را نوشت و یا برعکس باید عمل کرد. یعنی پیش از هر تراکنشی ابتدا باید لاگ آن را ثبت کرد و سپس تراکنش را اجرا نمود. بر همین اساس سیاستی تعریف میشود بنام سیاست write-ahead log یا WAL که سوال دوم را تایید میکند. یعنی میگوید هنگامی که قرار است عملی در پایگاه داده صورت گیرد ابتدا باید ان عمل بطور کامل لاگ شود و سپس آن را اجرا نمود. این سیاست هدفی را دنبال میکند.
پیش از آنکه هدف این سیاست را توضیح دهیم لازم است نکته ای در مورد عملیات redo و undo بیان شود. شما با این دو عملیات در برنامههای مختلفی مانند آفیس، فتوشاپ و غیره آشنایی دارید. اما توجه داشته باشید که در DBMS این دو عملیات از پیچیدگی بیشتری برخوردار میباشند. اصطلاحا در پایگاه داده گفته میشود که عملیات redo و undo باید idempotent باشند. معنی idempotent بودن اینست که اگر قرار است تراکنشی در پایگاه داده undo شود، اگر بارها و بارها عمل undo را بر روی آن تراکنش انجام دهیم مانند این باشد این عمل را تنها یکبار انجام داده ایم. در مورد redo نیز این مسئله صادق است.
در تعریف idempotent بودن ویژگیهای دیگری نیز وجود دارد. بعنوان مثال گفته میشود undo بر روی عملی که هنوز انجام نشده هیچ تاثیری نخواهد داشت. این مسئله یکی از دلایل اهمیت استفاده از سیاستWAL را بیان میکند. بعنوان مثال فرض کنید میخواهیم رکوردی را در جدولی درج کنیم. همانطور که گفتیم دو روش برای این منظور وجود دارد. در روش اول ابتدا رکورد را در جدول مورد نظر درج میکنیم و سپس لاگ آن را مینویسیم. در این صورت اگر پس از درج رکورد سیستم با مشکل مواجه شود و مجبور به انجام عمل بازیابی شویم، بدلیل آنکه برای بازیابی بر اساس لاگ فایل عمل میکنیم و برای درج آن رکورد لاگی در سیستم ثبت نشده است، آن عمل را از دست میدهیم. در نتیجه بازیابی بطور کامل نمیتواند سیستم را ترمیم نماید. چراکه درج صورت گرفته اما لاگی برای آن ثبت نشده است. در روش دوم فرض کنید بر اساس سیاست WAL عمل میکنیم. ابتدا لاگ مربوط به درج رکورد را مینویسم. سپس پیش از آنکه عمل درج را انجام دهیم سیستم crash می کند و مجبور به بازیابی میشویم. دراین صورت هنگامی که Recovery Manager به رکورد مربوط به عمل درج در لاگ فایل میرسد یا باید آن را redo کند و یا undo (بعدا میگوییم بر چه اساس تصمیم گیری میکند). اگر تصمیم به undo کردن بگیرد بدلیل ویژگی گفته شده، عمل undo بر روی عملی که انجام نشده است هیچ تاثیری در پایگاه داده نخواهد گذاشت. اگر عمل redo را بخواهد انجام دهد نیز بدلیل آنکه لاگ مربوط به عمل درج در سیستم ثبت شده بدون هیچ مشکلی این عمل مجددا انجام میگیرد. بنابراین بر خلاف روش قبل هیچ تراکنشی را از دست نمیدهیم و سیستم بطور کامل بازیابی و ترمیم میشود. به این دلیل است که توصیه میشود در طراحیDBMS ها سیاست WAL بکار گیری شود.
نکته بسیار مهمی که در اینجا ذکر آن ضروری بنظر میرسد اینست که در هنگام لاگ کردن تراکنش ها، علاوه بر آنکه خود تراکنش لاگ میشود و این لاگها نیز در فایل فیزیکی باید نوشته شوند، عملیات لازم برای Redo کردن و یا undo کردن آن نیز لاگ میشود تا سیستم در هنگام بازیابی بداند که چه کاری برایredo و undo کردن باید انجام دهد. توجه داشته باشید در این سیاست، COMMIT تراکنشی انجام نمیشود مگر انکه تمامی لاگهای مربوط به عملیات redo و undo آن تراکنش در لاگ فایل فیزیکی ثبت شود.
قرار دادن checkpoint در لاگ فایل:
گفتیم که در هنگام رخ دادن یک خطا، برای بازیابی و ترمیم پایگاه داده به لاگ فایل مراجعه میکنیم و بر اساس تراکنش هایی که در آن ثبت شده است، عمل ترمیم را انجام میدهیم. علاوه بر آن، این را هم گفتیم که لاگ فایل، معمولا فایلی بزرگ است که از نظر منطقی با ظرفیت بینهایت پیاده سازی میشود. حال سوال اینجاست که اگر بعد گذشت ساعتها از عمر پایگاه داده و ثبت رکوردهای متعدد در لاگ فایل خطایی رخ داد، آیا مدیر بازیابی و ترمیم پایگاه داده باید از ابتدای لاگ فایل شروع به خواندن و بازیابی نماید؟ اگر چنین باشد در بانکهای اطلاعاتی بسیار بزرگ عمل بازیابی بسیار زمان بر و پر هزینه خواهد بود. برای جلوگیری از این کار مدیر بازیابی پایگاه داده وظیفه دارد در فواصل مشخصی در لاگ فایل نقاطی را علامت گذاری کند تا اگر خطایی رخ داد عمل undo کردن تراکنش را تنها تا همان نقطه انجام دهیم (نه تا ابتدای فایل). به این نقاط checkpoint گفته میشود که انتخاب صحیح آنها تاثیر بسیاری در کیفیت و کارایی عمل بازیابی دارد.
نکته بسیار مهمی که در مورد checkpoint ها وجود دارد اینست که آنها چیزی فراتر از یک علامت در لاگ فایل هستند. هنگامی که DBMS به زمانی میرسد که باید در لاگ فایل checkpoint قرار دهد، باید اعمال مهمی ابتدا انجام شود. اولین کاری که در زمان checkpoint باید صورت بگیرد اینست که رکورد هایی از لاگ فایل که هنوز به دیسک منتقل نشده اند، بر روی لاگ فایل فیزیکی بر روی دیسک نوشته شوند. به این عمل flush کردن لاگ رکوردها نیز گفته میشود. دومین کاری که در این زمان باید صورت بگیرید اینست که رکوردی خاص بعنوان checkpoint record در لاگ فایل درج گردد. در این رکورد در واقع تصویری از وضعیت دیتابیس در زمان checkpoint را نگهداری میکنیم. دقت داشته باشید که در زمان checkpoint،DBMS برای یک لحظه تمامی تراکنشهای در حال اجرا را متوقف میکند و لیستی از این تراکنشها را در رکورد مربوط به checkpoint نگهداری میکند تا در زمان بازیابی بداند چه تراکنش هایی در آن زمان هنوز commit نشده و تاثیرشان به پایگاه داده اعمال نشده است. سومین کاری که در این لحظه بایدا انجام گیرد ایسنت که اگر داده هایی از پایگاه داده هستند که عملیات مربوط به آنها COMMIT شده اند اما هنوز به دیسک منتقل نشده اند بر روی دیسک نوشته شوند.آخرین کاری که باید انجام شود اینست که آدرس رکورد مربوط به checkpoint در فایلی بنام raster file ذخیره شود. علت این کار آنست که در هنگام بازیابی بتوانیم بسرعت آدرس آخرین checkpoint را بدست آوریم.
عمل UNDO :
در اینجا قصد داریم معنی و مفهوم عمل undo را بر روی انواع مختلف تراکنشها را بیان کنیم.
- هنگامی که میگوییم یک عمل بروز رسانی (update) را میخواهیم undo کنیم منظور اینست که مقدار قبلی فیلد مورد نظر را به جای مقدار جدید آن قرار دهیم.
- هنگامی که عمل undo را بر روی عملیات حذف میخواهیم انجام دهیم منظور اینست که مقدار قبلی جدول (رکورد حذف شده) را مجددا باز گردانیم.
- هنگامی که عمل undo را بر روی عملیات درج (insert) می خواهیم انجام دهیم منظور این است که مقدار جدید درج شده در جدول را حذف کنیم.
انجام عمل بازیابی و ترمیم :
تا اینجا مقدمات لازم برای ترمیم پایگاه داده را گفتیم. حال میخواهیم بسراغ چگونگی انجام عمل ترمیم برویم. هنگامی که میخواهیم پایگاه داده ای را ترمیم کنیم اولین کاری که باید انجام گیرد اینست که بوسیله raster file، آدرس آخرین checkpoint لاگ فایل را پیدا کنیم. سپس فایل لاگ را از نقطه checkpoint به پایین اسکن میکنیم. در هنگام اسکن کردن باید تراکنشها را به دو گروه تقکیک کنیم، تراکنش هایی که باید undo شوند و تراکنش هایی که باید عمل redo بر روی انها انجام گیرد. علت این کار اینست که در هنگام undo کردن از انتهای لاگ فایل به سمت بالا باید حرکت کنیم و برای Redo کردن بصورت عکس، از بالا به سمت پایین میآییم. بنابراین جهت حرکت در لاگ فایل برای این دو عمل متفاوت است. بهمین دلیل باید ابتدا تراکنشها تفکیک شوند. اما چگونه این تفکیک صورت میگیرد؟
هنگام اسکن کردن (از نقطه checkpoint به سمت انتهای لاگ فایل (لحظه خطا) )، هر تراکنشی که رکورد لاگ مربوط به commit آن دیده شود باید در گروه redo قرار گیرد. بعبارت دیگر تراکنش هایی که در این فاصله commit شده اند را در گروه redo قرار میدهیم. در مقابل هر تراکنشی که commit آن دیده نشود (commit نشده اند) باید undo شود. باز هم تاکید میکنیم که این عمل تنها در فاصله بین آخرینcheckpoint تا لحظه وقوع خطا انجام میشود.
دقت داشته باشید که در شروع اسکن کردن اولین رکوردی که خوانده میشود رکورد مربوط بهcheckpoint می باشد که حاوی تراکنش هایی است که در زمان checkpoint در حال انجام بوده اند، یعنی هنوز commit نشده اند. بنابراین تمامی این تراکنشها را ابتدا در گروه تراکنش هایی که باید undo شوند قرار میدهیم. بمرور که عمل اسکن را ادامه میدهیم اگر به تراکنشی رسیدیم که رکورد مربوط به شروع ان ثبت شده باشد، باید آن تراکنش را در لیست undo قرار دهیم. تراکنش هایی که commit آنها دیده شود را نیز باید از گروه undo حذف و به گروه Redo اضافه نماییم. پس از خاتمه عمل اسکن ما دو لیست از تراکنشها داریم. یکی تراکنش هایی که باید Redo شوند و دیگری آنهایی که باید undo گردند.
پس از مشخص شدن دو لیست Redo و Undo، باید دو کار دیگر انجام شود. اولین کار اینست که تراکنش هایی که باید undo شوند را از پایین به بالا undo کنیم. یکی از دلایل اینکه ابتدا عملیات undo را انجام میدهیم ایسنت هنگامی که تراکنش ها commit نشده اند، قفل هایی را که بر روی منابع پایگاه داده زده اند هنوز آزاد نکرده اند. با عمل undo کردن این قفلها را آزاد میکنیم و بدین وسیله کمک میکنیم تا درجه همروندی پایگاه داده پایین نیاید. پس از خاتمه عملیات undo، به نقطه checkpoint می رسیم. در این لحظه مانند اینست که هیچ تراکنشی در سیستم وجود ندارد. حالا بر اساس لیست redo از بالا یعنی نقطهcheckpoint به سمت پایین فایل لاگ حرکت میکنیم و تراکنشهای موجود در لیست redo را مجدد اجرا میکنیم. پس از خاتمه این گام نیز عملیات بازیابی خاتمه مییابد میتوان گفت سیستم به وضعیت پایدار قبلی خود باز گشسته است.
برای روشنتر شدن موضوع به شکل زیر توجه کنید. در این شکل نقطه Tf زمان رخ دادن خطا را در پایگاه داده نشان میدهد. اولین کاری که برای بازیابی باید انجام گیرد، همانطور که گفته شده اینست که آدرس مربوط به زمان checkpoint (Tc) از raster file خوانده شود. پس از این کار از لحظه Tc به سمت Tf شروع به اسکن کردن لاگ فایل میکنیم. بدلیل آنکه در زمان Tc دو تراکنش T2 و T3 در حال اجرا بودند (و نام آنها در checkpoint record نیز ثبت شده است)، این دو تراکنش را در لیست redo قرار میدهیم. سپس عمل اسکن را به سمت پایین ادامه میدهیم. در حین اسکن کردن ابتدا به رکورد start trasnactionمربوط به تراکنش T4 می رسیم. بهمین دلیل این تراکنش را به لیست undo ها اضافه میکنیم. پس از آن به commit تراکنش T2 می رسیم. همانطور که گفته شد باید T2 را از لیست undo ها خارج و به یست تراکنش هایی که باید redo شوند اضافه گردد. سپس به تراکنش T5 می رسیم که تازه آغاز شده است. ان را نیز در گروه undo قرار میدهیم. بعد از ان رکورد مربوط به commit تراکنش T4 دیده میشود و ان را از لیست undo حذف و لیست redo اضافه میکنی. اسکن را ادامه میدهیم تا به نقطه Tf می رسیم. در ان لحظه لیست undo ها شامل دو تراکنش T3 و T5 و لیست Redo ها شامل تراکنش های T2 و T4 می باشند. در مورد تراکنش T1 نیز چون پیش از لحظه Tc کامیت شده است عملی صورت نمیگیرد.
موفق و پیروز باشید
Foundation
Blueprint
Skeleton
Less Framework
YAML
Gumby
Wirefy
Golden Grid System
BluCSS
Amazium
The 1140 CSS Grid
320 and Up
Titan Framework
Columnal
Gridless
InuitCSS
از فریم ورکها استفاده نمی کنم
قالب ساده خاص خودم را دارم
فریم ورک توسعه یافته شرکتی
جهت هر وب سایت، قالبی خاص خودش را طراحی میکنم
Unsemantic
960 GS
استفاده از کامپوننت AutoComplete کتابخانهی Angular Material
کتابخانهی Angular Material به همراه یک کامپوننت Auto Complete نیز هست. در اینجا قصد داریم آنرا در یک صفحهی دیالوگ جدید نمایش دهیم و با انتخاب کاربری از لیست توصیههای آن و کلیک بر روی دکمهی نمایش آن کاربر، جزئیات کاربر یافت شده را نمایش دهیم.
به همین جهت ابتدا کامپوننت جدید search-auto-complete را به صورت زیر به مجموعهی کامپوننتهای تعریف شده اضافه میکنیم:
ng g c contact-manager/components/search-auto-complete --no-spec
import { SearchAutoCompleteComponent } from "./components/search-auto-complete/search-auto-complete.component"; @NgModule({ entryComponents: [ SearchAutoCompleteComponent ] }) export class ContactManagerModule { }
در ادامه برای نمایش این کامپوننت به صورت popup، دکمهی جدید جستجو را به toolbar اضافه میکنیم:
برای این منظور به فایل toolbar\toolbar.component.html مراجعه کرده و دکمهی جستجو را پیش از دکمهی نمایش منو، قرار میدهیم:
<span fxFlex="1 1 auto"></span> <button mat-button (click)="openSearchDialog()"> <mat-icon>search</mat-icon> </button> <button mat-button [matMenuTriggerFor]="menu"> <mat-icon>more_vert</mat-icon> </button>
@Component() export class ToolbarComponent { constructor( private dialog: MatDialog, private router: Router) { } openSearchDialog() { const dialogRef = this.dialog.open(SearchAutoCompleteComponent, { width: "650px" }); dialogRef.afterClosed().subscribe((result: User) => { console.log("The SearchAutoComplete dialog was closed", result); if (result) { this.router.navigate(["/contactmanager", result.id]); } }); } }
کنترلر جستجوی سمت سرور و سرویس سمت کلاینت استفاده کنندهی از آن
در اینجا کنترلر و اکشن متدی را جهت جستجوی قسمتی از نام کاربران را مشاهده میکنید:
namespace MaterialAspNetCoreBackend.WebApp.Controllers { [Route("api/[controller]")] public class TypeaheadController : Controller { private readonly IUsersService _usersService; public TypeaheadController(IUsersService usersService) { _usersService = usersService ?? throw new ArgumentNullException(nameof(usersService)); } [HttpGet("[action]")] public async Task<IActionResult> SearchUsers(string term) { return Ok(await _usersService.SearchUsersAsync(term)); } } }
از این کنترلر به نحو ذیل در برنامهی Angular برای ارسال اطلاعات و انجام جستجو استفاده میشود:
import { HttpClient, HttpErrorResponse } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable, throwError } from "rxjs"; import { catchError, map } from "rxjs/operators"; import { User } from "../models/user"; @Injectable({ providedIn: "root" }) export class UserService { constructor(private http: HttpClient) { } searchUsers(term: string): Observable<User[]> { return this.http .get<User[]>(`/api/Typeahead/SearchUsers?term=${encodeURIComponent(term)}`) .pipe( map(response => response || []), catchError((error: HttpErrorResponse) => throwError(error)) ); } }
تکمیل کامپوننت جستجوی کاربران توسط یک AutoComplete
پس از این مقدمات که شامل تکمیل سرویسهای سمت سرور و کلاینت دریافت اطلاعات کاربران جستجو شده و نمایش صفحهی جستجو به صورت یک popup است، اکنون میخواهیم محتوای این popup را تکمیل کنیم. البته در اینجا فرض بر این است که مطلب «کنترل نرخ ورود اطلاعات در برنامههای Angular» را پیشتر مطالعه کردهاید و با جزئیات آن آشنایی دارید.
تکمیل قالب search-auto-complete.component.html
<h2 mat-dialog-title>Search</h2> <mat-dialog-content> <div fxLayout="column"> <mat-form-field class="example-full-width"> <input matInput placeholder="Choose a user" [matAutocomplete]="auto1" (input)="onSearchChange($event.target.value)"> </mat-form-field> <mat-autocomplete #auto1="matAutocomplete" [displayWith]="displayFn" (optionSelected)="onOptionSelected($event)"> <mat-option *ngIf="isLoading" class="is-loading"> <mat-spinner diameter="50"></mat-spinner> </mat-option> <ng-container *ngIf="!isLoading"> <mat-option *ngFor="let user of filteredUsers" [value]="user"> <span>{{ user.name }}</span> <small> | ID: {{user.id}}</small> </mat-option> </ng-container> </mat-autocomplete> </div> </mat-dialog-content> <mat-dialog-actions> <button mat-button color="primary" (click)="showUser()"> <mat-icon>search</mat-icon> Show User </button> <button mat-button color="primary" [mat-dialog-close]="true"> <mat-icon>cancel</mat-icon> Close </button> </mat-dialog-actions>
سپس نحوهی اتصال یک Input box معمولی را به کامپوننت mat-autocomplete مشاهده میکنید که شامل این موارد است:
- جعبه متنی که قرار است به یک mat-autocomplete متصل شود، توسط دایرکتیو matAutocomplete به template reference variable تعریف شدهی در آن autocomplete اشاره میکند. برای مثال در اینجا این متغیر auto1 است.
- برای انتقال دکمههای فشرده شدهی در input box به کامپوننت، از رخداد input استفاده شدهاست. این روش با هر دو نوع حالت مدیریت فرمهای Angular سازگاری دارد و کدهای آن یکی است.
در کامپوننت mat-autocomplete این تنظیمات صورت گرفتهاند:
- در لیست ظاهر شدهی توسط یک autocomplete، هر نوع ظاهری را میتوان طراحی کرد. برای مثال در اینجا نام و id کاربر نمایش داده میشوند. اما برای تعیین اینکه پس از انتخاب یک آیتم از لیست، چه گزینهای در input box ظاهر شود، از خاصیت displayWith که در اینجا به متد displayFn کامپوننت متصل شدهاست، کمک گرفته خواهد شد.
- از رخداد optionSelected برای دریافت آیتم انتخاب شده، در کدهای کامپوننت استفاده میشود.
- در آخر کار نمایش لیستی از کاربران توسط mat-optionها انجام میشود. در اینجا برای اینکه بتوان تاخیر دریافت اطلاعات از سرور را توسط یک mat-spinner نمایش داد، از خاصیت isLoading تعریف شدهی در کامپوننت استفاده خواهد شد.
تکمیل کامپوننت search-auto-complete.component.ts
کدهای کامل این کامپوننت را در ادامه مشاهده میکنید:
import { Component, OnDestroy, OnInit } from "@angular/core"; import { MatAutocompleteSelectedEvent, MatDialogRef } from "@angular/material"; import { Subject, Subscription } from "rxjs"; import { debounceTime, distinctUntilChanged, finalize, switchMap, tap } from "rxjs/operators"; import { User } from "../../models/user"; import { UserService } from "../../services/user.service"; @Component({ selector: "app-search-auto-complete", templateUrl: "./search-auto-complete.component.html", styleUrls: ["./search-auto-complete.component.css"] }) export class SearchAutoCompleteComponent implements OnInit, OnDestroy { private modelChanged: Subject<string> = new Subject<string>(); private dueTime = 300; private modelChangeSubscription: Subscription; private selectedUser: User = null; filteredUsers: User[] = []; isLoading = false; constructor( private userService: UserService, private dialogRef: MatDialogRef<SearchAutoCompleteComponent>) { } ngOnInit() { this.modelChangeSubscription = this.modelChanged .pipe( debounceTime(this.dueTime), distinctUntilChanged(), tap(() => this.isLoading = true), switchMap(inputValue => this.userService.searchUsers(inputValue).pipe( finalize(() => this.isLoading = false) ) ) ) .subscribe(users => { this.filteredUsers = users; }); } ngOnDestroy() { if (this.modelChangeSubscription) { this.modelChangeSubscription.unsubscribe(); } } onSearchChange(value: string) { this.modelChanged.next(value); } displayFn(user: User) { if (user) { return user.name; } } onOptionSelected(event: MatAutocompleteSelectedEvent) { console.log("Selected user", event.option.value); this.selectedUser = event.option.value as User; } showUser() { if (this.selectedUser) { this.dialogRef.close(this.selectedUser); } } }
- توسط متد displayFn، عبارتی که در نهایت پس از انتخاب از لیست نمایش داده شده در input box قرار میگیرد، مشخص خواهد شد.
- در متد onOptionSelected، میتوان به شیء انتخاب شدهی توسط کاربر از لیست mat-autocomplete دسترسی داشت.
- این شیء انتخاب شده را در متد showUser و توسط سرویس MatDialogRef به کامپوننت toolbar که در حال گوش فرادادن به رخداد بسته شدن کامپوننت جاری است، ارسال میکنیم. به این صورت است که کامپوننت toolbar میتواند کار هدایت به جزئیات این کاربر را انجام دهد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
برای اجرای آن:
الف) ابتدا به پوشهی src\MaterialAngularClient وارد شده و فایلهای restore.bat و ng-build-dev.bat را اجرا کنید.
ب) سپس به پوشهی src\MaterialAspNetCoreBackend\MaterialAspNetCoreBackend.WebApp وارد شده و فایلهای restore.bat و dotnet_run.bat را اجرا کنید.
اکنون برنامه در آدرس https://localhost:5001 قابل دسترسی است.
ترجمه و تالیف: بهروز راد
وضعیت: در دست چاپ
Web API چیست؟
Web API، نوع قالب جدیدی برای پروژههای مبتنی بر وب در NET. است که بر مبنای اصول و الگوهای موجود در ASP.NET MVC ساخته شده است و همراه با ASP.NET MVC 4 وجود دارد. Web API توسعه گران را قادر میسازد تا با استفاده از یک الگوی ساده که در Controllerها پیاده سازی میشود، وب سرویسهای مبتنی بر پروتوکل HTTP را با کدها و تنظیمات کم ایجاد کنند. این سبک جدید برای ایجاد وب سرویس ها، میتواند در انواع پروژههای NET. مانند ASP.NET MVC، ASP.NET Web Forms، Windows Application و ... استفاده شود.
یک سوال کاملاً منطقی در اینجا به وجود میآید. چرا نیاز به بستری جدید برای ایجاد وب سرویس داریم؟ آیا در حال حاضر مایکروسافت بستری محبوب و فراگیر برای توسعهی وب سرویس هایی که بتوانند با پروتوکل SOAP تعامل داشته باشند در اختیار ندارد؟ مگر وب سرویسهای ASMX از زمان معرفی ASP.NET وجود نداشته اند؟ آیا تکنولوژی WCF مایکروسافت، بیشترین انعطاف پذیری و قدرت را برای تولید وب سرویسها در اختیار قرار نمیدهد؟ وب سرویسها جایگاه خود را یافته اند و توسعه گران با تکنولوژیهای موجود به خوبی آنها را پیاده سازی و درک میکنند. چرا Web API؟
چرا Web Api؟
برای پاسخ به این سوال، باید برخی مشکلات را بررسی کنیم و ببینیم ابزارهای موجود چه راه حلی برای آنها در نظر گرفته اند. اگر با گزینه هایی که در ادامه میآیند موافق هستید، خواندن این مطلب را ادامه دهید، و اگر اعتقادی به آنها ندارید، پس نیازهای شما به خوبی با بسترهای موجود پاسخ داده میشوند.
- من معتقد هستم که راه بهتری برای ایجاد وب سرویسها وجود دارد.
- من معتقد هستم که روشهای سادهتری برای ایجاد وب سرویسها وجود دارد و WCF بیش از حد پیچیده است.
- من معتقد هستم که تکنولوژیهای پایهی وب مانند اَفعال GET، POST، PUT و DELETE برای انجام اَعمال مختلف توسط وب سرویسها کافی هستند.
تفاوت Web API و WCF
وب سرویسهای ASMX تا چندین سال، انتخاب اول برای ایجاد وب سرویسهای مبتنی بر پروتوکل SOAP با استفاده از پروتوکل HTTP بودند. وب سرویسهای ASMX، از وب سرویسهای ساده که نیاز به قابلیت تعامل پایین داشتند و در نتیجه به پروتوکل SOAP نیز وابسته نبودند پشتیبانی نمیکردند. WCF جای وب سرویسهای ASMX را گرفت و خود را به عنوان آخرین و بهترین روش برای ایجاد وب سرویسها در بستر NET. معرفی کرد. نمونه ای از یک سرویس WCF بر مبنای پروتوکل HTTP در NET. به صورت ذیل است.
[ServiceContract] public interface IService1 { [OperationContract] string GetData(int value); [OperationContract] CompositeType GetDataUsingDataContract(CompositeType composite); } ... public class Service1 : IService1 { public string GetData(int value) { return string.Format("You entered: {0}", value); } public CompositeType GetDataUsingDataContract(CompositeType composite) { if (composite == null) { throw new ArgumentNullException("composite"); } if (composite.BoolValue) { composite.StringValue += "Suffix"; } return composite; } }
نتیجه گیری
Web API، یک روش جدید و آسان برای ایجاد وب سرویس ها، بر مبنای مفاهیم آشنای ASP.NET MVC و پایهی وب است. از این روش میتوان در انواع پروژههای NET. استفاده کرد.
MVVM و نمایش دیالوگها
از مطالب خوب مفیدتون تشکر می کنم.
سوال: راهی برای مدیریت دسترسی کاربران به Command ها وجود داره.
به این صورت که در فرم تعریف دسترسی ها لیست فرامین نمایش داده شود و سپس آنها را برای کاربران یا گروههای کاربری فعال و غیر فعال کنیم.
خلاصه کلام: راهی برای نمایش لیست کردن فرامین یک ویومدل وجود داره یانه؟
منابع مطالعاتی مرتبط
این موضوع اینقدر مهم است که تابحال چندین کتاب در مورد آن تالیف شده است:
Temporal Data & the Relational Model
Trees and Hierarchies in SQL
Developing Time-Oriented Database Applications in SQL
Temporal Data: Time and Relational Databases
Temporal Database Entries for the Springer Encyclopedia of Database Systems
Temporal Database Management
نکته مهمی که در این مآخذ وجود دارند، واژه کلیدی «Temporal data » است که میتواند در جستجوهای اینترنتی بسیار مفید واقع شود.
بررسی ابعاد زمان
فرض کنید کارمندی را استخدام کردهاید که ساعتی 2000 تومان از ابتدای فروردین ماه حقوق دریافت میکند. حقوق این شخص از ابتدای مهرماه قرار است به ساعتی 2400 تومان افزایش یابد. اگر مامور مالیات در بهمن ماه در مورد حقوق این شخص سؤال پرسید، ما چه پاسخی را باید ارائه دهیم؟ قطعا در بهمن ماه عنوان میکنیم که حقوقش ساعتی 2400 تومان است؛ اما واقعیت این است که این عدد از ابتدای استخدام او ثابت نبوده است و باید تاریخچه تغییرات آن، در نحوه محاسبه مالیات سال جاری لحاظ شود.
بنابراین در مدل سازی این سیستم به دو زمان نیاز داریم:
الف) actual time یا زمان رخ دادن واقعهای. برای مثال حقوق شخصی در تاریخ ابتدای مهر ماه تغییر کرده است. به این تاریخ در منابع مختلف Valid time نیز گفته میشود.
ب) record time یا زمان ثبت یک واقعه؛ مثلا زمان پرداخت حقوق. به آن Transaction time هم گفته شده است.
یک مثال:
record date actual date حقوق دریافتی 1392/01/01 1392/01/01 2000/روز 1392/02/01 1392/01/01 2000/روز ... 1392/07/01 1392/07/01 2400/روز ... 1392/17/01 1392/07/01 2400/روز
به ترکیب Valid Time و Transaction Time، اصطلاحا Bitemporal data میگویند.
مشکلات طراحیهای متداول اطلاعات وابسته به زمان
در طراحیهای متداول، عموما یک جدول کارمندان وجود دارد و یک جدول لیست حقوقهای پرداختی. رکوردهای لیست حقوقهای پرداختی نیز توسط یک کلید خارجی به اطلاعات هر کارمند متصل است؛ از این جهت که نمیخواهیم اطلاعاتی تکراری را در جدول لیست حقوقی ثبت کنیم و طراحی نرمال سازی شدهای مدنظر میباشد.
خوب؛ اول مهرماه حقوق شخصی تغییر کرده است. بنابراین کارمند بخش مالی اطلاعات شخص را به روز میکند. با این کار، کل سابقه حقوقهای پرداختی شخص نیز از بین خواهد رفت. چون وجود این کلید خارجی به معنای استفاده از آخرین اطلاعات به روز شده یک کارمند در جدول لیست حقوقی است. الان اگر از جدول لیست حقوقی گزارش بگیریم، کارمندان همواره از آخرین حقوق به روز شده خودشان استفاده خواهند کرد.
راه حلهای متفاوت مدل سازی اطلاعات وابسته به زمان
برای رفع این مشکل مهم، راه حلهای متفاوتی وجود دارند که در ادامه آنها را بررسی خواهیم کرد.
الف) نگهداری اطلاعات وابسته به زمان در جداول نهایی مرتبط
اگر حقوق پایه شخص در زمانهای مختلف تغییر میکند، بهتر است عدد نهایی این حقوق پرداختی نیز در یک فیلد مشخص، در همان جدول لیست حقوقی ثبت شود. این مورد به معنای داشتن «دادهای تکراری» نیست. از این جهت که دادهای تکراری است که اطلاعات آن در تمام زمانها، دارای یک مقدار و مفهوم باشد و اطلاعات حقوق یک شخص اینچنین نیست.
ب) نگهداری اطلاعات تغییرات حقوقی در یک جداول جداگانه
یک جدول ثانویه حقوق جاری کارمندان مرتبط با جدول اصلی کارمندان باید ایجاد شود. در این جدول هر رکورد آن باید دارای بازه زمانی (valid_start_time و valid_end_time) مشخصی باشد. مثلا از تاریخ X تا تاریخ Y، حقوق کارمند شماره 11 ، 2000 تومان در ساعت بوده است. از تاریخ H تا تاریخ Z اطلاعات دیگری ثبت خواهند شد. به این ترتیب با گزارشگیری از جدول لیست حقوقهای پرداخت شده، سابقه گذشته اشخاص محو نشده و هر رکورد بر اساس قرارگیری در یک بازه زمانی ثبت شده در جدول ثانویه حقوق جاری کارمندان تفسیر میشود.
در این حالت باید دقت داشت که بازههای زمانی تعریف شده، با هم تداخل نکنند و برنامه ثبت کننده اطلاعات باید این مساله را به ازای هر کارمند کنترل کند و یا با ثبت record_date، اجازه ثبت بازههای تکراری را نیز بدهد (توضیحات در قسمت بعد).
به این جدول، یک Temporal table نیز گفته میشود. نمونه دیگر آن، نگهداری قیمت یک کالا است از یک تاریخ تا تاریخی مشخص. به این ترتیب میتوان کوئری گرفت که بستنی مگنوم فروخته شده در ماه آبان سال قبل، بر مبنای قیمت آن زمان، دقیقا چقدر فروش کرده است و نه اینکه صرفا بر اساس آخرین قیمت روز این کالا گزارشگیری کنیم که در این حالت اطلاعات نهایی استخراج شده صحیح نیستند.
حال اگر به این طراحی در جدولی دیگر Transaction time یا زمان ثبت یک رکورد یا زمان ثبت یک فروش را هم اضافه کنیم، به جداول حاصل Bitemporal Tables میگویند.
مدیریت به روز رسانیها در جداول Temporal
در جداول Temporal، حذف فیزیکی اطلاعات مطلقا ممنوع است؛ چون سابقه سیستم را تخریب میکند. اگر اطلاعاتی در این جداول دیگر معتبر نیست باید تنها تاریخ پایان دوره آن به روز شوند یا یک رکورد جدید بر اساس بازهای جدید ثبت گردد.
همچنین به روز رسانیها در این جداول نیز معادل هستند با یک Insert جدید به همراه فیلد record_date و نه به روز رسانی واقعی یک رکورد قبلی (شبیه به سیستمهای حسابداری باید عمل کرد).
یک مثال:
فرض کنید حقوق کارمندی که مثال زده شد، در مهرماه به ساعتی 2400 تومان افزایش یافته است و حقوق نهایی نیز پرداخته شده است. بعد از یک ماه مشخص میشود که مدیر عامل سیستم گفته بوده است که ساعتی 2500 تومان و نه ساعتی 2400 تومان! (از این نوع مسایل در دنیای واقعی زیاد رخ میدهند!) خوب؛ اکنون چه باید کرد؟ آیا باید رفت و رکورد ساعتی 2400 تومان را به روز کرد؟ خیر. چون سابقه پرداخت واقعی صورت گرفته را تخریب میکند. به روز رسانی شما ابدا به این معنا نخواهد بود که دریافتی واقعی شخص در آن تاریخ خاص، ساعتی 2500 بوده است.
بنابراین در جداول Temporal، تنها «تغییرات افزودنی» مجاز هستند و این تغییرات همواره به عنوان آخرین رکورد جدول ثبت میشوند. به این ترتیب میتوان اصطلاحا «مابه التفاوت» حقوق پرداخت نشده را به شخص خاصی، محاسبه و پرداخت کرد (میدانیم در یک بازه زمانی خاص به او چقد حقوق دادهایم. همچنین میدانیم که این بازه در یک record_date دیگر لغو و با عددی دیگر، جایگزین شدهاست).
برای مطالعه بیشتر
Bitemporal Database Table Design - The Basics
Temporal Data Techniques in SQL
Database Design: A Point in Time Architecture
Temporal database
Temporal Patterns
راه حلی دیگر؛ استفاده از بانکهای اطلاعاتی NoSQL
بانکهای اطلاعاتی NoSQL برخلاف بانکهای اطلاعاتی رابطهای برای اعمال Read بهینه سازی میشوند و نه برای Write. در چند دهه قبل که بانکهای اطلاعاتی رابطهای پدیدار شدند، یک سخت دیسک 10 مگابایتی حدود 4000 دلار قیمت داشته است. به همین جهت مباحث نرمال سازی اطلاعات و ذخیره نکردن اطلاعات تکراری تا این حد در این نوع بانکهای اطلاعاتی مهم بوده است. اما در بانکهای اطلاعاتی NoSQL امروزی، اگر قرار است فیش حقوقی شخصی ثبت شود، میتوان کل اطلاعات جاری او را یکجا داخل یک سند ثبت کرد (از اطلاعات شخص در آن تاریخ تا اطلاعات تمام اجزای فیش حقوقی در قالب یک شیء تو در توی JSON). به همین جهت بسیار سریع هستند برای اعمال Read و گزارشگیری. همچنین این نوع سیستمها برای نگهداری نگارشهای مختلف یک سند بهینه سازی شدهاند و جزو ساختار توکار آنها است. بنابراین در این نوع سیستمها اگر نیاز است از یک سند خاصی گزارش بگیریم، دقیقا اطلاعات همان تاریخ خاص را دارا است و اگر اطلاعات پایه سیستم را به روز کنیم، از امروز به بعد در سندهای جدید ثبت خواهد شد. این نوع سیستمها رابطهای نیستند و بسیاری از مباحث نرمال سازی اطلاعات در آنها ضرورتی ندارد. قرار است یک فیش حقوقی شخص را نمایش دهیم؟ خوب، چرا تمام اطلاعات مورد نیاز او را در قالب یک شیء JSON تو در توی حاضر و آماده نداشته باشیم؟
خلاصه نکات SEO
خبرنامه Angular 2
در قسمت قبل بیان شد همراه با نصب Angular Material، تعدادی تم از قبل ساخته شده نیز نصب خواهند شد که شامل یکسری استایل با رنگهای مشخصی هستند. این تمها عبارتند از:
- indigo-pink
- deeppurple-amber
- purple-green
- pink-bluegrey
انگیولار متریال ۲ علاوه بر این، امکاناتی برای ایجاد تم سفارشی را نیز فراهم کرده است. در این بخش قصد داریم برای قالب نمونهای که قبلا ایجاد کرده بودیم یک تم سفارشی بسازیم.
مقدمه
تم در انگیولار متریال، از ترکیب چند پالت رنگی، ساخته میشود. پالتهای رنگ را در طراحی متریال ( Material Design ) در اینجا میتوانید مشاهده کنید. انگیولار متریال رنگهای مورد استفاده خود را در گروههای زیر دسته بندی کرده است.
- Primary : این پالت رنگی به صورت گسترده در
بخشهای مختلف صفحه و کامپوننتها مورد استفاده قرار میگیرد.
- Accent : این پالت رنگی برای دکمههای
شناور و همچنین المنتهای تعاملی مورد استفاده قرار میگیرد.
- Warn : این پالت رنگی برای مشخص کردن
حالتهای خطا، مورد استفاده قرار میگیرد.
- Foreground : این پالت رنگی برای متون و
آیکونها مورد استفاده قرار میگیرد.
- Background : این پالت رنگی برای المنتهای پس
زمینه مورد استفاده قرار میگیرد.
در انگیولار متریال تمامی تمها
در زمان build به صورت استاتیک تولید میشوند. این قابلیت با خارج کردن چرخه
تولید تم از چرخه راهاندازی برنامه، باعث بهبود در راهاندازی خواهد شد.
تعریف تم سفارشی
برای ساخت تم سفارشی نیاز به یک فایل Sass خواهیم داشت. پس در مسیر /src یک فایل Sass را با نام my-custom-theme.scss ایجاد میکنیم (شما میتوانید از هر نام دیگری برای فایل Sass استفاده کنید). اگر از AngularCLI برای برنامههای خود استفاده میکنید، بایستی فایل Sass ایجاد شده را به لیست استایلها در فایل angular-cli.json اضافه کنید. این کار باعث میشود AngularCLI این فایل Sass را در زمان build به css کامپایل کند.
نکته: استفاده از فایل Sass برای ساختن تم سفارشی به این معنی نیست که شما از Sass برای سایر Style های برنامه خود استفاده کنید.
"styles": [ "styles.css", "my-custom-theme.scss" ],
اگر از AngularCLI استفاده نمیکنید، شما نیاز به ابزاری برای کامپایل فایل Sass به css خواهید داشت. ابزارهای بسیاری در این زمینه وجود دارند از جمله: gulp-sass و grunt-sass . ولی سادهترین ابزار برای این کار node-sass میباشد. کافی است بعد از نصب، دستور زیر را اجرا کنید تا فایل sass به css کامپایل شود. فایل css تولید شده را مستقیما در صفحه index.html خود میتوانید استفاده کنید.
node-sass src/my-custom-theme.scss dist/my-custom-theme.css
در فایل تم ایجاد شده ( my-custom-theme.scss ) ابتدا بایستی فایل Sass اصلی انگیولار متریال را وارد کنید.
@import '~@angular/material/theming';
در قدم بعدی mixin تعریف شده با نام mat-core را در فایل Sass انگیولار متریال، وارد میکنیم. این mixin شامل تمامی Styleهای مشترکی است که توسط کامپوننتهای مختلف استفاده میشود.
@include mat-core();
نکته: مطمئن شوید فقط یک بار این mixin را در سرتاسر برنامه خود وارد کرده باشید. در غیر این صورت، فایل css تولید شده شامل یکسری Style تکراری خواهد بود و این باعث بزرگ و حجیم شدن فایل css نهایی خواهد شد.
تا اینجا فایل تم ایجاد شده اینگونه خواهد بود:
@import '~@angular/material/theming'; @include mat-core();
حالا نوبت تعریف تم سفارشی است. ولی قبل از آن باید با سیستم رنگها در طراحی
متریال ( Material
Design ) آشنایی داشته
باشیم. در طراحی متریال ۱۹ پالت رنگی با نامهای مختلف وجود دارند. برای ۱۶ پالت رنگی، ۱۴ طیف رنگی و برای ۳ پالت رنگی دیگر، ۱۰ طیف رنگی در نظر گرفته شده است. هر کدام از این طیفهای رنگی، دارای یک مقدار عددی است. یعنی یک رنگ در سیستم طراحی متریال متشکل از یک نام رنگ و یک شماره طیف رنگ است که مقدار پیش فرض طیف رنگ، عدد ۵۰۰ میباشد.
حالا با استفاده از تابع mat-palette تعریف شده در فایل Sass انگیولار متریال، سه متغیر را برای رنگهای Primary ، Accent و Warn در فایل my-custom-theme.scss ، به شکل زیر تعریف میکنیم.
$my-app-primary: mat-palette($mat-indigo); $my-app-accent: mat-palette($mat-pink, 500, A100, A400); $my-app-warn: mat-palette($mat-deep-orange);
تابع mat-palette در فایل Sass اصلی انگیولار متریال، به شکل زیر تعریف شده است.
@function mat-palette($base-palette, $default: 500, $lighter: 100, $darker: 700)
این
تابع یک پارامتر اجباری دارد و بقیه پارامترها اختیاری هستند.
- base-palette $: نام رنگ را دریافت میکند. این پارامتر
اجباری است و باید مشخص شود.
- default$: با این
پارامتر، طیف پیشفرض رنگ انتخاب شده را مشخص میکنیم. این پارامتر اختیاری است و
مقدار پیش فرض آن 500 است.
- lighter$: با این پارامتر، طیف روشن رنگ انتخاب شده را مشخص میکنیم. این
پارامتر اختیاری است و مقدار پیش فرض آن 100 است.
- darker$: با این پارامتر، طیف تیره رنگ انتخاب شده را مشخص میکنیم. این
پارامتر اختیاری است و مقدار پیش فرض آن 700 است.
در قدم آخر با استفاده از تابع mat-light-theme یا mat-dark-theme، رنگهای تعریف شده در مرحله قبل را ترکیب کرده و نتیجه را به عنوان ورودی به mixin به نام angular-material-theme ارسال و بارگذاری میکنیم.
تابع mat-light-theme و mat-dark-theme سه پارامتر را دریافت میکند. پارارمتر اول پالت رنگ ایجاد شده توسط تابع mat-palette برای گروه Primary ، پارامتر دوم پالت رنگ ایجاد شده برای گروه Accent و پارامتر سوم پالت رنگ ایجاد شده برای گروه Warn را دریافت میکند. دو پارامتر اول اجباری و پارامتر سوم اختیاری با مقدار پیش فرض mat-palette($mat-red) میباشد.
شکل
کلی فایل Sass در نهایت به شکل زیر خواهد
بود.
@import '~@angular/material/theming'; @include mat-core(); $my-app-primary: mat-palette($mat-teal); $my-app-accent: mat-palette($mat-amber, 500, A100, A400); $my-app-warn: mat-palette($mat-deep-orange); $my-app-theme: mat-light-theme($my-app-primary, $my-app-accent, $my-app-warn); @include angular-material-theme($my-app-theme);
برای استفاده از پالت رنگهای ایجاد شده، از خصوصیت color در المنتهای انگولار متریال استفاده میکنیم. برای نمونه بعد از تغییر فایل Sass به شکل بالا و حذف لینک تم از پیش ساخته شده که در پست قبلی به Style.cs اضافه کرده بودیم، میتوانیم کار خود را به صورت زیر آزمایش کنیم. در فایل app.component.html در تگ main کدهای زیر را اضافه کنید.
<md-card> <button md-raised-button color="primary"> Primary </button> <button md-raised-button color="accent"> Accent </button> <button md-raised-button color="warn"> Warning </button> </md-card>
خروجی زیر را مشاهده خواهید کرد.
همچنین میتوانید به جای استفاده از تابع mat-light-theme از تابع mat-dark-theme استفاده کنید. دراین صورت خروجی زیر را خواهید دید.
در بخش بعدی نحوه ساخت چند تم دیگر را در کنار تم اصلی، ساخت تم به ازای هر کامپوننت و نحوه تعویض تم از طریق کد را دنبال خواهیم کرد.
کدهای این قسمت را از اینجا دریافت کنید: ساخت-تم-سفارشی-در-انگولار-متریال-۲---بخش-اول.rar