اشتراکها
نظرات مطالب
Url Routing در ASP.Net WebForms
سلام . معمولا در code behind صفحات داخلی ID رو میگیرند ، نه در مستر پیج و اینکه احتمالان اشتباه میکنید. Page یک متد توکار هستش و همیشه وجود داره . اگر به null برمیخورید احتمالان به خاطر استفاده از .ToString هستش . اگه مقداری که وجود نداره رو ازش .ToString() بگیریم به ارور "object reference not set to an instance of an object" بر میخوریم . در این آموزش نمونه برنامههای زیادی وجود داره ، ازشون کمک بگیرید.
نظرات مطالب
چک لیست تهیه یک برنامه ASP.NET MVC
- بله. کار اصولی انجام دادن نیاز به کدنویسی بیشتری داره. اما در دراز مدت نگهداری سادهتری خواهد داشت چون حد و مرزها مشخص است و تغییرات سیستم جزئیتر خواهند بود با به هم ریختن حداقل قسمتهای دیگر که به آن گره نخوردهاند.
- ViewModel تغذیه کننده لایه UI است و نه اینکه با آن یکی باشد. اگر علاقمند هستید که وابستگی به UI رو بدونید مراجعه کنید به Web formها و جائیکه ارجاع مستقیمی از یک TextBox داخل کدهای برنامه (code behind) است. به این وابستگی میگن و نه حالت دیگری.
- در مورد محل قرارگیری Models و ViewModels در ابتدای متن فوق توضیح داده شده.
- ViewModel تغذیه کننده لایه UI است و نه اینکه با آن یکی باشد. اگر علاقمند هستید که وابستگی به UI رو بدونید مراجعه کنید به Web formها و جائیکه ارجاع مستقیمی از یک TextBox داخل کدهای برنامه (code behind) است. به این وابستگی میگن و نه حالت دیگری.
- در مورد محل قرارگیری Models و ViewModels در ابتدای متن فوق توضیح داده شده.
مطالب دورهها
کار با فرمها در بوت استرپ 3
در مطلب «استفاده از Twitter Bootstrap در کارهای روزمره طراحی وب» به نکات مرتبط با کار با فرمها در بوت استرپ 2 پرداخته شد. همچنین مطالبی مانند «ویرایش قالب پیش فرض Add View در ASP.NET MVC برای سازگار سازی آن با Twitter bootstrap» برای خودکار سازی تولید فرمهای بوت استرپ 2 در برنامههای ASP.NET MVC و نکات «اعمال کلاسهای ویژه اعتبارسنجی Twitter bootstrap به فرمهای ASP.NET MVC» نیز بررسی شدند. در بوت استرپ 3، بسیاری از این نکات تغییر کردهاند و نیاز است با نحوه ارتقاء فرمهای بوت استرپ 2 به 3 و کلا نحوه کار با فرمها در بوت استرپ 3 بیشتر آشنا شد.
نحوه ارتقاء فرمهای بوت استرپ 2 به 3
تمام این تغییرات در بوت استرپ 3، جهت پیاده سازی ایده mobile-first بودن آن است. برای مثال فرمهای افقی بوت استرپ 3 با کوچک شدن اندازه صفحه، به صورت خودکار واکنش نشان داده و تبدیل به فرمهای معمولی که اجزای آن به صورت یک stack عمودی قرار گرفتهاند، میشوند.
اکنون اگر فرمهایی را دارید که در برنامههای پیشین خود از بوت استرپ 2 استفاده کردهاند، نیاز است تغییرات ذیل را به آنها اعمال کنید تا با سیستم جدید بوت استرپ 3 سازگار شوند:
- کلاس control-group را به کلاس form-group تبدیل کنید.
- form-search حذف شده است. آنرا با form-inline جایگزین کنید.
- دیگر نیازی به استفاده از input-block-level نیست؛ از آنجائیکه به صورت پیش فرض کلیه inputها دارای عرض 100 درصد هستند.
- help-inline حذف شده است. آنرا با help-block جایگزین کنید.
- عرض ستونها را در فرمهای افقی، برچسبها و کنترلها مشخص کنید.
- کلاس controls حذف شده است.
- کلاس form-control را به inputها و selectها اضافه کنید.
- checkboxها و radioها باید در یک div محصور شوند.
- کلاسهای radio.inline و checkbox.inline باید با inline جایگزین شوند.
- کلاسهای input-small به input-sm و input-large به input-lg تبدیل شدهاند.
- کلاسهای input-prepend با input-group و input-append با input-group جایگزین شدهاند.
- کلاس alert-error حذف شدهاست. بجای آن میشود از alert-warning استفاده کرد.
- کلاس alert-block را با alert جایگزین کنید.
ایجاد اولین فرم افقی با بوت استرپ 3
فرض کنید که قصد داریم یک چنین فرم افقی را توسط امکانات بوت استرپ 3 ایجاد کنیم:
همانطور که ملاحظه میکنید، با کوچک شدن اندازه صفحه، این فرم نیز تغییر شکل میدهد:
کدهای کامل این فرم را در ادامه ملاحظه میکنید:
توضیحات:
- باید درنظر داشت که اگر هیچگونه فرمتی را به فرمهای بوت استرپ 3 اعمال نکنیم، به صورت پیش فرض فرمت دهی شده و تبدیل به فرمهای عمودی شکیلی میشوند که شاید از دیدگاه خیلیها مناسب بوده و نیاز به تغییرات خاصی نداشته باشند.
- برای تبدیل این فرم عمودی پیش فرض، به فرمهای افقی دو ستونه، نیاز است یک سری کلاس بوت استرپ 3 را به المانهای آن اضافه کنیم. برای این منظور ابتدا کلاس form-horizontal را به تگ فرم اضافه میکنیم.
- هر سطر فرم، در یک المان section با کلاس row قرار خواهد گرفت.
- اکنون هر سطر، از یک برچسب به همراه یک یا چند المان تشکیل خواهد شد. در هر سطر، کنترلها در یک div با کلاس controls قرار میگیرند.
- برای اینکه برچسبهای هر ردیف با کنترلها و المانهای آن ردیف، تراز شوند، تنها کافی است به آنها کلاس control-label را اضافه کنیم.
در ادامه تمام این مراحل را باید به ازای هر سطر فرم تکرار کنیم.
- زمانیکه به radio buttons یا check boxes میرسیم، باید به چند نکته دقت داشت:
الف) حین کار با radio buttons، علاوه بر برچسب آن سطر که با label مشخص میشود، هر radio button نیز باید داخل یک label با کلاس radio محصور شود.
ب) تمام radio buttons یک سطر نیز باید در یک div ایی با کلاس controls محصور شوند.
این نکته در مورد check boxes نیز صادق است.
با رعایت همین چند نکته ساده میتوان به یک طراحی دو ستونی خودکار واکنشگرا رسید.
اصلاح قالب ایجاد فرمهای خودکار ASP.NET MVC بر اساس بوت استرپ 3
مطلب «ویرایش قالب پیش فرض Add View در ASP.NET MVC برای سازگار سازی آن با Twitter bootstrap» جهت بوت استرپ 2 تهیه شده بود. فایل نهایی ویرایش شده آنرا با توجه به توضیحات مطلب جاری برای بوت استرپ 3 از پیوست انتهای بحث دریافت کنید و برای استفاده از آن فقط کافی است آنرا در مسیر CodeTemplates\AddView\CSHTML\CreateBootstrap3Form.tt ریشه پروژه جاری خود کپی و به پروژه اضافه کنید تا در صفحه دیالوگ Add view ظاهر شود (خاصیت custom tool آنرا هم خالی کنید).
در مورد اعتبارسنجیهای فرمها چطور؟
اصلاح مطالبی مانند «اعمال کلاسهای ویژه اعتبارسنجی Twitter bootstrap به فرمهای ASP.NET MVC» جهت کار با فرمهای بوت استرپ 3 بسیار ساده است. از این جهت که در کدهای آن فقط باید نام کلاسهای CSS قدیمی به جدید ویرایش شوند. مابقی کدها یکسان است. مثلا نام کلاس control-group شده است form-group (همان توضیحات ابتدای بحث جاری). کلاسهای error شدهاند has-error و success شده است has-success.
فایلهای نهایی این قسمت را از اینجا نیز میتوانید دریافت کنید:
bs3-sample05.zip
نحوه ارتقاء فرمهای بوت استرپ 2 به 3
تمام این تغییرات در بوت استرپ 3، جهت پیاده سازی ایده mobile-first بودن آن است. برای مثال فرمهای افقی بوت استرپ 3 با کوچک شدن اندازه صفحه، به صورت خودکار واکنش نشان داده و تبدیل به فرمهای معمولی که اجزای آن به صورت یک stack عمودی قرار گرفتهاند، میشوند.
اکنون اگر فرمهایی را دارید که در برنامههای پیشین خود از بوت استرپ 2 استفاده کردهاند، نیاز است تغییرات ذیل را به آنها اعمال کنید تا با سیستم جدید بوت استرپ 3 سازگار شوند:
- کلاس control-group را به کلاس form-group تبدیل کنید.
- form-search حذف شده است. آنرا با form-inline جایگزین کنید.
- دیگر نیازی به استفاده از input-block-level نیست؛ از آنجائیکه به صورت پیش فرض کلیه inputها دارای عرض 100 درصد هستند.
- help-inline حذف شده است. آنرا با help-block جایگزین کنید.
- عرض ستونها را در فرمهای افقی، برچسبها و کنترلها مشخص کنید.
- کلاس controls حذف شده است.
- کلاس form-control را به inputها و selectها اضافه کنید.
- checkboxها و radioها باید در یک div محصور شوند.
- کلاسهای radio.inline و checkbox.inline باید با inline جایگزین شوند.
- کلاسهای input-small به input-sm و input-large به input-lg تبدیل شدهاند.
- کلاسهای input-prepend با input-group و input-append با input-group جایگزین شدهاند.
- کلاس alert-error حذف شدهاست. بجای آن میشود از alert-warning استفاده کرد.
- کلاس alert-block را با alert جایگزین کنید.
ایجاد اولین فرم افقی با بوت استرپ 3
فرض کنید که قصد داریم یک چنین فرم افقی را توسط امکانات بوت استرپ 3 ایجاد کنیم:
همانطور که ملاحظه میکنید، با کوچک شدن اندازه صفحه، این فرم نیز تغییر شکل میدهد:
کدهای کامل این فرم را در ادامه ملاحظه میکنید:
<div class="container"> <h4 class="alert alert-info"> فرمهای بوت استرپ 3</h4> <div class="row"> <article class="registrationform"> <h2> فرم ثبت نام</h2> <form class="registration form-horizontal" action="#"> <fieldset id="personalinfo"> <legend>اطلاعات شخصی</legend> <section class="row"> <label class="col col-lg-4 control-label" for="myname"> نام</label> <div class="controls"> <input class="col col-lg-8" type="text" name="myname" id="myname" autofocus placeholder="نام و نام خانوادگی" required> </div> <!-- controls --> </section><!-- row --> <section class="row"> <label class="col col-lg-4 control-label" for="companyname"> نام شرکت</label> <div class="controls"> <input class="col col-lg-8" type="text" name="companybname" id="companyname" /> </div> <!-- controls --> </section><!-- row --> <section class="row"> <label class="col col-lg-4 control-label" for="myemail"> ایمیل</label> <div class="controls"> <input class="col col-lg-8" type="email" name="myemail" id="myemail" required autocomplete="off" /> </div> <!-- controls --> </section><!-- row --> </fieldset> <!-- personal info --> <fieldset id="otherinfo"> <legend>سایر اطلاعات</legend> <section class="row"> <label class="col col-lg-4 control-label"> نوع درخواست</label> <div class="controls col col-lg-8"> <label class="radio"> <input type="radio" name="requesttype" value="question" /> سؤال </label> <label class="radio"> <input type="radio" name="requesttype" value="comment" /> انتقاد </label> </div> <!-- controls --> </section><!-- row --> <section class="row"> <label class="col col-lg-4 control-label"> خبرنامه</label> <div class="controls col col-lg-8"> <label class="checkbox"> <input type="checkbox" id="subscribe" name="subscribe" checked value="yes" /> آیا مایل به دریافت ایمیلهای خبرنامه ما هستید؟ </label> </div> <!-- controls --> </section><!-- row --> <section class="row"> <label class="col col-lg-4 control-label" for="reference"> چطور از وجود سایت ما آگاه شدید؟</label> <div class="controls col col-lg-8"> <select name="reference" id="reference"> <option>لطفا انتخاب کنید...</option> <option value="friend">از طریق یک دوست</option> <option value="facebook">Facebook</option> <option value="twitter">Twitter</option> </select> </div> <!-- controls --> </section><!-- row --> </fieldset> <button class="btn" type="submit"> ارسال</button> </form> </article> </div> <!-- end row --> </div> <!-- /container -->
- باید درنظر داشت که اگر هیچگونه فرمتی را به فرمهای بوت استرپ 3 اعمال نکنیم، به صورت پیش فرض فرمت دهی شده و تبدیل به فرمهای عمودی شکیلی میشوند که شاید از دیدگاه خیلیها مناسب بوده و نیاز به تغییرات خاصی نداشته باشند.
- برای تبدیل این فرم عمودی پیش فرض، به فرمهای افقی دو ستونه، نیاز است یک سری کلاس بوت استرپ 3 را به المانهای آن اضافه کنیم. برای این منظور ابتدا کلاس form-horizontal را به تگ فرم اضافه میکنیم.
- هر سطر فرم، در یک المان section با کلاس row قرار خواهد گرفت.
- اکنون هر سطر، از یک برچسب به همراه یک یا چند المان تشکیل خواهد شد. در هر سطر، کنترلها در یک div با کلاس controls قرار میگیرند.
- برای اینکه برچسبهای هر ردیف با کنترلها و المانهای آن ردیف، تراز شوند، تنها کافی است به آنها کلاس control-label را اضافه کنیم.
در ادامه تمام این مراحل را باید به ازای هر سطر فرم تکرار کنیم.
- زمانیکه به radio buttons یا check boxes میرسیم، باید به چند نکته دقت داشت:
الف) حین کار با radio buttons، علاوه بر برچسب آن سطر که با label مشخص میشود، هر radio button نیز باید داخل یک label با کلاس radio محصور شود.
ب) تمام radio buttons یک سطر نیز باید در یک div ایی با کلاس controls محصور شوند.
این نکته در مورد check boxes نیز صادق است.
با رعایت همین چند نکته ساده میتوان به یک طراحی دو ستونی خودکار واکنشگرا رسید.
اصلاح قالب ایجاد فرمهای خودکار ASP.NET MVC بر اساس بوت استرپ 3
مطلب «ویرایش قالب پیش فرض Add View در ASP.NET MVC برای سازگار سازی آن با Twitter bootstrap» جهت بوت استرپ 2 تهیه شده بود. فایل نهایی ویرایش شده آنرا با توجه به توضیحات مطلب جاری برای بوت استرپ 3 از پیوست انتهای بحث دریافت کنید و برای استفاده از آن فقط کافی است آنرا در مسیر CodeTemplates\AddView\CSHTML\CreateBootstrap3Form.tt ریشه پروژه جاری خود کپی و به پروژه اضافه کنید تا در صفحه دیالوگ Add view ظاهر شود (خاصیت custom tool آنرا هم خالی کنید).
در مورد اعتبارسنجیهای فرمها چطور؟
اصلاح مطالبی مانند «اعمال کلاسهای ویژه اعتبارسنجی Twitter bootstrap به فرمهای ASP.NET MVC» جهت کار با فرمهای بوت استرپ 3 بسیار ساده است. از این جهت که در کدهای آن فقط باید نام کلاسهای CSS قدیمی به جدید ویرایش شوند. مابقی کدها یکسان است. مثلا نام کلاس control-group شده است form-group (همان توضیحات ابتدای بحث جاری). کلاسهای error شدهاند has-error و success شده است has-success.
فایلهای نهایی این قسمت را از اینجا نیز میتوانید دریافت کنید:
bs3-sample05.zip
سلام، ممنون واسه پست عالیتون
بنده از smtpmessage خود asp.net استفاده کردم و کاملا کار میده و ایمیل تایید حساب کاربری رو ارسال میکنه و روی لینک هم که کلیک میکنم توی صفحه مورد نظر میره (و توی آدرس بار code و UserId هست )ولی فیلد EmailConfirmed ، True نمیشه!
باید خودم دستی توی فرم لود اون لینکی که بهش میدم با استفاده از UserId که توی آدرس بار دارم ، فیلد ایمیل کانفیرم رو True کنم؟
بازخوردهای پروژهها
مشکل با نوشتن تابع تجمعی سفارشی(از طریق پیاده سازی IAggregateFunction)
با سلام؛ ضمن تشکر از اینکه تجربیاتتون رو رایگان در اختیار بقیه قرار میدید، به شخصه خیلی استفاده کردم.
و همچنین این هم تنظیمات ستونی که از این تابع تجمعی میخوام استفاده کنم.
ممنون میشم در صورت امکان کمکم کنید.
سوالی داشتم در رابطه با پیاده سازی اینترفیس IAggregateFunction
من میخوام یه گزارش بنویسم که تو اون ستون آخرش میخواد مانده تجمعی را حساب کنه.
بنابراین میخواستم با پیاده سازی این اینترفیس و همچنین بازنویسی متد ProcessingBoundary آخرین مقدار رو به عنوان خروجی تابع تجمعی ارسال کنم.
public object ProcessingBoundary(IList<SummaryCellData> columnCellsSummaryData) { if (columnCellsSummaryData == null || !columnCellsSummaryData.Any()) return 0; var list = columnCellsSummaryData; var lastItem = list.Last(); return lastItem.CellData.PropertyValue; }
در پروژهی دیگه ای این اینترفیس رو پیاده سازی کردم و مشکلی نبود ولی در پروژه جاری
که پروژه ایست با مشخصات:
نوع پروژه : WPF with MVVM
از Prism و Unity هم برای ماژولار شدن استفاده کردم.
خطای زیر رو میده :
Method 'set_DisplayFormatFormula' in type 'Hezareh.Modules.Accounting.Reporting.ViewModels.MySampleAggregateFunction' from assembly 'Hezareh.Modules.Accounting, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
در صورتی که اینترفیس IAggregateFunction به صورت کامل توسط کلاس MySampleAggregateFunction پیاده سازی شده است و این هم کد کامل کلاس که همون کد مثال Sum خودتونه، که فقط تابع ProcessingBoundary رو تغییر دادم. این هم کد کاملش :
public class MySampleAggregateFunction : IAggregateFunction { public MySampleAggregateFunction() { } /// <summary> /// Fires before rendering of this cell. /// Now you have time to manipulate the received object and apply your custom formatting function. /// It can be null. /// </summary> public Func<object, string> DisplayFormatFormula { set; get; } #region Fields (6) double _groupAvg; long _groupRowNumber; double _groupSum; double _overallAvg; long _overallRowNumber; double _overallSum; #endregion Fields #region Properties (2) /// <summary> /// Returns current groups' aggregate value. /// </summary> public object GroupValue { get { return _groupAvg; } } /// <summary> /// Returns current row's aggregate value without considering the presence of the groups. /// </summary> public object OverallValue { get { return _overallAvg; } } #endregion Properties #region Methods (4) // Public Methods (1) /// <summary> /// Fires after adding a cell to the main table. /// </summary> /// <param name="cellDataValue">Current cell's data</param> /// <param name="isNewGroupStarted">Indicated starting a new group</param> public void CellAdded(object cellDataValue, bool isNewGroupStarted) { checkNewGroupStarted(isNewGroupStarted); _overallRowNumber++; _groupRowNumber++; double cellValue; if (double.TryParse(cellDataValue.ToSafeString(), NumberStyles.AllowThousands | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out cellValue)) { groupAvg(cellValue); overallAvg(cellValue); } } // Private Methods (3) private void checkNewGroupStarted(bool newGroupStarted) { if (newGroupStarted) { _groupRowNumber = 0; _groupAvg = 0; _groupSum = 0; } } private void groupAvg(double cellValue) { _groupSum += cellValue; _groupAvg = _groupSum / _groupRowNumber; } private void overallAvg(double cellValue) { _overallSum += cellValue; _overallAvg = _overallSum / _overallRowNumber; } /// <summary> /// A general method which takes a list of data and calculates its corresponding aggregate value. /// It will be used to calculate the aggregate value of each pages individually, with considering the previous pages data. /// </summary> /// <param name="columnCellsSummaryData">List of data</param> /// <returns>Aggregate value</returns> public object ProcessingBoundary(IList<SummaryCellData> columnCellsSummaryData) { if (columnCellsSummaryData == null || !columnCellsSummaryData.Any()) return 0; var list = columnCellsSummaryData; var lastItem = list.Last(); return lastItem.CellData.PropertyValue; } #endregion Methods }
columns.AddColumn(column => { column.PropertyName<VoucherRowPrintViewModel>(x => x.CaclulatedRemains); column.CellsHorizontalAlignment(PdfRpt.Core.Contracts.HorizontalAlignment.Right); column.IsVisible(true); column.Order(5); column.Width(1.5f); column.ColumnItemsTemplate(template => { template.TextBlock(); template.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)); }); column.AggregateFunction(aggregateFunction => { aggregateFunction.CustomAggregateFunction(new MySampleAggregateFunction()); aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)); }); column.HeaderCell("مانده"); });
مطالب
ASP.NET MVC #3
تهیه پیشنیازهای شروع به کار با ASP.NET MVC
در زمان نگارش این مطلب، نگارش نهایی ASP.NET MVC 3 در دسترس است و همچنین نگارش بتای 4 آن نیز قابل دریافت و نصب میباشد. بنابراین فعلا اساس را بر مبنای نگارشی قرار خواهیم داد که در محیط کاری قابل استفاده باشد.
ASP.NET MVC 3 پس از ارائه Visual Studio 2010، منتشر شد و VS.NET به صورت پیش فرض به همراه ASP.NET MVC 2 است. سادهترین روش نصب ASP.NET MVC 3 بر روی VS 2010 استفاده از برنامه رایگانی است به نام Web Platform Installer. این برنامه را از این آدرس میتوان دریافت کرد: http://microsoft.com/web/downloads
پس از دریافت آن حداقل دو راه برای نصب ASP.NET MVC 3 وجود دارد. یا گزینهی نصب ASP.NET MVC 3 Tools Update را انتخاب کنید و یا سرویس پک یک VS 2010 را از طریق این برنامه یا جداگانه (بسته کامل و مستقل) دریافت و نصب نمائید. VS 2010 SP1 نیز به همراه ASP.NET MVC 3 است؛ همچنین IIS Express را که نسخه ساده شده IIS 7.5 مخصوص توسعه دهندهها است، میتوان با این نگارش یکپارچه کرد.
بنابراین به صورت خلاصه بهترین کار این است که سرویس پک یک VS 2010 را یکبار نصب نمائید. اگر این نصب از طریق برنامه Web Platform Installer باشد، به صورت خودکار IIS Express را هم انتخاب و نصب خواهد کرد. اگر فقط SP1 را به صورت مستقل دریافت کردهاید، حاوی IIS Express نیست و باید جداگانه آنرا دریافت و نصب نمائید (^). البته نصب IIS Express در اینجا یک گزینه اختیاری است و الزامی نیست.
مروری بر ساختار یک پروژه ASP.NET MVC
پس از نصب پیش نیازها، امکان انتخاب یک پروژه وب ASP.NET MVC 3 در VS 2010 میسر خواهد شد:
در اینجا گزینهی ASP.NET MVC 3 Web Application را انتخاب میکنیم. در صفحه بعدی که ظاهر میشود:
حالت Internet Application به همراه یک سری مدل و کنترلر از پیش نوشته شده جهت مدیریت ورود به سایت و ثبت نام در سایت است و حالت Empty تنها به همراه ساختار پیش فرض پوشههای یک پروژه ASP.NET MVC است.
فعلا جهت توضیحات اولیه بیشتر، گزینهی Internet Application و نوع View Engine را هم ASPX انتخاب میکنیم. کار View Engine، رندر یک View به شکل HTML و ارائه نهایی اطلاعات آن به کاربر است. این نوعهای متفاوت هم فقط در Syntax تفاوت دارند (به آن templating language هم گفته میشود). نوع ASPX همان Syntax متداول قدیمی ASP.NET را تداعی میکند و نوع Razor به صورت اختصاصی برای ASP.NET MVC تهیه شده است.
باید در نظر داشت که گزینه مرجح از نگارش 3 به بعد، Razor است (البته این هم سلیقهای است. اگر هیچکدام از این دو را هم نخواهید استفاده کنید مشکلی نیست! میشود کلا آن را عوض کرد). هدفم هم از انتخاب ASPX نمایش یک سری ریزه کاری است که شاید برای برنامه نویسهای ASP.NET Web forms جالب باشد. این موارد را در حالت انتخاب Razor به این وضوح مشاهده نخواهید کرد و محیط خیلی ساده شده است.
همانطور که ملاحظه میکنید این فریم ورک یک سری پوشه پیش فرض را توصیه میکند. بدیهی است که ضرورتی ندارد تا پوشه Models یا پوشه Controllers حتما در همین پروژه قرار داشته باشند؛ چون زمانیکه پروژه کامپایل شد، محل این پوشه بندیها آنچنان اهمیتی ندارد.
نکته جالب در این تصویر، فایل Site.Master است. بله، این فایل شبیه به همان فایل master page موجود در ASP.NET Web form است که قالب کلی سایت را به همراه داشته و سایر صفحات، قالب خود را از آن به ارث میبرند. حتی تگ runat=server هم به وضوح در این فایل، در چندین جای آن قابل مشاهده است. تنها تفاوت آن نداشتن فایل code behind است. asp:ContentPlaceHolder نیز در آن تعریف شده است. خلاصه این محیط جدید به معنای دور ریختن تمام آنچیزی که در Web forms وجود دارد نیست. برای نمونه اگر فایل ChangePassword.aspx موجود در پوشه Account را باز کنید، باز هم همان asp:Content معروف به همراه تگ runat=server قابل مشاهده است. برای مثال این محتوای صفحه Error.aspx پیش فرض آن است:
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>" %>
<asp:Content ID="errorTitle" ContentPlaceHolderID="TitleContent" runat="server">
Error
</asp:Content>
<asp:Content ID="errorContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Sorry, an error occurred while processing your request.
</h2>
</asp:Content>
اگر از قسمت Inherits آن صرفنظر کنیم، «هیچ» تفاوتی با ASP.NET Web forms ندارد؛ علت هم به این بر میگردد که موتوری که Web forms و MVC از آن استفاده میکنند، یکی است. هر دو بر فراز موتور ASP.NET معنا پیدا خواهند کرد.
قرار دادهای پوشههای پیش فرض یک پروژه ASP.NET MVC
- پوشه Controllers حاوی کلاسهای کنترلری است که درخواستهای رسیده را مدیریت میکنند.
- پوشه Models حاوی کلاسهایی است که اشیاء تجاری و همچنین کار با اطلاعات را تعریف و مدیریت میکنند.
- در پوشه Views، فایلهای قالبهای رابط کاربری که مسئول ارائه خروجی به کاربر هستند قرار میگیرند. همچنین مطابق قرارداد دیگری، اگر نام کنترلر ما مثلا ProductController باشد (با توجه به اینکه نام کلاس آن هم مطابق قرارداد، مختوم به کلمه Controller است)، فایلهای Viewهای مرتبط با آن در پوشه Views/Product قرار خواهند گرفت.
- در پوشه Scripts، فایلهای جاوا اسکریپت مورد استفاده در سایت قرار خواهند گرفت.
- پوشه Content محل قرارگیری فایلهای CSS و تصاویر است.
- پوشه App_Data جایی است که فایلهایی با قابلیت read/write در آن قرار میگیرند (و باید دقت داشت که فقط همینجا هم باید قرار گیرند و گرنه این نوشتنها در مکانهای متفرقه، ممکن است سبب ری استارت شدن برنامه شوند:(^)).
سلام وقت بخیر. چگونه میتوان کد زیر یا کد مشابهی را به اینترفیس unitofwork پروژه اضافه کرد.
using System.Linq.Expressions; public class ContextWithExtensionExample { public void DoSomeContextWork(DbContext context) { var uni = new Unicorn(); context.Set<Unicorn>().AddIfNotExists(uni , x => x.Name == "James"); } } public static class DbSetExtensions { public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate = null) where T : class, new() { var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any(); return !exists ? dbSet.Add(entity) : null; } }
نظرات مطالب
EF Code First #1
با سلام
ممنون از مطالب مفید شما.
من کلا با اصل code first مشکل دارم.
صرف وقت کم درسته برنامه سریع تر بالا می یاد ولی از طرف دیگه باید وقت بیشتری صرف توسعه مواردی کرد که توسط نویسنده فریم ورک پیش بینی نشده.
مشکل دومی که وجود داره در code first خیلی از ویژگی هایی که در هر دیتابیس وجو داره از دست داده می شه مثل استفاده از sp
در آخر یک سوال آیا شما حاضری یک برنامه پیچیده تحت وب با این مدل پیاده سازی کنی به عنوان یک مهندس صاحب نظر؟
ممنون از مطالب مفید شما.
من کلا با اصل code first مشکل دارم.
صرف وقت کم درسته برنامه سریع تر بالا می یاد ولی از طرف دیگه باید وقت بیشتری صرف توسعه مواردی کرد که توسط نویسنده فریم ورک پیش بینی نشده.
مشکل دومی که وجود داره در code first خیلی از ویژگی هایی که در هر دیتابیس وجو داره از دست داده می شه مثل استفاده از sp
در آخر یک سوال آیا شما حاضری یک برنامه پیچیده تحت وب با این مدل پیاده سازی کنی به عنوان یک مهندس صاحب نظر؟
نظرات مطالب
ASP.NET MVC #11
با سلام و خسته نباشید
کاملاً مشهوده که رفته رفته ViewModel ها بیشتر مورد استقبال قرار می گیرند. اما به نظر من استفاده از ViewModel ها در MVC صحیح نیست. دلیل اصلی اش اینه که ساختار MVC و اهداف اون رو تحت تأثیر قرار می ده. بقیه دلایل هم نهایتاً به همین بر می گردند.
معمولا ظاهر نرم افزارهای کاربردی تحت وب بیشتر از منطق درونی آنها تغییر می کنند. مثلا برای تنوع، یک کار ثابت رو به شکل های جدید انجام می دهند. در واقع برای یک کاری که توسط کنترلر بر روی مدل انجام می گیرد، view های جدید درست می شود. در این حالت برای اینکه بتوانیم تغییرات جدید رو اعمال کنیم، در هر بار علاوه بر اینکه باید ViewModel رو تصحیح کنیم (قطعات جدید اضافه یا حذف کنیم)، باید کنترلرهای مربوطه رو هم هماهنگ کنیم که این باعث کاهش استقلال لایه ها می شود.
دلیل محبوبیت ViewModel ها همانا سادگی آن ها و راحتی طراحی است. یعنی در یک صفحه نسبتاً پیچیده که قسمت های مختلفی دارد، خیلی زود می شه یک ViewModel درست کرد و همه قسمت های صفحه رو توش پوشش داد. اما به نظر من این بعداً مشکل ساز خواهد شد. بهتر است به دور از عجله کمی به ساختار صفحه و راه حل های جایگزین فکر کرد. به عنوان نمونه:
1- می شه از امکانات خود MVC برای انتقال اطلاعات بین کنترلر و ویو استفاده کرد. می تونیم نمونه ای از مدل رو به ویو بفرستیم و برای موارد اضافی از ViewBag استفاده کنیم. مثلاً می شه اسامی استان ها رو که به یک DropDownList بایند خواهند شد، به شکل زیر بفرستیم:
ViewBag.StatesList = new SelectList(_bll.GetStatesList(), "Id", "Title"); //ltr
2- این توضیح رو بدم که MVC ما رو تشویق می کنه که منطق نرم افزارمون رو به ماژول های کوچک تقسیم کنیم و هر ماژول رو به تنهایی مدیریت کنیم. همه جا این تقسیم بندی ها هست. اول کل ساختار نرم افزار رو به سه لایه M و V و C تقسیم می کنه. بعد View ها و کنترلر های هر مدل رو که (که در عالم واقعیت هم مستقل هستند) از هم تفکیک می کنه و یاد میده اینها هر کدوم باید تو فولدر جداگانه ای باشند. Area ها هم در همین راستا هستند. حتی در داخل کنترلر ها، هر کدوم از اکشن ها یک کار واحد کوچک رو انجام می دهند. اما ViewModel این ساختار رو می شکنه و ما رو مجبور می کنه با مفاهیم مرکب سرو کار داشته باشیم. شاید سؤال این باشه که در عمل ما صفحاتی داریم که باید چند ماهیت رو در اونها نمایش بدیم. مثلاً ممکنه در یک صفحه واحد، هم لیست دانش آموز ها رو به همراه امکانات حذف، اضافه و ویرایش داشته باشیم و هم لیست کلاس ها رو. علاوه بر اون یک فرم ارسال نظر در پایین صفحه، یک textbox برای فیلتر کردن و ... داشته باشیم. درسته که می شه با زحمت کمی یک ViewModel حاوی تمام اینها درست کرد و بعد یک View از اون ساخت و در نگاه اول همه چی به خوبی کار کنه. ولی بهتره هر کدوم از اینها مستقلاً پیاده سازی شوند. مثلاً صفحه ای برای دانش آموزان، صفحه ای برای کلاس ها و ... غصه اون صفحه بزرگ رو هم نخورید. می شه هر کدوم از View های مستقل رو در داخل اون نمایش داد و همون ظاهر رو بدست آورد (مثلاً با استفاده از Html.Action یا Ajax). حسن بزرگی که داره اینه که هر کدوم از این قطعات در قسمت های مختلف قابل استفاده می شوند. به این ترتیب می توانیم با جابجایی آن ها در صفحات مختلف، ظاهر نرم افزارمون رو به دلخواه تغییر بدیم بدون اینکه مجبور باشیم تغییری در کنترلر ها و قسمت های دیگه ایجاد کنیم.
در کل ViewModel ها استقلال لایه ها را کاهش می دهند، شدیداً موجب افزایش کدها می شوند، قابلیت گسترش منطقی ندارند و رفته رفته بزرگتر و بزرگتر می شوند، ماژولار نیستند و ...
بنابراین استفاده از ViewModel مکروه بوده و دوری از آن احتیاط واجب است! منتظر فتوای شما در این زمینه هستم. چون به نظر من ارجحه و فصل الخطاب.
با اینکه سعی کردم کوتاه بشه نشد. ببخشید. دوستون دارم. داود زینی
کاملاً مشهوده که رفته رفته ViewModel ها بیشتر مورد استقبال قرار می گیرند. اما به نظر من استفاده از ViewModel ها در MVC صحیح نیست. دلیل اصلی اش اینه که ساختار MVC و اهداف اون رو تحت تأثیر قرار می ده. بقیه دلایل هم نهایتاً به همین بر می گردند.
معمولا ظاهر نرم افزارهای کاربردی تحت وب بیشتر از منطق درونی آنها تغییر می کنند. مثلا برای تنوع، یک کار ثابت رو به شکل های جدید انجام می دهند. در واقع برای یک کاری که توسط کنترلر بر روی مدل انجام می گیرد، view های جدید درست می شود. در این حالت برای اینکه بتوانیم تغییرات جدید رو اعمال کنیم، در هر بار علاوه بر اینکه باید ViewModel رو تصحیح کنیم (قطعات جدید اضافه یا حذف کنیم)، باید کنترلرهای مربوطه رو هم هماهنگ کنیم که این باعث کاهش استقلال لایه ها می شود.
دلیل محبوبیت ViewModel ها همانا سادگی آن ها و راحتی طراحی است. یعنی در یک صفحه نسبتاً پیچیده که قسمت های مختلفی دارد، خیلی زود می شه یک ViewModel درست کرد و همه قسمت های صفحه رو توش پوشش داد. اما به نظر من این بعداً مشکل ساز خواهد شد. بهتر است به دور از عجله کمی به ساختار صفحه و راه حل های جایگزین فکر کرد. به عنوان نمونه:
1- می شه از امکانات خود MVC برای انتقال اطلاعات بین کنترلر و ویو استفاده کرد. می تونیم نمونه ای از مدل رو به ویو بفرستیم و برای موارد اضافی از ViewBag استفاده کنیم. مثلاً می شه اسامی استان ها رو که به یک DropDownList بایند خواهند شد، به شکل زیر بفرستیم:
ViewBag.StatesList = new SelectList(_bll.GetStatesList(), "Id", "Title"); //ltr
2- این توضیح رو بدم که MVC ما رو تشویق می کنه که منطق نرم افزارمون رو به ماژول های کوچک تقسیم کنیم و هر ماژول رو به تنهایی مدیریت کنیم. همه جا این تقسیم بندی ها هست. اول کل ساختار نرم افزار رو به سه لایه M و V و C تقسیم می کنه. بعد View ها و کنترلر های هر مدل رو که (که در عالم واقعیت هم مستقل هستند) از هم تفکیک می کنه و یاد میده اینها هر کدوم باید تو فولدر جداگانه ای باشند. Area ها هم در همین راستا هستند. حتی در داخل کنترلر ها، هر کدوم از اکشن ها یک کار واحد کوچک رو انجام می دهند. اما ViewModel این ساختار رو می شکنه و ما رو مجبور می کنه با مفاهیم مرکب سرو کار داشته باشیم. شاید سؤال این باشه که در عمل ما صفحاتی داریم که باید چند ماهیت رو در اونها نمایش بدیم. مثلاً ممکنه در یک صفحه واحد، هم لیست دانش آموز ها رو به همراه امکانات حذف، اضافه و ویرایش داشته باشیم و هم لیست کلاس ها رو. علاوه بر اون یک فرم ارسال نظر در پایین صفحه، یک textbox برای فیلتر کردن و ... داشته باشیم. درسته که می شه با زحمت کمی یک ViewModel حاوی تمام اینها درست کرد و بعد یک View از اون ساخت و در نگاه اول همه چی به خوبی کار کنه. ولی بهتره هر کدوم از اینها مستقلاً پیاده سازی شوند. مثلاً صفحه ای برای دانش آموزان، صفحه ای برای کلاس ها و ... غصه اون صفحه بزرگ رو هم نخورید. می شه هر کدوم از View های مستقل رو در داخل اون نمایش داد و همون ظاهر رو بدست آورد (مثلاً با استفاده از Html.Action یا Ajax). حسن بزرگی که داره اینه که هر کدوم از این قطعات در قسمت های مختلف قابل استفاده می شوند. به این ترتیب می توانیم با جابجایی آن ها در صفحات مختلف، ظاهر نرم افزارمون رو به دلخواه تغییر بدیم بدون اینکه مجبور باشیم تغییری در کنترلر ها و قسمت های دیگه ایجاد کنیم.
در کل ViewModel ها استقلال لایه ها را کاهش می دهند، شدیداً موجب افزایش کدها می شوند، قابلیت گسترش منطقی ندارند و رفته رفته بزرگتر و بزرگتر می شوند، ماژولار نیستند و ...
بنابراین استفاده از ViewModel مکروه بوده و دوری از آن احتیاط واجب است! منتظر فتوای شما در این زمینه هستم. چون به نظر من ارجحه و فصل الخطاب.
با اینکه سعی کردم کوتاه بشه نشد. ببخشید. دوستون دارم. داود زینی