نمایش پیامها و اخطارهای یک برنامهی Angular توسط ng2-toasty
در مطلب «ایجاد Drop Down Listهای آبشاری در Angular» در قسمت دریافت اطلاعات drop down دوم از سرور، اگر کاربر مجددا گروه را بر روی حالت «لطفا گروهی را انتخاب کنید ...» قرار دهد، مقدار categoryId به undefined تغییر میکند:
fetchProducts(categoryId?: number) { console.log(categoryId); this.products = []; if (categoryId === undefined || categoryId.toString() === "undefined") { return; }
پیشنیازهای کار با کامپوننت Angular2 Toasty توسط یک برنامهی Angular CLI
برای کار با کامپوننت Angular2 Toasty، ابتدا از طریق خط فرمان به پوشهی ریشهی برنامه وارد شده و سپس دستور ذیل را صادر میکنیم:
> npm install ng2-toasty --save
یک نکته: اگر در حین اجرای این دستور به خطای ذیل برخوردید:
npm ERR! Error: EPERM: operation not permitted, rename
پس از آن نیاز است یکی از شیوهنامههایی را که در تصویر فوق ملاحظه میکنید، در فایل angular-cli.json. مشخص کنیم:
"styles": [ "../node_modules/bootstrap/dist/css/bootstrap.min.css", "../node_modules/ng2-toasty/bundles/style-bootstrap.css", "styles.css" ],
سپس باید به فایل src\app\app.module.ts مراجعه کرد و ماژول این کامپوننت را معرفی نمود:
import { ToastyModule } from "ng2-toasty"; @NgModule({ imports: [ BrowserModule, ToastyModule.forRoot(),
همچنین در همین قسمت، به فایل قالب src\app\app.component.html مراجعه کرده و selector tag این کامپوننت را در ابتدای آن تعریف میکنیم:
<ng2-toasty [position]="'top-right'"></ng2-toasty>
نمایش یک پیام خطا توسط ToastyService
اکنون که کار برپایی کامپوننت Angular2 Toasty به پایان رسید، کار کردن با آن به سادگی تزریق سرویس آن به سازندهی یک کامپوننت و فراخوانی متدهای info، success ، wait ، error و warning آن است:
import { ToastyService, ToastOptions } from "ng2-toasty"; export class ProductGroupComponent implements OnInit { constructor( private productItemsService: ProductItemsService, private toastyService: ToastyService) { } fetchProducts(categoryId?: number) { console.log(categoryId); this.products = []; if (categoryId === undefined || categoryId.toString() === "undefined") { this.toastyService.error(<ToastOptions>{ title: "Error!", msg: "Please select a category.", theme: "bootstrap", showClose: true, timeout: 5000 }); return; }
- سپس ToastyService به سازندهی کلاس کامپوننت مدنظر تزریق شدهاست تا بتوان از امکانات آن استفاده کرد.
- در ادامه، فراخوانی متد this.toastyService.error سبب نمایش اخطار قرمز رنگی میشود که تصویر آنرا در ابتدای مطلب جاری مشاهده کردید.
- علت ذکر <ToastOptions> در اینجا این است که وجود آن سبب خواهد شد تا intellisense در VSCode فعال شود و پس از آن بتوان تمام گزینههای این متد و تنظیمات را بدون مراجعهی به مستندات آن از طریق intellisense یافت و درج کرد:
مدیریت سراسری خطاهای مدیریت نشده، در یک برنامهی Angular
در برنامههای Angular از این دست کدها بسیار مشاهده میشوند:
this.productItemsService.getCategories().subscribe( data => { this.categories = data; }, err => console.log("get error: ", err) );
به همین منظور کلاس جدیدی را به صورت ذیل در پوشهی src\app اضافه میکنیم:
> ng g cl app.error-handler
installing class create src\app\app.error-handler.ts
import { ErrorHandler } from "@angular/core"; export class AppErrorHandler implements ErrorHandler { handleError(error: any): void { console.log("Error:", error); } }
اکنون نیاز است این ErrorHandler سفارشی را بجای نمونهی اصلی به برنامه معرفی کنیم. برای این منظور به فایل src\app\app.module.ts مراجعه کرده و تغییرات ذیل را اعمال میکنیم:
import { NgModule, ErrorHandler } from "@angular/core"; import { AppErrorHandler } from "./app.error-handler"; @NgModule({ providers: [ { provide: ErrorHandler, useClass: AppErrorHandler } ]
اکنون برای آزمایش آن، در کدهای سمت سرور مطلب «ایجاد Drop Down Listهای آبشاری در Angular»، یک استثنای عمدی را قرار میدهیم:
[HttpGet("[action]/{categoryId:int}")] public async Task<IActionResult> GetProducts(int categoryId) { throw new Exception();
برای اینکه AppErrorHandler، مورد استفاده قرار گیرد، قسمت err دریافت لیست محصولات را نیز حذف میکنیم (تا تبدیل به یک استثنای مدیریت نشده شود):
this.productItemsService.getProducts(categoryId).subscribe( data => { this.products = data; this.isLoadingProducts = false; }// , // err => { // console.log("get error: ", err); // this.isLoadingProducts = false; // } );
افزودن ToastyService به AppErrorHandler
در ادامه میخواهیم بجای console.log از ToastyService برای نمایش خطاهای مدیریت نشدهی برنامه در کلاس AppErrorHandler استفاده کنیم:
import { ToastyService, ToastOptions } from "ng2-toasty"; import { ErrorHandler } from "@angular/core"; export class AppErrorHandler implements ErrorHandler { constructor(private toastyService: ToastyService) { } handleError(error: any): void { // console.log("Error:", error); this.toastyService.error(<ToastOptions>{ title: "Error!", msg: "Fatal error!", theme: "bootstrap", showClose: true, timeout: 5000 }); } }
مشکل اول! اکنون اگر برنامه را اجرا کنیم، در کنسول developer tools چنین خطایی ظاهر میشود:
Uncaught Error: Can't resolve all parameters for AppErrorHandler: (?).
import { ErrorHandler, Inject } from "@angular/core"; export class AppErrorHandler implements ErrorHandler { constructor( @Inject(ToastyService) private toastyService: ToastyService ) { }
مشکل دوم! اینبار برنامه را اجرا کنید. سپس گروهی را انتخاب نمائید. مشاهده میکنید که خطایی نمایش داده نشد؛ هرچند در کنسول developer tools میتوان اثری از آن را مشاهده کرد. مجددا گروه دیگری را انتخاب کنید، در این بار دوم است که خطای ارائه شدهی توسط this.toastyService.error ظاهر میشود. توضیح آن نیاز به بررسی مفهومی به نام Zones در Angular دارد.
مفهوم Zones در Angular
زمانیکه متد this.toastyService.error در یک کامپوننت برنامه مورد استفاده قرار گرفت، به خوبی کار میکرد و در همان بار اول فراخوانی، پیام را نمایش میداد. اما با انتقال آن به کلاسAppErrorHandler ، این قابلیت از کار افتاد. علت اینجا است که زمینهی اجرایی این قطعه کد، اکنون خارج از Zone یا ناحیهی Angular است و به همین دلیل متوجه تغییرات آن نمیشود. Zone زمینهی اجرایی اعمال async است و اگر به فایل package.json یک برنامهی Angular دقت کنید، بستهی zone.js، یکی از وابستگیهای همراه آن است.
تغییرات حالت برنامه، توسط یکی از اعمال ذیل رخ میدهند:
الف) بروز رخدادهایی مانند کلیک، ورود اطلاعات و یا ارسال فرم
ب) اعمال Ajax ایی
ج) استفاده از Timers مانند استفاده از setTimeout و setInterval
هر سه مورد یاد شده از نوع async بوده و زمانیکه رخ میدهند، حالت برنامه را تغییر خواهند داد. Angular نیز تنها به این موارد علاقمند بوده و به آنها در جهت به روز رسانی رابط کاربری برنامه واکنش نشان میدهد.
برای مثال this.toastyService.error دارای خاصیتی است به نام timeout: 5000 که در آن، مورد «ج» فوق رخ میدهد؛ یعنی یک Timer پس از 5 ثانیه سبب بسته شدن آن خواهد شد. به همین جهت است که اگر پیش از پایان این 5 ثانیه مجددا درخواست واکشی لیست محصولات یک گروه را بدهیم، خطای مربوطه مشاهده میشود. چون Angular زمینهی اجرایی لازم را فراهم کرده (یا همان Zone در اینجا) و مجبور به واکنش به عملیات async از نوع Timer است.
برای دسترسی به امکانات کتابخانهی zone.js، میتوان از طریق تزریق سرویس آن به نام NgZone به سازندهی کلاس شروع کرد:
import { ToastyService, ToastOptions } from "ng2-toasty"; import { ErrorHandler, Inject, NgZone } from "@angular/core"; import { LocationStrategy, PathLocationStrategy } from "@angular/common"; export class AppErrorHandler implements ErrorHandler { constructor( @Inject(NgZone) private ngZone: NgZone, @Inject(ToastyService) private toastyService: ToastyService, @Inject(LocationStrategy) private locationProvider: LocationStrategy ) { } handleError(error: any): void { // console.log("Error:", error); const url = this.locationProvider instanceof PathLocationStrategy ? this.locationProvider.path() : ""; const message = error.message ? error.message : error.toString(); this.ngZone.run(() => { this.toastyService.error(<ToastOptions>{ title: "Error!", msg: `URL:${url} \n ERROR:${message}`, theme: "bootstrap", showClose: true, timeout: 5000 }); }); // IMPORTANT: Rethrow the error otherwise it gets swallowed // throw error; } }
چند نکته
1- اگر میخواهید علاوه بر رخدادگردانی سراسری خطاها، این خطاها را به محل اصلی آنها نیز انتشار دهید، نیاز است سطر throw error را در انتهای متد handleError نیز ذکر کنید. در غیر اینصورت، کار در همینجا به پایان خواهد رسید و این خطاها دیگر منتشر نمیشوند.
2- روش دریافت URL جاری صفحه را نیز در اینجا مشاهده میکنید. این اطلاعات میتوانند جهت ارسال به سرور برای ثبت و بررسیهای بعدی مفید باشند.
3- مقدار new Error().stack معادل stack trace جاری است و تقریبا در تمام مرورگرهای جدید پشتیبانی میشود.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-07.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس به ریشهی پروژه وارد شده و دو پنجرهی کنسول مجزا را باز کنید. در اولی دستورات
>npm install >ng build --watch
>dotnet restore >dotnet watch run
در این مجموعه مقالات، به بررسی و آموزش برنامه نویسی شیء گرا در جاوا اسکریپت میپردازیم. در طول آموزش، فرض را بر این قرار دادیم که شما به عنوان خوانندهی این مقاله، با مبانی جاوا اسکریپت آشنا میباشید و حداقل چند قطعه کد مفید را با جاوا اسکریپت نوشتهاید. همچنین کمی هم با مباحث شیء گرایی آشنا میباشید.
روال آموزش در این مجموعه به گونه است که در ابتدا به معرفی مباحث پیش نیاز جهت ورود به دنیای شیء گرایی در جاوا اسکریپت، پرداخته خواهد شد. سپس مباحث شیء گرایی را آغاز خواهیم نمود و تمامی نکات ریز و درشت آن را بررسی خواهیم کرد. پس از پایان این مقالات قادر خواهید بود تا تمامی کتابخانهها و Frameworkهای جاوا اسکریپتی را مطالعه نموده و به راحتی تکنیکهای کد نویسی آن را درک کنید. همچنین خود شما نیز برای نوشتن یک کتابخانه یا Framework جاوا اسکریپتی استاندارد و حرفهای، دست به کار شوید و یک کتابخانه سودمند را ارائه نمایید.
با چند مثال ساده و بصورت تقریبا مبتدی مسائل پایه را تشریح نموده و آرام آرام مباحث حرفه ای را آغاز خواهیم کرد. قرار است در پایان این آموزشها به بررسی الگوهای موجود در جاوا اسکریپت نیز بپردازم که مجموعه مقالات مربوط به الگوها را در قالب مجموعه مقالاتی مجزا ارائه مینمایم.
در تهیهی این مجموعه مقالات از منابع زیر استفاده شده است:
1. Pro JavaScript Techniques (John Resig ، خالق JQuery – فصول 2 و 3)
2. Professional JavaScript for Web Developers (Third Edition) (Nicholas C. Zakas – فصول 3، 4، 5، 6 و 7)
3. Object-Oriented JavaScript (Stoyan Stefanov – فصول 3، 4، 5، 6 و 8)
4. و تجربهی ناچیز اینجانب
توابع (Functions)
همیشه در برنامه، مجموعه دستوراتی وجود دارند که عمل خاصی را انجام میدهند و به دفعات نیز مورد استفاده قرار میگیرند. جهت جلوگیری از تکرار این دستورات، افزایش خوانایی و کاهش پیچیدگی برنامه، این دستورات را در بستهای به نام تابع قرار میدهیم تا در زمان نیاز، آن را فراخوانی یا اجرا نماییم. جهت تعریف تابع در جاوا اسکریپت به صورت زیر عمل مینماییم:
function <functionName> ([arg0, arg1, …, argN]) { <statements> }
به عنوان مثال:
function sayHello(name, message) { alert("Hello " + name + ", " + message); }
این تابع شامل دو آرگومان ورودی است که میتواند به صورت زیر فراخوانی شود:
sayHello("Meysam", "welcome to site");
خروجی این تابع “Hello Meysam, welcome to site” می باشد که به صورت یک پنجرهی پیغام، نمایش مییابد. تابع فوق هیچ مقداری را به عنوان خروجی به برنامهی اصلی یا محل فراخوانی خود بر نمیگرداند. اگر بخواهیم توسط تابع مقداری را برگردانیم میتوانیم از دستور return برای این منظور استفاده نماییم. به مثال زیر توجه کنید:
function sum(num1, num2) { return num1 + num2; }
تابع sum دو عدد را به عنوان آرگومان ورودی دریافت نموده و حاصل جمع آنها را توسط دستور return به عنوان خروجی بر میگرداند. تابع فوق را میتوان به صورت زیر فراخوانی نمود:
alert(sum(10, 20));
خروجی :
30
اینک به بررسی چند نکته در مورد دستور return میپردازیم. دستور return فقط میتواند یک مقدار را برگرداند و نمیتوان، چند مقدار را مقابل این دستور نوشت. همچنین روال اجرای تابع با رسیدن به دستور return خاتمه مییابد و دستورات بعد از آن اجرا نخواهند شد. به مثال زیر توجه کنید:
function sum(num1, num2) { return num1 + num2; alert("Hello"); }
در مثال فوق دستور alert به هیچ عنوان اجرا نخواهد شد؛ زیرا تابع با رسیدن به دستور return خاتمه مییابد. یک تابع میتواند شامل بیش از یک دستور return باشد.
function diff(num1,num2) { if (num1 > num2) return num1 - num2; else return num2 - num1; }
تابع فوق اختلاف دو عدد را بدست میآورد و اگر عدد اول بزرگتر باشد، عدد دوم را از عدد اول تفریق میکند؛ در غیر اینصورت عدد اول را از عدد دوم تفریق میکند و به عنوان خروجی بر میگرداند. نکتهی دیگری که لازم است بدانید این است که دستور return می تواند هیچ مقداری را بر نگرداند و به تنهایی بکار گرفته شود. در این صورت دقیقا بعد از دستور return سمی کالن (;) قرار میدهیم.
function checkNumber(num) { if (isNaN(num)) { alert("Not a number"); return; } alert(num+" is a number"); }
تابع فوق یک ورودی را دریافت مینماید و در صورتی که آرگومان ورودی عدد نباشد پیغام “Not a number” را نمایش میدهد و از تابع خارج میشود. در صورتی که آرگومان ورودی یک عدد باشد، پیغام دوم را نمایش میدهد.
توجه:
یک تابع بهتر است همیشه یک مقداری را به عنوان خروجی برگرداند و یا کلا هیچ مقداری را بر نگرداند. نوشتن توابعی که در برخی شرایط مقداری را به عنوان خروجی بر میگردانند و در برخی شرایط مقداری را برنمی گردانند موجب ایجاد پیچیدگی در اشکال زدایی میگردند. نوشتن تابعی به صورت زیر کمی گیج کننده و نامتعارف میباشد:
function sum(num1, num2) { if (isNaN(num1) || isNaN(num2)) return; // بهتر است حداقل مقدار 0 برگردانده شود return num1 + num2; }
کار با آرگومان ها
رفتار آرگومانها در جاوا اسکریپت نسبت به سایر زبانهای برنامه نویسی کاملا متفاوت میباشد. در جاوا اسکریپت تعداد و نوع آرگومانهای ارسالی بررسی نمیشوند و خطایی هم رخ نخواهد داد. به عنوان مثال اگر تابعی با 3 آرگومان ورودی دارید، میتوانید با 0 تا 3 آرگومان ورودی، آن تابع را فراخوانی نمایید. زیرا آرگومانها در سیستم داخلی جاوا اسکریپت به صورت یک آرایه ارسال میگردند و تابع توجه نمیکند که کدام آرگومانها به این آرایه ارسال شدهاند.
با توجه به قابلیتی که در مورد آرگومانها ذکر شد و به دلیل عدم مدیریت نوع و تعداد آرگومانهای ارسالی، مطمئنا جهت جلوگیری از بروز خطا در توابع، باید تعداد و نوع آرگومانهای ارسالی بررسی و مدیریت شوند. همچنین در هر تابع، آرایهای به نام arguments به صورت توکار تعبیه شده است که مدیریت آرگومانها را تسهیل میبخشد. به مثال زیر توجه کنید:
function sayHello() { alert("Hello " + arguments[0] + ", " + arguments[1]); } sayHello("Meysam", "welcome to site");
خروجی :
"Hello Meysam, welcome to site"
تابع فوق هیچ آرگومان ورودی ندارد ولی با دو آرگومان ورودی فراخوانی شده است. در داخل تابع توسط آرایه arguments به آرگومانهای ارسالی دسترسی پیدا کردیم. حال به مثال زیر توجه کنید:
function sum() { var s = 0; for (var i = 0; i < arguments.length; i++) s += arguments[i]; return s; } alert(sum()); alert(sum(10, 20, 30, 40, 50)); alert(sum(10));
خروجی :
0
150
10
در تابع فوق هیچ آرگومان ورودی وجود ندارد ولی این تابع را با 0، 5 و 1 آرگومان ورودی فراخوانی نمودیم. این تابع مجموع چند عدد را محاسبه و بر میگرداند و میتواند به تعداد نامحدودی عدد دریافت نماید. البته بهتر است نوع آرگومانهای ارسالی نیز بررسی شوند تا خطایی در محاسبات رخ ندهد. همچنین بجای حلقه for از حلقه for…in استفاده خواهم کرد.
function sum() { var s = 0; for (var i in arguments) { if (isNaN(arguments[i])) continue; s += arguments[i]; } return s; } alert(sum()); alert(sum(10, 20, "a", 40, 50)); alert(sum(10));
خروجی :
0
120
10
اگر دقت کرده باشید در فراخوانی دوم، رشته “a” به تابع ارسال شده است و چون آرگومانهای نامعتبر را مدیریت نمودهایم، خطایی در خروجی رخ نمیدهد. به مثال زیر نیز توجه نمایید:
function sum(a,b,c) { return a + b + c; } alert(sum(10, 20, 30)); alert(sum(10, 20)); alert(sum());
خروجی :
60
NaN
NaN
تابع فوق دارای 3 آرگومان ورودی میباشد؛ ولی ما این تابع را با 2 و 0 آرگومان ورودی فراخوانی نمودیم که خروجی نامناسبی را تولید نموده است. برای رفع این مشکل و معتبر سازی آرگومانهای ارسالی میتوانیم به صورت زیر عمل نماییم:
function sum(a, b, c) { if (isNaN(a)) a = 0; if (isNaN(b)) b = 0; if (isNaN(c)) c = 0; return a + b + c; } alert(sum(10, 20, 30)); alert(sum(10, 20)); alert(sum());
خروجی :
60
30
0
در تابع، قبل از انجام عمل محاسبه، بررسی کردیم که آرگومانهای ارسالی مقدار دهی شده باشند. در صورت عدم مقداردهی و یا مقداردهی نامناسب، آرگومان ارسالی را با صفر مقداردهی مینماید. اگر آرگومان مورد نظر به تابع ارسال نشود، مقدار پیش فرض آن undefined میباشد.
با توجه به مسائل مطرح شده در مورد توابع، این روش استفاده و کاربرد توابع، جزء معایب توابع در جاوا اسکریپت محسوب نمیشود. این تکنیک استفاده از توابع، موجب افزایش انعطاف پذیری توابع و آزادی عمل برنامه نویس میگردد که لذت بیشتری را به برنامه نویسی میدهد.
توجه:
به آرگومانهایی که در تابع دارای نام میباشند و یا به عبارتی، آرگومانهایی که نام آنها در تابع ذکر میشود، Named Arguments یا آرگومانهای نامی (اسمی و یا نامدار) میگویند. مثل آرگومان های a ، b وc در تابع sum .
عدم پشتیبانی از سربارگذاری یا Overloading
در زبانهای برنامه نویسی شیء گرا، امکان تعریف توابع هم نام وجود دارد؛ به شرطی که امضای این توابع با هم متفاوت باشند. منظور از امضاء، تعداد و نوع آرگومانهای ورودی میباشد. از آنجاییکه در سیستم داخلی جاوا اسکریپت، آرگومانها بصورت یک آرایه ارسال میشوند، بنابراین امضاء برای توابع مفهومی ندارد؛ پس نمیتوانیم توابع هم نام یا overloading داشته باشیم.
اگر دو تابع هم نام داشته باشیم، تابعی که دیرتر تعریف میشود، جایگزین تابع قبلی میگردد. به مثال زیر توجه کنید:
function calc(num1,num2) { return num1 + num2; } function calc(num1,num2) { return num1 - num2; } alert(calc(200,100));
خروجی :
100
همانطور که در مثال فوق مشاهده مینمایید، تابع دوم فراخوانی شدهاست و حاصل تفریق به عنوان خروجی نمایش یافته است.
دلیل خطا این است که از نسخه 1.2 به بعد در Angular سیستم مسیر یابی به این شکل امکان پذیر نیست و بخش مسیریابی به یک فایل دیگر به نام angular-route.js منتقل شده است. در نتیجه اگر به سبک نسخههای قبلی Angular از سیستم مسیریابی استفاده نمایید با خطا مواجه خواهید شد و خطای مورد نظر هم مربوط به عدم توانایی در تزریق وابستگی routeProvider$ به ماژول مورد نظر است. حال راه حل چیست؟
کافیست در هنگام تعریف ماژول، ngRoute را به عنوان وابستگی ماژول تعیین نمایید. و از طرفی فایل اسکریپتی angular-route.js را بعد از angular.js فراخوانی کنید.
بررسی مثال:
کدهای زیر مربوط به مثالهای پست قبلی میباشد که شرح کامل آن در این پست است:
var myFirstRoute = angular.module('myFirstRoute', []); myFirstRoute.config(['$routeProvider', function($routeProvider) { $routeProvider. when('/pageOne', { templateUrl: 'templates/page_one.html', controller: 'ShowPage1Controller' }). when('/pageTwo', { templateUrl: 'templates/page_two.html', controller: 'ShowPage2Controller' }). otherwise({ redirectTo: '/pageOne' }); }]); myFirstRoute.controller('ShowPage1Controller', function($scope) { $scope.message = 'Content of page-one.html'; }); myFirstRoute.controller('ShowPage2Controller', function($scope) { $scope.message = 'Content of page-two.html'; });
var myFirstRoute = angular.module('myFirstRoute',['ngRoute']); myFirstRoute.config(['$routeProvider', function($routeProvider) { $routeProvider. when('/pageOne', { templateUrl: 'templates/page_one.html', controller: 'ShowPage1Controller' }). when('/pageTwo', { templateUrl: 'templates/page_two.html', controller: 'ShowPage2Controller' }). otherwise({ redirectTo: '/pageOne' }); }]);
<body ng-app="app"> <div> <div> <div> <ul> <li><a href="#pageOne"> Show page one </a></li> <li><a href="#pageTwo"> Show page two </a></li> </ul> </div> <div> <div ng-view></div> </div> </div> </div> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script> <script src="angular-route.js"></script> <script src="app.js"></script> </body>
کتابخانه angucomplete-alt
وبینار بهینه سازی گیت توسط کلاب
در تاریخ نه سپتامبر، شرکت کلاب در نظر دارد در مورد بهینه سازی سیستم کنترل نسخه گیت وبیناری را برگزار کند.
همچنین میتوانید زمان وبینار را که در دو ساعت 6:30 به وقت تهران Eastern و 9:30 شب به وقت تهران Pacific برگزار خواهد شد را انتخاب کنید.
پیاده سازی UnitOfWork به وسیله MEF
من الان یک ویژگی StringLength به طول 30 رو روی خاصیت Title کلاس Category مقاله جاری اضافه کردم. با همین کدهای فوق، این فیلد با طول 30 الان در دیتابیس قابل مشاهده است. آغاز دیتابیس اصلا کاری به MEF نداره.
این هم فایل مثالی که در آن ویژگی طول رشته اعمال شده برای آزمایش:
ساعت کاری محل کار شما چگونه است؟
همه روزه 7:30 تا 14:30
پر کردن ساعت کاری مشخص ولی با ساعت کاری دلخواه
شنبه تا چهارشنبه از 8 تا 18