آناتومی یک گزارش خطای خوب
برای دوستانی که انگلیسیشون زیاد خوب نیست (مثل خودم) و کلا با انگلیسی مشکل دارن، جهت سرچ در گوگل روش زیر پیشنهاد میگردد.
معمولا بهتره توی گوگل مشکلتون رو اینطوری سرچ کنین :
how to <doing problem> in <your programming language>
how to convert int to string in c#
<programming language> how <problem>
نکته دوم اینه که اگر توی نتیجه سرچ لینکی از سایت StackOverFlow دیدین ، اول بهتره اون لینک رو باز کنین.
چون معمولا این سایت بصورت سوال و جوابه و سریع با دیدن کدهای نمونه ای که در پاسخها موجوده مشکلتون حل میشه و معمولا اصلا نیاز نیست متون انگلیسی رو بخونین.
مخصوصا که برای هر پاسخ یک امتیاز وجود داره که با بررسی امتیاز هر پاسخ میتوان تا حدودی به درست بودن و بهینه بودن راه حل پی برد (این مورد حدودی است)
البته سایت codeProject هم خوب است ولی تا جایی که من میدونم بصورت مقاله ای است و باید کلی توضیح و مثال و ... رو بررسی کنین تا به جواب سوالتون برسین که یکم وقت گیر تره ...
نکته بعدی: بد نیست برخی مشکلات رو بصورت فارسی هم سرچ بگیرین ، در برخی موارد مطلب فارسی هم در مورد برخی خطاها پیدا میشه (معمولا فارسی زبونا زیاد مقاله درباره خطاها نمینویسن و بیشتر تکنولوژی رو آموزش میدن ...)
{البته همه دوستان حرفه ای هستند ، به جهت دوستانی که مثل خودم زیاد با انگلیسی میونه خوبی ندارند عرض کردم}
ضمنا در تکمیل مقاله آقای نصیری باید ذکر کنم که :
*همیشه به خاطر داشته باشید که هیچکس علمشو مجانی بدست نیاورده که مجانی در اختیار شما قرار بده !
پس اگر کسی داره مقاله منتشر میکنه یا جواب سوالتونو میده ، داره به شما لطف میکنه...
پس سعی کنین همیشه ازش تشکر کنین و بهش احترام بذارین تا بیشتر تشویق بشه که مطالب بیشتری بذاره ... (به نظر من دست کسی که مجانی داره بهت چیز یاد میده ، باید بوسید؛ چون در حقیقت وقت و انرژی و علمشو داره به شما هدیه میکنه که خیلی ارزشمنده)
*نوشتن کامنت هایی که دانش نفر رو زیر سوال ببره ، مثلا "این روش که دیگه خدا بیامرز شده ، الان هر برنامه نویس حرفه ای از فلان روش استفاده میکنه" خیلی بده و نفر رو نا امید میکنه.
*لحن گفتن یک سوال یا بیان یک کامنت خیلی خیلی مهمه.
مثلا همین مقال بالا رو میشه بدین صورت هم گفت : "در تکمیل مقاله جناب X باید ذکر نمایم که روش Y نیز بسیار جالب میباشد و دارای کد نویسی بهتری است" و حتی یک لینک آموزشی خوب هم درباره روش جدید ارائه دهیم.
(اینطوری هر دو طرف سود میکنن)
همین!
موفق باشید.
در این پست و با استفاده از سری پستهای آقای مهندس یوسف نژاد در این زمینه، یک Attribute جهت هماهنگ سازی با سیستم اعتبار سنجی ASP.NET MVC در سمت سرور و سمت کلاینت (با استفاده از jQuery Validation) بررسی خواهد شد.
قبل از شروع مطالعه سری پستهای MVC و Entity Framework الزامی است.
برای انجام این کار ابتدا مدل زیر را در برنامه خود ایجاد میکنیم.
using System;
public class SampleModel
{
public DateTime StartDate { get; set; }
public string Data { get; set; }
public int Id { get; set; }
}
using System; using System.ComponentModel.DataAnnotations; public class SampleModel { [Required(ErrorMessage = "Start date is required")] public DateTime StartDate { get; set; } [Required(ErrorMessage = "Data is required")] public string Data { get; set; } public int Id { get; set; } }
@model SampleModel @{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } <section> <header> <h3>SampleModel</h3> </header> @Html.ValidationSummary(true, null, new { @class = "alert alert-error alert-block" }) @using (Html.BeginForm("SaveData", "Sample", FormMethod.Post)) { <p> @Html.LabelFor(x => x.StartDate) @Html.TextBoxFor(x => x.StartDate) @Html.ValidationMessageFor(x => x.StartDate) </p> <p> @Html.LabelFor(x => x.Data) @Html.TextBoxFor(x => x.Data) @Html.ValidationMessageFor(x => x.Data) </p> <input type="submit" value="Save"/> } </section>
public class SampleController : Controller { // // GET: /Sample/ public ActionResult Index() { return View(); } public ActionResult SaveData(SampleModel item) { if (ModelState.IsValid) { //save data } else { ModelState.AddModelError("","لطفا خطاهای زیر را برطرف نمایید"); RedirectToAction("Index", item); } return View("Index"); } }
تا اینجای کار روال عادی همیشگی است. اما برای طراحی Attribute دلخواه جهت اعتبار سنجی (مثلا برای مجبور کردن کاربر به وارد کردن یک فیلد با پیام دلخواه و زبان دلخواه) باید یک کلاس جدید تعریف کرده و از کلاس RequiredAttribute ارث ببرد. در پارامتر ورودی این کلاس جهت کار با Resourceهای ثابت در نظر گرفته شده است اما برای اینکه فیلد دلخواه را از دیتابیس بخواند این روش جوابگو نیست. برای انجام آن باید کلاس RequiredAttribute بازنویسی شود.
کلاس طراحی شده باید به صورت زیر باشد:
public class VegaRequiredAttribute : RequiredAttribute, IClientValidatable { #region Fields (2) private readonly string _resourceId; private String _resourceString = String.Empty; #endregion Fields #region Constructors (1) public VegaRequiredAttribute(string resourceId) { _resourceId = resourceId; ErrorMessage = _resourceId; AllowEmptyStrings = true; } #endregion Constructors #region Properties (1) public new String ErrorMessage { get { return _resourceString; } set { _resourceString = GetMessageFromResource(value); } } #endregion Properties #region Methods (2) // Public Methods (1) public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { yield return new ModelClientValidationRule { ErrorMessage = GetMessageFromResource(_resourceId), ValidationType = "required" }; } // Private Methods (1) private string GetMessageFromResource(string resourceId) { var errorMessage = HttpContext.GetGlobalResourceObject(_resourceId, "Yes") as string; return errorMessage ?? ErrorMessage; } #endregion Methods }
1- ابتدا دستور
HttpContext.GetGlobalResourceObject(_resourceId, "Yes") as string;
که عنوان کلید Resource را از سازنده کلاس گرفته (کد اقای یوسف نژاد) رشته معادل آن را از دیتابیس بازیابی میکند.
2- ارث بری از اینترفیس IClientValidatable، در صورتی که از این اینترفیس ارث بری نداشته باشیم. Validator طراحی شده در طرف کلاینت کار نمیکند. بلکه کاربر با کلیک بروی دکمه مورد نظر دادهها را به سمت سرور ارسال میکند. در صورت وجود خطا در پست بک خطا نمایش داده خواهد شد. اما با ارث بری از این اینترفیس و پیاده سازی متد GetClientValidationRules میتوان تعریف کرد که در طرف کلاینت با استفاده از Unobtrusive jQuery پیام خطای مورد نظر به کنترل ورودی مورد نظر (مانند تکست باکس) اعمال میشود. مثلا در این مثال خصوصیت data-val-required به input هایی که قبلا در مدل ما Reqired تعریف شده اند اعمال میشود.
حال در مدل تعریف شده میتوان به جای Required میتوان از VegaRequiredAttribute مانند زیر استفاده کرد. (همراه با نام کلید مورد نظر در دیتابیس)
public class SampleModel { [VegaRequired("RequiredMessage")] public DateTime StartDate { get; set; } [VegaRequired("RequiredMessage")] public string Data { get; set; } public int Id { get; set; } }
با اجرای برنامه اینبار خروجی به شکل زیر خواهد بود.
جهت فعال ساری اعتبار سنجی سمت کلاینت ابتدا باید اسکریپتهای زیر به صفحه اضافه شود.
<script src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<appSettings> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true"/> </appSettings>
<script type="text/javascript"> jQuery.validator.addMethod('required', function (value, element, params) { if (value == null | value == "") { return false; } else { return true; } }, ''); jQuery.validator.unobtrusive.adapters.add('required', {}, function (options) { options.rules['required'] = true; options.messages['required'] = options.message; }); </script>
موفق و موید باشید.
منابع ^ و ^ و ^ و ^
در این بلاگ میتوان:
- یک مطلب جدید را ارسال کرد.
- مطالب قابل ویرایش و یا حذف هستند.
- مطالب بلاگ قسمت ارسال نظرات دارند.
- امکان گزارشگیری از آخرین نظرات ارسالی وجود دارد.
- سایت صفحات درباره و تماس با ما را نیز دارا است.
ساختار پوشههای برنامه
در تصویر ذیل، ساختار پوشههای برنامه بلاگ را ملاحظه میکنید. چون قسمت سمت کلاینت این برنامه کاملا جاوا اسکریپتی است، پوشههای App، Controllers، Libs، Models، Routes و Templates آن در پوشهی Scripts تعریف شدهاند و به این ترتیب میتوان تفکیک بهتری را بین اجزای تشکیل دهندهی یک برنامهی تک صفحهای وب Emeber.js پدید آورد.
فایل CSS بوت استرپ نیز به پوشهی Content اضافه شدهاست.
دریافت پیشنیازهای سمت کاربر برنامه
در ساختار پوشههای فوق، از پوشهی Libs برای قرار دادن کتابخانههای پایه برنامه مانند jQuery و Ember.js استفاده خواهیم کرد. به این ترتیب:
- نیاز به آخرین نگارشهای Ember.js و همچنین افزونهی Ember-Data آن برای کار سادهتر با دادهها و سرور وجود دارد. این فایلها را از آدرس ذیل میتوانید دریافت کنید (نسخههای نیوگت به دلیل قدیمی بودن و به روز نشدن مداوم آنها توصیه نمیشوند):
http://emberjs.com/builds/#/beta
برای حالت آزمایش برنامه، استفاده از فایلهای دیباگ آن توصیه میشوند (فایلهایی با نام اصلی و بدون پسوند prod یا min). زیرا این فایلها خطاها و اطلاعات بسیار مفصلی را از اشکالات رخ داده، در کنسول وب مرورگرها، فایرباگ و یا Developer tools آنها نمایش میدهند. نسخهی min برای حالت ارائهی نهایی برنامه است. نسخهی prod همان نسخهی دیباگ است با حذف اطلاعات دیباگ (نسخهی production فشرده نشده). نسخهی فشرده شدهی prod آن، فایل min نهایی را تشکیل میدهد.
- دریافت جیکوئری
- آخرین نگارش handlebars.js را از سایت رسمی آن دریافت کنید: http://handlebarsjs.com
- Ember Handlebars Loader: https://github.com/michaelrkn/ember-handlebars-loader
- Ember Data Local Storage Adapter: https://github.com/kurko/ember-localstorage-adapter
ترتیب تعریف و قرارگیری این فایلها را پس از دریافت، در فایل index.html قرار گرفته در ریشهی سایت، در کدهای ذیل مشاهده میکنید:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Ember Blog</title> <link href="Content/bootstrap.css" rel="stylesheet" /> <link href="Content/bootstrap-theme.css" rel="stylesheet" /> <link href="Content/styles.css" rel="stylesheet" /> <script src="Scripts/Libs/jquery-2.1.1.min.js" type="text/javascript"></script> <script src="Scripts/Libs/bootstrap.min.js" type="text/javascript"></script> <script src="Scripts/Libs/handlebars-v2.0.0.js" type="text/javascript"></script> <script src="Scripts/Libs/ember.js" type="text/javascript"></script> <script src="Scripts/Libs/ember-handlebars-loader-0.0.1.js" type="text/javascript"></script> <script src="Scripts/Libs/ember-data.js" type="text/javascript"></script> <script src="Scripts/Libs/localstorage_adapter.js" type="text/javascript"></script> </head> <body> </body> </html>
اصلاح فایل ember-handlebars-loader-0.0.1.js
اگر به فایل ember-handlebars-loader-0.0.1.js مراجعه کنید، مسیر فایلهای قالب handlebars قسمتهای مختلف برنامه را از پوشهی templates واقع در ریشهی سایت میخواند. با توجه به تصویر ساختار پوشهی پروژهی جاری، پوشهی template به داخل پوشهی Scripts منتقل شدهاست و نیاز به یک چنین اصلاحی دارد:
url: "Scripts/Templates/" + name + ".hbs",
<system.webServer> <staticContent> <mimeMap fileExtension=".hbs" mimeType="text/x-handlebars-template" /> </staticContent> </system.webServer>
مزیت استفاده از نسخهی دیباگ ember.js در حین توسعهی برنامه
نسخهی دیباگ ember.js علاوه بر به همراه داشتن خطاهای بسیار جامع و توضیح علل مشکلات، مواردی را که در آینده منسوخ خواهند شد نیز توضیح میدهد:
برای مثال در اینجا عنوان شدهاست که دیگر از linkTo استفاده نکنید و آنرا به link-to تغییر دهید.
همچنین اگر از مرورگر کروم استفاده میکنید، افزونهی Ember Inspector را نیز میتوانید نصب کنید تا اطلاعات بیشتری را از جزئیات مسیریابیهای تعریف شده و قالبهای Ember.js بتوان مشاهده کرد. این افزونه به صورت یک برگهی جدید در Developer tools آن ظاهر میشود.
ایجاد شیء Application
همانطور که در قسمتهای پیشین نیز عنوان شد (^ و ^ )، یک برنامهی Ember.js با تعریف وهلهای از شیء Application آن آغاز میشود. برای این منظور به پوشهی App مراجعه کرده و فایل جدید Scripts\App\blogger.js را اضافه کنید؛ با این محتوا:
Blogger = Ember.Application.create();
<script src="Scripts/App/blogger.js" type="text/javascript"></script>
تعاریف مسیریابی و قالبها
اکنون در ادامه قصد داریم لیستی از عناوین مطالب ارسالی را نمایش دهیم. در ابتدا این عناوین را از یک آرایهی ثابت جاوا اسکریپتی دریافت خواهیم کرد و پس از آن از یک Web API کنترلر، جهت دریافت اطلاعات از سرور کمک خواهیم گرفت.
کار router در Ember.js، نگاشت آدرس درخواستی توسط کاربر، به یک route یا مسیریابی تعریف شدهاست. به این ترتیب مدل، کنترلر و قالب آن route به صورت خودکار بارگذاری و پردازش خواهند.
با مراجعه به ریشهی سایت، فایل index.html بارگذاری میشود.
در اینجا تصویری از صفحهی آغازین بلاگ را مشاهده میکنید. در آن صفحهی posts همان ریشهی سایت نیز میباشد. بنابراین نیاز است ابتدا مسیریابی آنرا تعریف کرد. برای این منظور، فایل جدید Scripts\App\router.js را به پوشهی App اضافه کنید؛ با این محتوا:
Blogger.Router.map(function () { this.resource('posts', { path: '/' }); });
همچنین مدخل آنرا نیز در فایل index.html تعریف نمائید:
<script src="Scripts/App/blogger.js" type="text/javascript"></script> <script src="Scripts/App/router.js" type="text/javascript"></script>
افزودن مسیریابی و قالب posts
در ادامه فایل جدید Scripts\Templates\posts.hbs را اضافه کنید. به این ترتیب قالب خالی مطالب به پوشهی templates اضافه میشود. محتوای این فایل را به نحو ذیل تنظیم کنید:
<div class="container"> <h1>Emeber.js blog</h1> <ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> </div>
برای بارگذاری این قالب نیاز است آنرا به template loader توضیح داده شده در ابتدای بحث، در فایل index.html اضافه کنیم:
<script> EmberHandlebarsLoader.loadTemplates([ 'posts' ]); </script>
افزودن مسیریابی و قالب about
در ادامه قصد داریم صفحهی about را اضافه کنیم. مجددا با افزودن مسیریابی آن به فایل Scripts\App\router.js شروع میکنیم:
Blogger.Router.map(function () { this.resource('posts', { path: '/' }); this.resource('about'); });
<h1>About Ember Blog</h1> <p>Bla bla bla!</p>
<script> EmberHandlebarsLoader.loadTemplates([ 'posts', 'about' ]); </script>
<script type="text/x-handlebars" data-template-name="about"> </script>
برای آزمایش این مسیر و قالب جدید آن، آدرس http://localhost/#/about را بررسی کنید.
اضافه کردن منوی ثابت بالای سایت
روش اول این است که به ابتدای هر دو قالب about.hbs و posts.hbs، تعاریف منو را اضافه کنیم. مشکل اینکار، تکرار کدها و پایین آمدن قابلیت نگهداری برنامه است. روش بهتر، افزودن کدهای مشترک بین صفحات، در قالب application برنامه است. نمونهی آنرا در مثال قسمت قبل مشاهده کردهاید. در اینجا چون قصد نداریم به صورت دستی قالبها را به صفحه اضافه کنیم و همچنین برای ساده شدن نگهداری برنامه، قالبها را در فایلهای مجزایی قرار دادهایم، تنها کافی است فایل جدید Scripts\Templates\application.hbs را به پوشهی قالبهای برنامه اضافه کنیم؛ با این محتوا:
<div class='container'> <nav class='navbar navbar-default' role='navigation'> <ul class='nav navbar-nav'> <li>{{#link-to 'posts'}}Posts{{/link-to}}</li> <li>{{#link-to 'about'}}About{{/link-to}}</li> </ul> </nav> {{outlet}} </div>
<script> EmberHandlebarsLoader.loadTemplates([ 'posts', 'about', 'application' ]); </script>
افزودن مسیریابی و قالب contact
برای افزودن صفحهی تماس با مای سایت، ابتدا مسیریابی آنرا در فایل Scripts\App\router.js تعریف میکنیم:
Blogger.Router.map(function () { this.resource('posts', { path: '/' }); this.resource('about'); this.resource('contact'); });
<h1>Contact</h1> <ul> <li>Phone: ...</li> <li>Email: ...</li> </ul>
<script> EmberHandlebarsLoader.loadTemplates([ 'posts', 'about', 'application', 'contact' ]); </script>
<div class='container'> <nav class='navbar navbar-default' role='navigation'> <ul class='nav navbar-nav'> <li>{{#link-to 'posts'}}Posts{{/link-to}}</li> <li>{{#link-to 'about'}}About{{/link-to}}</li> <li>{{#link-to 'contact'}}Contact{{/link-to}}</li> </ul> </nav> {{outlet}} </div>
تعریف مسیریابی تو در تو در صفحهی contact
در حال حاضر صفحهی Contact، ایمیل و شماره تماس را در همان بار اول نمایش میدهد. میخواهیم این دو را تبدیل به دو لینک جدید کنیم که با کلیک بر روی هر کدام، محتوای مرتبط، در قسمتی از همان صفحه بارگذاری شده و نمایش داده شود.
برای اینکار نیاز است مسیریابی را تو در تو تعریف کنیم:
Blogger.Router.map(function () { this.resource('posts', { path: '/' }); this.resource('about'); this.resource('contact', function () { this.resource('email'); this.resource('phone'); }); });
پس از آن دو فایل قالب جدید Scripts\Templates\email.hbs را با محتوای:
<h2>Email</h2> <p> <span></span> Email name@site.com. </p>
<h2>Phone</h2> <p> <span></span> Call 12345678. </p>
<script> EmberHandlebarsLoader.loadTemplates([ 'posts', 'about', 'application', 'contact', 'email', 'phone' ]); </script>
<h1>Contact</h1> <div class="row"> <div class="col-md-6"> <p> Want to get in touch? <ul> <li>{{#link-to 'phone'}}Phone{{/link-to}}</li> <li>{{#link-to 'email'}}Email{{/link-to}}</li> </ul> </p> </div> <div class="col-md-6"> {{outlet}} </div> </div>
در اینجا نحوهی پردازش مسیریابی contact را ملاحظه میکنید. ابتدا درخواستی جهت مشاهدهی آدرس http://localhost/#/contact دریافت میشود. سپس router این درخواست را به مسیریابی همنامی منتقل میکند. این مسیریابی ابتدا قالب عمومی application را رندر کرده و سپس قالب اصلی و همنام مسیریابی جاری یا همان contact.hbs را رندر میکند. در این صفحه چون مسیریابی تو در تویی تعریف شدهاست، اگر درخواستی برای مشاهدهی http://localhost/#/contact/phone دریافت شود، محتوای آنرا در {{outlet}} قالب contact.hbs جاری رندر میکند.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید:
EmberJS03_01.zip
چند دقیقه مثل Scott Hansleman وقت بذارین Terminal تون رو حرفه ای و زیبا کنین. یک عمر لذتش رو ببرین :)
اول اگه کاربر ویندوز هستین و با ترمینال/powershell/wsl/cmd و... سروکار دارین حتما Windows Terminal رو نصب کنین
اگه نمیدونین چیه؟ یه ترمینال باحال سبک و ساده با امکانات کاربردی
توضیحات بیشتر:
https://docs.microsoft.com/en-us/windows/terminal/
بعدش دوتا ماژول posh-git و oh-my-posh رو طبق آموزش زیر نصب کنین
https://docs.microsoft.com/en-us/windows/terminal/tutorials/powerline-setup
اینطوری موقع کار با ترمینال، پوشه ها، وضعیت ریپازیتوری git و... رو بهتون زیباتر نشون میده
https://github.com/dahlbyk/posh-git
در آخر هم وقت بذارین یه فونت خوب متناسب با سلیقه تون از سایت nerd fonts پیدا کنین و نصب کنین. nerd fonts اومده اومده فونتهای محبوب و معروف برنامه نویسی رو برداشته کلی icon و glyph بهشون اضافه کرده که جون میده باهاش ترمینالتون رو زیبا کنین مثل کاری که Scott Hanselman کرده
https://nerdfonts.com/font-downloads
یه سایت دیگه هم هست که کلی فونت مخصوص برنامه_نویسی داره و به شما اجازه میده به صورت انلاین پیش نمایشش رو واسه یه تیکه کد ببینین، تست کنین و بعد انتخاب کنین
مثلا این 3تا لینک مربوط به فونتهای معروف Cascadia Code و Fira Code و JetBrains Mono هست
خواندنیهای 16 اردیبهشت
به خاطر کتاب خوبتون در رابطه با asp ممنونم
شرمنده از اینکه مزاحم شدم . یه مشکلی پیدا کردم که نمی تونم حلش کنم و هرچقدر هم سرچ کردم و توی دولوپرسنتر هم گذاشتم جوابمو پیدا نکردم برای همین هم مزاحم شما شدم
اگه لطف کنین و راهنمایی ام کنین ممنون میشم
من دارم یه فروشگاه الکترونیکی مینویسم و این فروشگاه دیتابیسی با جدول های زیر داره:
user
product
category
order
و در قسمت user علاوه بر مشخصات عمومی کاربر، username , pass , email شخص هم هست
از طرف دیگه میخوام از form_base security که توسط ویژوال استودیو فراهم میشه (asp.net configuration موجود در سلوشن اکسپلورر) استفاده کنم و این خودش باعث ایجاد یه دیتابیس دیگه میشه و برنامه من در حقیقت دارای دو تا دیتابیس میشه
حالا من باید چیکار کنم؟
منظورم اینه که pass , email در جدول مربوط به دیتابیس اصلی آیا باید حذف بشه و از اونایی که در دیتابیس دوم هست استفاده کنم و فیلد مربوط به username باید در هر دوتا باقی بمونه و با مقایسه اونا با هم متوجه بشم که کدام پسورد و ایمیل مال کدوم کاربره؟
یا فقط از دیتابیس دوم استفاده کنم و چهار تا جدول مربوط به دیتابیس اصلی را هم در همین دیتابیس دومی ادغام کنم(اگه بشه)؟
ممنون
شاید بعضی از سایتها را دیده باشید که در حین ثبت نام، پس از وارد کردن یک نام کاربری و سپس مشغول شدن به پر کردن فیلد کلمهی عبور، در قسمت نام کاربری شروع به جستجو در مورد آزاد بودن نام کاربری درخواستی میکنند یا نمونهای دیگر، فرم پرداخت الکترونیکی بانک سامان. پس از اینکه شماره قبض را وارد کردید، بلافاصله بدون ریفرش صفحه به شما پیغام میدهد که این شماره معتبر است یا خیر. امروز قصد داریم این قابلیت را با استفاده از کتابخانهی Ajax مجموعه jQuery در ASP.Net پیاده سازی کنیم (بدون استفاده از ASP.Net Ajax مایکروسافت).
ابتدا سورس کامل را ملاحظه نمائید:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AjaxTest.aspx.cs" Inherits="testWebForms87.AjaxTest" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>jQuery Ajax Text</title>
<script src="jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#<%= TextBox1.ClientID %>").blur(function(event) {
$.ajax({
type: "POST",
url: "AjaxTest.aspx/IsUserAvailable",
data: "{'username': '" + $('#<%= TextBox1.ClientID %>').val() + "'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
$('#valid').html("<img src='ajaxImages/waiting.gif' alt='لطفا کمی تامل کنید'>");
var delay = function() {
AjaxSucceeded(msg);
};
setTimeout(delay, 2000); //remove this
},
error: AjaxFailed
});
});
});
function AjaxSucceeded(result) {
if (result.d == true)
$('#msg').html("<img src='ajaxImages/available.gif' alt='نام کاربری درخواستی موجود است'>");
else
$('#msg').html("<img src='ajaxImages/taken.gif' alt='متاسفانه نام کاربری مورد نظر پیشتر دریافت شدهاست'>");
}
function AjaxFailed(result) {
alert(result.status + ' ' + result.statusText);
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
user name:
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<span id="msg"></span>
<br />
pass:
<asp:TextBox ID="TextBox2" TextMode="Password" runat="server"></asp:TextBox>
</div>
<!-- preload -->
<div style="display: none">
<img src="ajaxImages/available.gif" alt="available" />
<img src="ajaxImages/taken.gif" alt="taken" />
<img src="ajaxImages/waiting.gif" alt="waiting" />
</div>
</form>
</body>
</html>
using System;
using System.Web.Services;
namespace testWebForms87
{
public partial class AjaxTest : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
[WebMethod]
public static bool IsUserAvailable(string username)
{
// این مورد را با خواندن اطلاعات از دیتابیس میشود تعویض کرد
return username != "test";
}
}
}
همانطور که ملاحظه میکنید صفحهی ASP.Net ما بسیار ساده است و از دو تکست باکس استاندارد تشکیل میشود، به همراه تصاویر مربوط به Ajax که یک سری تصاویر ساده چرخان معروف منتظر بمانید ، یافت شد یا موجود نیست میباشند. این تصاویر در یک div مخفی (display: none) در صفحه قرار گرفتهاند و در هنگام بارگذاری صفحه، اینها نیز بارگذاری شده و حاضر و آماده خواهند بود. بنابراین هنگام استفاده از آنها، کاربر تاخیری را مشاهده نخواهد کرد. همچنین یک span با id مساوی msg را هم پس از تکست باکس اضافه کردهایم تا تصاویر مربوط به رخدادهای Ajax را با استفاده از تواناییهای jQuery به آن اضافه کنیم.
اسکریپت Ajax ما با دراختیار گرفتن روال رخداد گردان blur شیء textBox1 شروع میشود. همانطور که در مقالات پیشین سایت نیز ذکر شد، روش صحیح دریافت ID یک کنترل ASP.Net در کدهای سمت کلاینت جاوا اسکریپتی، بر اساس خاصیت ClientID آن است که در اولین سطر کدهای ما مشخص است (زیرا در ASP.Net نام و ID یک کنترل در هنگام رندر شدن به همراه ID کنترلهای دربرگیرنده آن نیز خواهد بود، بنابراین بهتر است این مورد را داینامیک کرد).
کار بررسی موجود بودن نام کاربری (یا مثلا یک شماره قبض و امثال آن) توسط WebMethod ایی به نام IsUserAvailable در code behind صفحه انجام میشود که پیاده سازی آنرا ملاحظه میکنید. بدیهی است در این مثال ساده، تنها نام کاربری از پیش رزرو شده، کلمهی test است و در یک کد واقعی این مورد با مقایسهی نام کاربری با اطلاعات موجود در دیتابیس باید صورت گیرد (و حملات تزریق اس کیوال را هم فراموش نکنید. برای رهایی از آنها "حتما" باید از پارامترهای ADO.Net استفاده کرد و گرنه کد شما مستعد به این نوع حملات خواهد بود).
سؤال: چرا از web method استفاده شد و همچنین چرا این متد static است؟
زمانیکه یک متد با کلمه کلیدی static مشخص میشود حالت state less پیدا میکند یعنی مستقل از وهلهی کلاس عمل میکند. در این حالت نیازی به ارسال ViewState نبوده (بنابراین در کوئری مورد نظر ما بسیار بهینه و سبک عمل میکنند) و همچنین نیازی به ایجاد یک وهلهای از کلاس صفحهی ما نیز نخواهد بود. برای توضیحات بیشتر به این مقاله مراجعه نمائید. (به صورت خلاصه، دلیل اصلی، کارآیی بالا و بهینه بودن این روش در این مساله ویژه است و در ASP.Net Ajax مایکروسافت به صورت گستردهای در پشت صحنه مورد استفاده قرار میگیرد)
استفاده از ویژگی WebMethod عملکرد صفحهی ما را شبیه به یک وب سرویس خواهد کرد و امکان دسترسی به آن در متدهای استاندارد POST به صورت ارسال دیتا به آدرس WebService.asmx/WebMethodName خواهد بود. یک مثال ساده و عملی
بررسی تابع Ajax بکار رفته:
این تابع هنگام فراخوانی رخداد blur تکستباکس ما (مطابق کد فوق) فراخوانی میشود. ساختار سادهای دارد که به شرح زیر است:
type: "POST"
url: "AjaxTest.aspx/IsUserAvailable"
data: "{'username': '" + $('#<%= TextBox1.ClientID %>').val() + "'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
<xx yy="nn"></xx>
{ "xx": {"yy":"nn"} }
success: function(msg)
error: AjaxFailed
در این مثال برای نمایش بهتر عملیات، یک وقفهی 2 ثانیهای توسط setTimeout ایجاد شده و بدیهی است در یک مثال واقعی باید آنرا حذف نمود.
نکته: با استفاده از افزونهی فایرباگ فایرفاکس، میتوان جزئیات این عملیات را بهتر مشاهده نمود:
Microbenchmark
static void Profile(string description, int iterations, Action func) { // clean up GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); // warm up func(); var watch = Stopwatch.StartNew(); for (int i = 0; i < iterations; i++) { func(); } watch.Stop(); Console.Write(description); Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds); }
Profile("a descriptions", how_many_iterations_to_run, () => { // ... code being profiled });
var iterations = 10000000; var testString = ".NET Tips is awesme!"; do { var sb1 = new StringBuilder(testString); var sb2 = new StringBuilder(testString) { Capacity = testString.Length * iterations }; try { Profiler.Profile("StringBuilder Profiler", iterations, () => sb1.Append(testString)); Profiler.Profile("StringBuilder Capacity Profiler", iterations, () => sb2.Append(testString)); } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { Console.WriteLine("----------------------------------------------------------------"); sb1.Clear(); sb2.Clear(); } } while (Console.ReadKey(true).Key == ConsoleKey.C); // C = continue
npm install @microsoft/signalr --save
npm install @microsoft/signalr --save