نظرات مطالب
سه مطلب کوتاه
هر ssl certificate حتما دارای یک تاریخ انقضاء است. احتمالا سایت مورد نظر شما مجوز خودش را به روز نکرده است. سایت‌های دست چندم ممکن است از این نوع مشکلات داشته باشند.
نظرات مطالب
EF Code First #2
این خطا به این معنا است که بر اساس تنظیمات رشته‌ی اتصالی شما، EF Code First سعی کرده‌است یک بانک اطلاعاتی جدید را از صفر ایجاد کند و ... کاربری که در رشته‌ی اتصالی ذکر شده‌است، دسترسی ایجاد بانک اطلاعاتی جدیدی را ندارد. به همین منظور، در این رشته‌ی اتصالی، از یک بانک اطلاعاتی از پیش ایجاد شده استفاده کنید. (اگر هاست اشتراکی است، باید درخواست دهید تا برای شما بانک اطلاعاتی جدیدی را ایجاد کنند؛ به همراه ارائه‌ی مشخصات اتصال به آن. سپس بر این اساس هست که باید رشته‌ی اتصالی شما اصلاح شود)
مطالب
Best Practice هایی برای طراحی RESTful API - قسمت دوم

طراحی Url در Restful API

Url بخش اصلی و راه ارتباطی API شما با توسعه دهنده است .بنابراین طراحی یک ساختار مناسب و یکپارچه برای Url ها دارای اهمیت زیادی است .

Url پایه API خود را ساده و خوانا ، حفظ کنید . داشتن یک Url پایه ساده استفاده از API را آسان کرده و خوانایی آن را بالا میبرد و باعث می‌شود که توسعه دهنده برای استفاده از آن نیاز کمتری به مراجعه به مستندات داشته باشد. پیشنهاد می‌شود که برای هر منبع تنها دو Url پایه وجود داشته باشد . یکی برای مجموعه ای از منبع موردنظر  و دیگری برای یک واحد مشخص از آن منبع . برای مثال اگر منبع موردنظر ما کتاب باشد ، خواهیم داشت :

.../books
برای مجموعه‌ی کتابها و
.../books/1001
برای کتابی با شناسه 1001

استفاده از این روش یک مزیت دیگر هم به همراه دارد و آن دور کردن افعال از Url‌‌ها است.

بسیاری در زمان طراحی Url‌‌ها و در نامگذاری از فعل‌ها استفاده می‌کنند. برای هر منبعی که مدلسازی می‌کنید هیچ وقت نمی‌توانید آن را به تنهایی و جداافتاده در نظر بگیرید. بلکه همیشه منابع مرتبطی وجود دارند که باید در نظر گرفته شوند. در مثال کتاب می‌توان منابعی مثل نویسنده ، ناشر ، موضوع و ... را بیان کرد. حالا سعی کنید به تمام Url هایی که برای پوشش دادن تمام درخواست‌های مربوط به منبع کتاب نیاز داریم فکر کنید . احتمالا به چیزی شبیه این می‌رسیم :

.../getAllBooks

.../getBook

.../newBook

.../getNewBooksSince

.../getComputerBooks

.../BooksNotPublished

.../UpdateBookPriceTo

.../bookForPublisher

.../GetLastBooks

.../DeleteBook

….
خیلی زود یک لیست طولانی از  Url‌‌ها خواهید داشت که به علت نداشتن یک الگوی ثابت و مشخص استفاده از API شما را واقعا سخت می‌کند.

پس حالا این درخواست‌های متنوع را چطور با دو Url اصلی انجام دهیم ؟

1- از افعال Http برای کار کردن بر روی منابع استفاده کنید . با استفاده از افعال Http شامل POST ، GET ، PUT و DELETE  و دو Url اصلی ، یک مجموعه‌ی مناسب از عملیات‌ها در دسترس توسعه دهنده خواهد بود . به جدول زیر نگاه کنید .

 

ترکیب افعال http و آدرس منبع

توسعه دهندگان احتمالا نیازی به این جدول برای درک اینکه API چطور کار می‌کند نخواهند داشت.

2- با استفاده از نکته قبلی بخشی از Url‌‌های بالا حذف خواهند شد. اما هنوز با روابط بین منابع چکار کنیم؟ منابع تقریبا همیشه دارای روابطی با دیگر منابع هستند . یک روش ساده برای بیان این روابط در API چیست ؟ به مثال کتاب برمیگردیم. کتاب‌ها دارای نویسنده هستند. اگر بخواهیم کتاب‌های یک نویسنده را برگردانیم چه باید بکنیم؟ با استفاده از Url‌‌های پایه و افعال Http می‌توان اینکار را انجام داد. یکی از ساختارهای ممکن این است  :

GET .../authors/1001/books
اگر بخواهیم یک کتاب جدید به کتابهای این نویسنده اضافه کنیم :
POST .../authors/1001/books
و حدس زدن اینکه برای حذف کتابهای این نویسنده چه باید کرد ، سخت نیست .  

3- بیشتر API‌ها دارای پیچیدگی‌های بیشتری نسبت به Url اصلی یک منبع هستند . هر منبع مشخصات و روابط متنوعی دارد که قابل جستجو کردن، مرتب سازی، بروزرسانی و تغییر هستند. Url اصلی را ساده نگه دارید و این پیچیدگی‌ها را به کوئری استرینگ منتقل کنید.

برای برگرداندن تمام کتابهای با قیمت پنچ هزار تومان با قطع جیبی که دارای امتیاز 8 به بالا هستند از کوئری زیر می‌شود استفاده کرد :

GET .../books?price=5000&size=pocket&score=8

و البته فراموش نکنید که لیستی از فیلدهای مجاز را در مستندات خود ارائه کنید.

4 - گفتیم که بهتر است افعال را از Url  ها خارج کنیم . ولی در مواردی که درخواست ارسال شده در مورد یک منبع نیست چطور؟ مواردی مثل محاسبه مالیات پرداختی یا هزینه بیمه ، جستجو در کل منابع ، ترجمه یک عبارت یا تبدیل واحدها . هیچکدام از اینها ارتباطی با یک منبع خاص ندارند. در این موارد بهتر است از افعال استفاده شود. و حتما در مستندات خود ذکر کنید که در این موارد از افعال استفاده می‌شود.
.../convert?value=25&from=px&to=em

.../translate?term=web&from=en&to=fa

5 - استفاده از اسامی جمع یا مفرد

با توجه به ساختاری که تا اینجا طراحی کرده ایم بکاربردن اسامی جمع بامعناتر و خواناتر است. اما مهمتر از روشی که بکار می‌برید ، اجتناب از بکاربردن هر دو روش با هم است ، اینکه در مورد یک منبع از اسم منفرد و در مورد دیگری از اسم جمع استفاده کنید . یکدستی API را حفظ کنید و به توسعه دهنده کمک کنید راحت‌تر API شما را یاد بگیرد.

6- استفاده از نام‌های عینی به جای نام‌های کلی و انتزاعی

API ی را در نظر بگیرید که محتواهایی را در فرمت‌های مختلف ارائه می‌دهد. بلاگ ، ویدئو ، اخبار و .... حالا فرض کنیداین API منابع را در بالاتری سطح  مدسازی کرده باشد مثل /items یا /assets . درک کردن محتوای این API و کاری که می‌توان با این API انجام داد برای توسعه دهنده سخت است . خیلی راحت‌تر و مفیدتر است که منابع را در قالب بلاگ ، اخبار ، ویدئو مدلسازی کنیم .

 
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 19 - بومی سازی
روش انتقال منابع مرتبط با کلاس‌ها و کنترلرها به یک اسمبلی دیگر
- فرض کنید یک class library مخصوص NET Core. را به نام Core1RtmTestResources.ExternalResources جهت درج منابع تهیه کرده‌اید و پوشه‌ی Resources را از پروژه‌ی اصلی به آن انتقال داده‌اید (بدون هیچ تغییر نامی).
- نیازی نیست تا قسمت options.ResourcesPath کلاس آغازین برنامه را تغییر دهید و همان مقدار Resources در این حالت هم کار می‌کند.
- فقط نکته‌ی مهم اینجا است:
public TestLocalController(
            IStringLocalizer<TestLocalController> stringLocalizer,
            IHtmlLocalizer<TestLocalController> htmlLocalizer)
زمانیکه این آرگومان‌های جنریک مشخص می‌شوند، دو کاربرد «تعیین نام نوع» و «تعیین محل اسمبلی» واقع شدن آن‌را به یکباره به همراه دارند.
الان چون این اسمبلی دیگر اسمبلی جاری نیست، باید به نحو زیر عمل کرد:
private readonly IStringLocalizer _stringLocalizer;
private readonly IHtmlLocalizer _htmlLocalizer;
 
public TestLocalController(
    IStringLocalizerFactory stringLocalizerFactory,
    IHtmlLocalizerFactory htmlLocalizerFactory)
{
    _stringLocalizer = stringLocalizerFactory.Create(
        baseName: "Controllers.TestLocalController" /*مشخصات کنترلر جاری*/,
        location: "Core1RtmTestResources.ExternalResources" /*نام اسمبلی ثالث*/);
    _htmlLocalizer = htmlLocalizerFactory.Create(
        baseName: "Controllers.TestLocalController" /*مشخصات کنترلر جاری*/,
        location: "Core1RtmTestResources.ExternalResources" /*نام اسمبلی ثالث*/);
}
یعنی باید کاری را که در پشت صحنه به صورت خودکار انجام می‌شود، اینبار دستی انجام داد و با کلاس‌های Factory شروع کرد. در اینجا پارامتر location دقیقا به نام اسمبلی ثالث دربرگیرنده‌ی منابع اشاره می‌کند. به این صورت است که اینبار اسمبلی مرتبط، به درستی یافت شده و از آن استفاده می‌شود.
مطالب
خلاصه اشتراک‌های روز 1390/06/25
مسیرراه‌ها
ASP.NET MVC
              مطالب
              تنظیم رشته اتصالی Entity Framework به بانک اطلاعاتی به وسیله کد
              در زمان ساخت مدل از بانک اطلاعاتی در روش Database First به صورت پیش فرض تنظیمات مربوط به اتصال (Connection String) مدل به بانک اطلاعاتی در فایل config برنامه ذخیره می‌شود. مشکل این روش آن است که در سیستم‌های مختلف، بسته به بستری که نرم افزار قرار است بر روی آن اجرا شود، باید تنظیمات مربوط به بانک اطلاعاتی صورت گیرد.
              مثلا فرض کنید شما در زمان توسعه نرم افزار، SQL Server را به صورت Local بر روی سیستم خود نصب کرده اید و Connection String ساخته شده توسط ویزارد Entity Framework بر همین اساس ساخته و ذخیره شده‌است. حال بعد از انتشار برنامه، شخصی تصمیم دارد برنامه را بر روی سیستمی نصب کند که بانک اطلاعاتی Local نداشته و تصمیم به اتصال به یک بانک اطلاعاتی بر روی سرور دیگر یا با مشخصات (Login و Password و ...) دیگر را دارد. برای این مواقع نیاز به پیاده سازی روشی است تا کاربر نهایی بتواند تنظیمات مربوط به اتصال به بانک اطلاعاتی را تغییر دهد.
              روش‌های مختلفی مثل تغییر فایل app.config به صورت Runtime یا ... در سایت‌های مختلف ارائه شده که اکثرا روش‌های غیر اصولی و زمانبری جهت پیاده سازی هستند.
              ساده‌ترین روش جهت انجام این کار، اعمال تغییری کوچک در Constructor کلاس مدل مشتق شده از DBContext می‌باشد. فرض کنید مدلی از بانک اطلاعاتی Personnely با نام PersonallyEntities ساخته اید که حاصل آن کلاس زیر خواهد بود:
                  public partial class PersonallyEntities : DbContext
                  {
                      public PersonallyEntities()
                          : base("name=PersonallyEntities")
                      {
                      }
                  }
              همانطور که مشاهده می‌کنید، در Constructor این کلاس، نام Connection String مورد استفاده جهت اتصال به بانک اطلاعاتی به صورت زیر آورده شده که به Connection String ذخیره شده در فایل Config اشاره می‌کند:
              "name=PersonallyEntities"
              اگر به Connection String ذخیره شده در فایل Config دقت کنید متوجه می‌شوید که Connection String ذخیره شده، دارای فرمتی خاص و متفاوتی نسبت به Connection String معمولی ADO.NET است. متن ذخیره شده شامل تنظیمات و Metadata مدل ساخته شده جهت ارتباط با بانک اطلاعاتی نیز می‌باشد:
               metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string="data source=.;initial catalog=Personally;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"
              جهت تولید پویای Connection String، بسته به تنظیمات کاربر، نیاز است تا در آخر Connection String ی با فرمت بالا در اختیار Entity Framework قرار دهیم تا امکان اتصال به بانک فراهم شود. جهت تبدیل Connection String معمول ADO.NET به Connection String قابل فهم EF میتوان از کلاس EntityConnectionStringBuilder به صورت زیر استفاده کرد:
                      public static string BuildEntityConnection(string connectionString)
                      {
                          var entityConnection = new EntityConnectionStringBuilder
                          {
                              Provider = "System.Data.SqlClient",
                              ProviderConnectionString = connectionString,
                              Metadata = "res://*"
                          };
              
                          return entityConnection.ToString();
                      }
              همانطور که مشاهده می‌کنید، متد بالا با دریافت یک connectionString که همان ADO.NET ConnectionString ما می‌باشد، تنظیمات و Metadata مورد نیاز Entity Framework را به آن اضافه کرده و یک EF ConnectionString برمی‌گرداند.
              برای اینکه بتوان EF ConnectionString تولید شده را در هنگام اجرای برنامه به صورت Runtime اعمال کرد، نیاز است تا تغییر کوچکی در Constructor کلاس مدل تولید شده توسط Entity Framework ایجاد کرد. کلاس PersonnelyEntities به صورت زیر تغییر پیدا می‌کند:

                  public partial class PersonallyEntities : DbContext
                  {
                      public PersonallyEntities(string connectionString)
                          : base(connectionString)
                      {
              
                      }
                  }
              با اضافه شدن پارامتر connectionString به سازنده کلاس PersonnelyEntities برای ساخت یک نمونه از مدل ساخته شده در کد نیاز است تا Connection String مورد نظر جهت برقراری ارتباط با بانک را به عنوان پارامتر، به متد سازنده پاس دهیم. سپس مقدار این پارامتر به کلاس والد ( DbContext ) جهت برقراری ارتباط با بانک اطلاعاتی ارجاع داده شده: 
              : base(connectionString)
              در آخر به صورت زیر میتوان توسط EF به بانک اطلاعاتی مورد نظر متصل شد :
              var entityConnectionString = BuildeEntityConnection("Data Source=localhost;Initial Catalog=Personally; Integrated Security=True");
              var PersonallyDb = new PersonallyEntities(entityConnectionString);
              با این روش میتوان ADO Connection String مربوط به اتصال بانک اطلاعاتی را به راحتی به صورت داینامیک به وسیله اطلاعات وارد شده توسط کاربر و کلاس‌های تولید Connection String نظیر SQLConnectionStringBuilder تولید کرد و بدون تغییر در کد‌های برنامه، به بانک‌های مختلفی متصل شد. همچنین با داینامیک کردن متد Provider کلاس EntityConnectionStringBuilder که در کد بالا با "System.Data.SqlClient" مقدار دهی شده، می‌توان وابستگی برنامه بانک اطلاعی خاص را از بین برد و بسته به تنظیمات مورد نظر کاربر، به موتورهای مختلف بانک اطلاعاتی متصل شد که البته لازمه این کار رعایت یکسری نکات فنی در پیاده سازی پروژه است که از حوصله این مقاله خارج است.
              موفق باشید
              مطالب
              توضیح مثالی از SIMD برای نشان دادن عملکرد آن - SIMD Performance
              پیشنیازها

              SIMD یا ترجمه آن به فارسی به معنی «تک دستورالعمل و چند داده»، قابلیت آن‌را دارد تا بر روی مقادیر عددی به صورت موازی و با استفاده از پردازنده کار کند. اگر بتوانیم ساختار پروژه‌های خود را به طوری ایجاد کنیم تا بتوانیم از SIMD در پردازش‌های خود استفاده کنیم، سرعت انجام فعالیت‌ها، بسیار زیاد افزایش پیدا خواهند کرد؛ به خصوص این امر در حجم‌های پردازشی زیاد محسوس خواهد بود. البته مدیریت استفاده از منابع و پردازنده نباید فراموش شوند.
              اطلاعات لازم از SIMD و نحوه عملکرد آن را می‌توانید در مقاله پیشنیاز بیابید. در این مقاله قصد داریم تا یک مثال ساده از کارآیی SIMD را مطرح کنیم. مثال زیر از مثال SimdSpike الگو برداری شده است و تغییراتی نیز جهت تکمیل شدن آن انجام شده است.
              در این مثال می‌خواهیم نمونه کدهایی را با روش‌های معمول اجرا کنیم و زمان اجرای آن را با زمان اجرای همان مثال‌ها با روش SIMD، مقایسه کنیم. 
              با استفاده از ویژوال استودیو 2015 آپدیت 3 یک پروژه کنسول با چارچوب دات نت 4.6.1 ایجاد کرده‌ایم. البته می‌توانید ازدیگر نسخه‌ها هم استفاده کنید به شرط آنکه دات نت 4.6x را نصب کرده باشید.

              در صورتی که ویژوال استودیوی شما دارای این ورژن و آپدیت نبود، می‌توانید چارچوب دات نت 4.6.1 را جداگانه در سیستم خود نصب نمایید. توجه داشته باشید که برای استفاده از چارچوب دات نت در ویژوال استودیو باید نسخه‌های DevPack یا DeveloperPack را نصب نمایید (دریافت  دات نت 4.6.1 نسخه مخصوص استفاده در ویژال استودیو). 

              در پروژه ایجاد شده فایلی به نام Program.cs و در آن کلاس Program وجود دارد. در این کلاس تابع شروع کننده برنامه یعنی Main وجود دارد و برنامه از این تابع شروع خواهد شد.

              نمایی از فایل‌های پروژه

              در تابع شروع کننده برنامه ابتدا وضعیت پشتیبانی از SIMD را چک می‌کنیم. این کار را همانطور که قبلا در مقاله پیشنیاز توضیح داده شده است با استفاده از خاصیت Vector.IsHardwareAccelerated بررسی می‌کنیم. اگر مقدار آن برابر با False باشد به معنای عدم پشتیبانی می‌باشد و با بررسی این موضوع در اول برنامه، در صورت عدم پشتیبانی از SIMD به اجرای ادامه‌ی برنامه خاتمه می‌دهیم.

              پس از بررسی وضعیت پشتیبانی از SIMD ، تابعی را که در فایل Utilities.cs نوشته شده است، فراخوانی می‌کنیم. این تابع به بررسی وضعیت تعداد رجیسترهای SIMD و وضعیت انواع نوع‌های داده‌ای در SIMD می‌پردازد. اگر هر نوع داده‌ای از SIMD پشتیبانی کند (که بستگی به نوع پردازنده شما دارد) اندازه هر نوع داده‌ای را در SIMD چاپ می‌کند و در صورت عدم پشتیبانی هر نوع داده‌ای از SIMD مقدار «عدم پشتیبانی SIMD از آن نوع داده‌ای» چاپ خواهد شد. 

                تا به اینجای برنامه کد‌های تابع شروع کننده به صورت زیر خواهد بود. 
              using System.Numerics;
              using static System.Console;
              
              namespace TestSIMD
              {
                  class Program
                  {
                      private const int ArraySize = 7680 * 4320;
                      static void Main(string[] args)
                      {
                          // بررسی وضعیت پشتیبانی از SIMD
                          if (!Vector.IsHardwareAccelerated)
                          {
                              WriteLine("Hardware acceleration not supported.");
                              WriteLine();
                              return; // عدم پشتیبانی و خاتمه برنامه
                          }
                          WriteLine("Hardware acceleration is supported"); // اعلام پشتیبانی از SIMD
                          WriteLine();
              
                          // بررسی وضعیت نوع‌های داده ای در مشخصات سخت افزاری SIMD
                          Utilities.PrintHardwareSpecificSimdEffectiveness();
              
                          //به منظور عدم خروج از برنامه و دیدن نتایج آزمایش
                          WriteLine("Press any key to exit");
                          ReadKey();
                      }
                  }
              }
              اجرای برنامه هم به صورت زیر به نمایش در خواهد آمد. 

              در فایل Utilities.cs، توابع دیگری هم وجود دارند که کارآیی هر یک به صورت توضیح در بالای هر تابع نوشته شده است. این توابع برای تولید یک نوع داده‌ای تصادفی و ایجاد آرایه‌ای از نوع داده‌ای به صورت تصادفی به کار برده می‌شوند. می توانید در سورس برنامه این توضیحات را مشاهده کنید.
              تا به اینجا تنها به بررسی پشتیبانی سخت افزاری از SIMD پرداختیم و همچنین توانستیم نوع‌های داده‌ای را که SIMD در سخت افزار ما پشتیبانی می‌کند، شناسایی کنیم و اندازه رجیستر‌های آنها را بیابیم.
              حال به بررسی عملکرد توابع SIMD می‌پردازیم و با نوشتن چند تابع، زمان اجرای محاسباتی آنها را با نوشتن همان توابع در حالت معمولی و ساده مقایسه می‌کنیم.
               برای انجام مقایسه، زمان اجرای یک عملیات را در حالت معمول، با زمان اجرای همان عملیات در حالت SIMD بررسی می‌کنیم. هر عملیات را 3 مرتبه پشت سر هم اجرا می‌کنیم و زمان آنها را ثبت می‌کنیم تا تفاوت زمان اجرا را با تکرار عملیات نیز مشاهده کنیم. توابعی که آزمایشات را انجام می‌دهند و زمان اجرا را ثبت و نمایش می‌دهند، در فایل PerformanceTests.cs و در کلاس PerformanceTests قرار دارند و از توابع سه کلاس دیگر که عملیات در آن نوشته شده‌اند، استفاده می‌کنند.
              • فایل IntSimdProcessor.cs
                • در این فایل کلاسی به نام IntSimdProcessor قرار دارد که شامل 6 تابع می‌باشد و این تابع‌ها با نوع داده‌ای صحیح یا همان Integer کار می‌کنند. نام کلاس هم به همین خاطر نام گذاری شده است. 
                • این 6 تابع در کل 3 عملیات را شامل عملیات‌های زیر انجام می‌دهند. یکبار در حالت معمولی و یکبار با استفاده از توابع SIMD این کار را انجام می‌دهند:
                  • پیدا کردن بزرگترین و کوچکترین عدد در آرایه
                  • جمع عناصر دو آرایه با هم با استفاده از یک آرایه کمکی که نتیجه در آرایه کمکی ریخته می‌شود
                  • جمع عناصر دو آرایه بدون استفاده از آرایه کمکی که مجموع در آرایه اول ریخته می‌شود
                • در بالای هر تابع در این فایل توضیحات لازم درباره‌ی فعالیت آن تابع ذکر شده است.
               
              • فایل FloatSimdProcessor.cs
                • در این فایل کلاسی با نام FloatSimdProcessor قرار دارد که همانطور که از نام کلاس پیداست، توابعی برای کار بر روی اعداد از نوع داده‌ای float در آن نوشته شده‌اند.
                • در این کلاس هم 6 تابع برای انجام 3 عملیات زیر نوشته شده است که به ازای هر عملیات دو تابع یکی در حالت معمولی و یکی در حالت SIMD نوشته شده است.
                  • جمع دو آرایه با استفاده از یک آرایه کمکی - مجموع در آرایه کمکی ریخته می‌شود
                  • جمع دو آرایه اول ورودی - مجموع در آرایه سوم ریخته می‌شود
                  • جمع دو آرایه بدون استفاده از آرایه کمکی - مجموع در آرایه اول ریخته می‌شود
                • در آزمایشات نوشته شده در کلاس PerformanceTests  تنها از عملیات آخری استفاده شده است و از دو عملیات اول استفاده نشده است که در صورت تمایل می‌توانید از دیگر عملیات‌ها نیز استفاده کنید.
                • در بالای هر تابع در این فایل توضیحات لازم درباره‌ی فعالیت آن تابع نیز ذکر شده است.
               
              • فایل UShortSimdProcessor.cs
                • در این فایل کلاسی با نام UShortSimdProcessor قرار دارد و همانطور که از نام کلاس پیداست، توابعی برای کار بر روی اعداد از نوع داده‌ای ushort یا همان اعداد صحیح کوچک بدون علامت نوشته شده‌اند.
                • در این کلاس 12 تابع برای انجام 6 عملیات زیر نوشته شده‌است که به ازای هر عملیات، دو تابع یکی در حالت معمولی و یکی در حالت SIMD نوشته شده است.
                  • جمع دو آرایه اول ورودی که مجموع در آرایه سوم ریخته می‌شود
                  • جمع دو آرایه بدون استفاده از آرایه کمکی که مجموع در آرایه اول ریخته می‌شود
                  • بدست آوردن کمترین و بیشترین مقدار در یک آرایه اعداد صحیح کوچک بدون علامت
                  • جمع عناصر آرایه ورودی و ذخیره مجموع آنها در یک متغیر کمکی
                  • جمع عناصر آرایه ورودی و ذخیره مجموع آنها در یک متغیر کمکی بدون بررسی سرریز (Overflow)
                  • محاسبه میانگین و بدست آوردن کمترین و بیشترین مقدار در یک آرایه اعداد صحیح کوچک بدون علامت
                • در بالای هر تابع در این فایل توضیحات لازم درباره‌ی فعالیت آن تابع ذکر شده است.
               
              حال در کلاس PerformanceTests برای انجام آزمایشات و مقایسه زمان اجرا، 10 تابع وجود دارند که 10 عملیات مختلف را بر روی 3 نوع داده‌ای، اجرا می‌کنند. 3 عملیات از کلاس IntSimdProcessor و یک عملیات از کلاس FloatSimdProcessor و 6 عملیات از کلاس UShortSimdProcessor را مورد آزمایش قرار داده‌ایم که در مجموع شامل 10 آزمایش در 10 تابع مختلف شده است.
              public static void TestIntArrayAdditionFunctions(int testSetSize) {
                  WriteLine();
                  Write("Testing int array addition, generating test data...");
                  var intsOne = GetRandomIntArray(testSetSize); //تولید آرایه عددی به صورت تصادفی
                  var intsTwo = GetRandomIntArray(testSetSize);
                  WriteLine($" done, testing...");// پایان تولید آرایه‌ها و شروع پردازش
                  var naiveTimesMs = new List<long>(); // تعریف لیستی برای ریختن زمان پاسخ دهی در حالت ساده و معمولی
                  var hwTimesMs = new List<long>(); // تعریف لیستی برای ریختن زمان پاسخ دهی در حالت SIMD و سخت افزاری 
                  for (var i = 0; i < 3; i++) { // ایجاد حلقه برای تکرار محاسبات برای اندازه گیری زمان در حالت تکراری
                      stopwatch.Restart();//شروع ثبت زمان
                      var result = IntSimdProcessor.NaiveSumFunc(intsOne, intsTwo);//اجرای تابع جمع دو آرایه
                      var naiveTimeMs = stopwatch.ElapsedMilliseconds;//ثبت زمان
                      naiveTimesMs.Add(naiveTimeMs);//افزودن زمان ثبت شده به لیست زمان‌های ساده و معمول
                      WriteLine($"Naive analysis took:                {naiveTimeMs}ms (last value = {result.Last()}).");
              
                      stopwatch.Restart();//شروع ثبت زمان
                      result = IntSimdProcessor.HWAcceleratedSumFunc(intsOne, intsTwo);//اجرای تابع جمع دو آرایه در حالت سخت افزاری
                      var hwTimeMs = stopwatch.ElapsedMilliseconds;//ثبت زمان
                      hwTimesMs.Add(hwTimeMs);//افزودن زمان به لیست زمان‌های سخت افزاری
                      WriteLine($"Hareware accelerated analysis took: {hwTimeMs}ms (last value = {result.Last()}).");
                  }//پایان حلقه و چاپ نتایج
                  WriteLine("Int array addition:");
                  WriteLine($"Naive method average time:          {naiveTimesMs.Average():.##}");
                  WriteLine($"HW accelerated method average time: {hwTimesMs.Average():.##}");
                  WriteLine($"Hardware speedup:                   {naiveTimesMs.Average() / hwTimesMs.Average():P}%");
              }
              در بالا تکه کدی مربوط به تابع آزمایش اول از کلاس PerformanceTests قرار دارد و وظیفه دارد عملیات جمع دو آرایه را با استفاده از یک آرایه کمکی اعداد صحیح، هم در حالت معمولی و هم در حالت SIMD انجام دهد و زمان اجرای آنها را ثبت و نمایش دهد تا بتوانیم این زمان اجرا‌ها را با هم مقایسه کنیم.
              ساختار و روند اجرای کلیه آزمایش‌ها و توابع در کلاس PerformanceTests با یکدیگر یکسان است و از یک stopwatch یا همان کرنومتر برای محاسبه زمان اجرا استفاده شده است.
              هر کدام از این توابع یک عملیات را مورد بررسی قرار می‌دهند و هر عملیات را 3 مرتبه اجرا می‌کنند تا زمان تکرار اجرا نیز مورد مقایسه قرار گیرد.

              نام تابع ذکر شده نشان دهنده آزمایش بر روی آرایه اعداد صحیح یا همان Integer می‌باشد که شامل یک پارامتر ورودی از نوع عدد صحیح می‌باشد. این پارامتر ورودی نشان دهنده اندازه هر آرایه‌ای می‌باشد که قرار است تولید شود.  

              TestIntArrayAdditionFunctions(int testSetSize)

              در قدم اول این تابع، باید آرایه‌ها را تولید کنیم که کد آن به صورت زیر است.

              Write("Testing int array addition, generating test data...");
              var intsOne = GetRandomIntArray(testSetSize);
              var intsTwo = GetRandomIntArray(testSetSize);
              WriteLine($" done, testing...");

              ابتدا در خروجی چاپ می‌کنیم که در حال ایجاد داده‌های مربوط به آزمایش هستیم و سپس با استفاده از تابع GetRandomIntArray آرایه‌ای را ایجاد می‌کنیم و در متغیر‌های مربوطه می‌ریزیم. این تابع دارای یک پارامتر ورودی از نوع عدد صحیح است که آرایه‌ای را به طول پارامتر ورودی تولید می‌کند. این تابع در فایل Utilities.cs قرار دارد.

              در پایان تولید آرایه‌ها، اتمام تولید و ایجاد آرایه‌ها را با چاپ در خروجی اعلام میکنیم.

              سپس با معرفی دو لیست زیر می‌توانیم زمان‌های اجرا را در آنها بریزیم و در پایان، تابع میانگین این زمان‌ها را محاسبه و چاپ کنیم. لیست اول برای نگهداری زمان‌های اجرای عملیات در حالت معمولی و لیست دوم برای نگهداری زمانهای اجرای عملیات در حالت SIMD می‌باشد.

              var naiveTimesMs = new List<long>();
              var hwTimesMs = new List<long>();

              سپس با ایجاد حلقه ای از 0 تا 3 که در کل 3 مرتبه اجرا می‌شود عملیات را تکرار و زمان آن را ثبت می‌کنیم. 

              for (var i = 0; i < 3; i++)

              درون حلقه یک عملیات را در دوحالت معمولی یا ساده و SIMD اجرا می‌کنیم. قبل از اجرای عملیات اول ابتدا stopwatch را ریست می‌کنیم. با این کار زمان صفر شده و شروع به اندازه گیری می‌کند. سپس عملیات مربوط به جمع دو آرایه را در حالت معمولی که در فایل IntSimdProcessor.cs قرار دارد، فراخوانی می‌کنیم. پس از اجرای این عملیات مقدار stopwatch را به میلی ثانیه در یک متغیر ذخیره میکنیم و این مقدار را به لیست زمان‌های اجرای معمولی اضافه می‌کنیم. در نهایت نتیجه زمان اجرا را در خروجی چاپ می‌کنیم. 

              stopwatch.Restart();
              var result = IntSimdProcessor.NaiveSumFunc(intsOne, intsTwo);
              var naiveTimeMs = stopwatch.ElapsedMilliseconds;
              naiveTimesMs.Add(naiveTimeMs);
              WriteLine($"Naive analysis took:                {naiveTimeMs}ms (last value = {result.Last()}).");

              پس از اجرای عملیات در حالت ساده یا معمولی، حال نوبت همان عملیات در حالت SIMD می‌باشد. دوباره stopwatch را ریست می‌کنیم و عملیات در SIMD را اجرا کرده و بعد از آن مقدار stopwatch را درون متغیری میریزیم و آن را به لیست زمان‌های اجرای عملیات در SIMD اضافه می‌کنیم و در نهایت نتیجه زمان اجرا را در خروجی چاپ می‌کنیم. 

              stopwatch.Restart();
              result = IntSimdProcessor.HWAcceleratedSumFunc(intsOne, intsTwo);
              var hwTimeMs = stopwatch.ElapsedMilliseconds;
              hwTimesMs.Add(hwTimeMs);
              WriteLine($"Hareware accelerated analysis took: {hwTimeMs}ms (last value = {result.Last()}).");

              پس از اجرای حلقه، حال نوبت به نمایش نتیجه میانگین زمان‌ها در خروجی است. ابتدا میانگین زمان‌های اجرا در حالت ساده یا معمولی را که به میلی ثانیه است را در خروجی چاپ می‌کنیم. بعد از آن میانگین زمان‌های اجرا در حالت SIMD را در خروجی چاپ می‌کنیم و در آخر سرعت زمان اجرا در حالت SIMD را نسبت به حالت معمولی به درصد چاپ می‌کنیم. 

              WriteLine($"Naive method average time:          {naiveTimesMs.Average():.##}");
              WriteLine($"HW accelerated method average time: {hwTimesMs.Average():.##}");
              WriteLine($"Hardware speedup:                   {naiveTimesMs.Average() / hwTimesMs.Average():P}%");

              در این مقاله تنها به توضیحی در مورد این آزمایش اکتفا می‌کنیم. لازم به ذکر است که دیگر آزمایش‌ها نیز دقیقا ساختاری مشابه این آزمایش را دارند و تنها عملیات اجرا در آنها متفاوت است. در کلاس PerformanceTests توضیحات لازم مربوط به هر آزمایش و تابع داده شده است و می‌توانید با مراجعه به کد برنامه آنها را مورد بررسی قرار دهید.

              برای اجرای تمامی آزمایش‌ها، کلیه توابع نوشته شده در کلاس PerformanceTests را در کلاس Program و در تابع Main که تابع شروع کننده برنامه می‌باشد، پس از بررسی وضعیت نوع‌های داده‌ای قرار می‌دهیم.

              تصویر مربوط به اجرای کامل برنامه را می‌توانید مشاهده می‌کنید. 

              این جدول بر اساس یک بار اجرای برنامه در سیستم من ترسیم شده است و اجرای برنامه در سیستم‌های مختلف خروجی‌های متفاوتی را دارد. لازم به ذکر است که اندازه آرایه‌ها بسیار بزرگ است و این نتایج با آرایه‌هایی به طول بیش از هزاران هزار عنصر می‌باشد.

              زمان‌ها در جدول به میلی ثانیه می‌باشد.

              ردیف

              عملیات

              دور اول

              دور دوم

              دور سوم

              میانگین حالت ساده

              میانگین حالت SIMD

              درحالت ساده

              درحالت SIMD

              درحالت ساده

              درحالت SIMD

              درحالت ساده

              درحالت SIMD

              1

              جمع دو آرایه با استفاده از یک آرایه کمکی در اعداد صحیح

              157

              131

              128

              131

              128

              138

              137.67

              133.33

              2

              جمع دو آرایه بدون استفاده از آرایه کمکی در اعداد float

              122

              133

              99

              99

              99

              93

              106.67

              108.33

              3

              جمع دو آرایه بدون استفاده از آرایه کمکی در اعداد صحیح

              83

              73

              86

              88

              78

              81

              82.33

              80.67

              4

              جمع دو آرایه اول ورودی - مجموع در آرایه سوم ریخته می‌شود - در اعداد صحیح کوچک بدون علامت

              58

              63

              50

              48

              58

              46

              55.33

              52.33

              5

              جمع دو آرایه بدون استفاده از آرایه کمکی در اعداد صحیح کوچک بدون علامت

              55

              40

              53

              36

              53

              46

              53.67

              40.67

              6

              بدست آوردن کمترین و بیشترین مقدار در یک آرایه اعداد صحیح

              91

              36

              91

              39

              90.67

              38

              90.66

              38

              7

              بدست آوردن کمترین و بیشترین مقدار در یک آرایه اعداد صحیح کوچک بدون علامت

              90

              20

              89

              19

              88

              18

              89

              19

              8

              جمع عناصر آرایه ورودی و ذخیره مجموع آنها در یک متغیر کمکی

              33

              309

              32

              263

              31

              291

              32

              287.67

              9

              جمع عناصر آرایه ورودی و ذخیره مجموع آنها در یک متغیر کمکی بدون بررسی سرریز

              30

              13

              29

              13

              30

              12

              29.67

              12.67

              10

              محاسبه میانگین و بدست آوردن کمترین و بیشترین مقدار در آرایه اعداد صحیح کوچک بدون علامت

              89

              50

              90

              51

              90

              49

              89.57

              50



              سورس کامل برنامه را که شامل تغییراتی در توابع برای بهبود و اضافه شدن کامنت برای فهم بیشتر کدها می‌باشد، در زیر می‌توانید دریافت کنید: 
                 TestSIMD.zip  

              نظرات مطالب
              ASP.NET MVC #4
              قرار نیست برای هر کنترلر و View ایی که تعریف می‌شود، در فایل global.asax.cs تغییری انجام شود. route پیش فرض آن در اکثر موارد جوابگو است.
              مگر اینکه بخواهید redirect‌های خاصی را تعریف کنید. مثلا لینک‌های تگ‌ها در بلاگر به این شکل بودند و هستند : http://site/search/label/tagname
              برای اینکه این نوع لینک‌های رسیده و bookmark شده یا ثبت شده در سایت‌های مختلف به آدرس جدید http://site/tag/name هدایت شوند، می‌شود یک route برای آن نوشت:
               routes.MapRoute(
                              "old_blogger", // Route name
                              "search/label/{name}", // URL with parameters
                              new { controller = "Tag", action = "Index", name = UrlParameter.Optional } // Parameter defaults
                          );
              به این ترتیب به صورت خودکار آدرس‌های قدیمی به آدرس‌های جدید (کنترلر Tag سایت) نگاشت خواهند شد و از این دست موارد. خلاصه فایل global.asax.cs نیاز به آنچنان تغییری ندارد و پیش فرض‌های آن در بسیاری از موارد کافی هستند. 
              مطالب
              آپلود فایل توسط فرم‌های پویای jqGrid
              پیشنیازها
              Ajax.BeginForm و ارسال فایل به سرور در ASP.NET MVC
              فعال سازی و پردازش صفحات پویای افزودن، ویرایش و حذف رکوردهای jqGrid در ASP.NET MVC
              فرمت کردن اطلاعات نمایش داده شده به کمک jqGrid در ASP.NET MVC
              استفاده ازExpressionها جهت ایجاد Strongly typed view در ASP.NET MVC


              فرم‌های پویای jqGrid نیز به صورت Ajax ایی به سرور ارسال می‌شوند و اگر نوع عناصر تشکیل دهنده‌ی آن‌ها file تعیین شوند، قادر به ارسال این فایل‌ها به سرور نخواهند بود. در ادامه نحوه‌ی یکپارچه سازی افزونه‌ی AjaxFileUpload را با فرم‌های jqGrid بررسی خواهیم کرد.


              تغییرات فایل Layout برنامه

              در اینجا دو فایل جدید ajaxfileupload.js و jquery.blockUI.js به مجموعه‌ی فایل‌های تعریف شده اضافه شده‌اند:
              <!DOCTYPE html>
              <html>
              <head>
                  <meta charset="utf-8" />
                  <meta name="viewport" content="width=device-width, initial-scale=1.0">
                  <title>@ViewBag.Title - My ASP.NET Application</title>
              
                  <link href="~/Content/themes/base/jquery.ui.all.css" rel="stylesheet" />
                  <link href="~/Content/jquery.jqGrid/ui.jqgrid.css" rel="stylesheet" />
                  <link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
              </head>
              <body>
                  <div>
                      @RenderBody()
                  </div>
              
                  <script src="~/Scripts/jquery-1.7.2.min.js"></script>
                  <script src="~/Scripts/jquery-ui-1.8.11.min.js"></script>
                  <script src="~/Scripts/i18n/grid.locale-fa.js"></script>
                  <script src="~/Scripts/jquery.jqGrid.src.js"></script>
                  <script src="~/Scripts/ajaxfileupload.js"></script>
                  <script src="~/Scripts/jquery.blockUI.js"></script>
              
                  @RenderSection("Scripts", required: false)
              </body>
              </html>
              از فایل jquery.blockUI.js برای نمایش صفحه‌ی منتظر بمانید تا فایل آپلود شود، استفاده خواهیم کرد.
               PM> Install-Package jQuery.BlockUI



              نکته‌ای در مورد واکنشگرا کردن jqGrid

              اگر می‌خواهید عرض jqGrid به تغییرات اندازه‌ی مرورگر پاسخ دهد، تنها کافی است تغییرات ذیل را اعمال کنید:
              <div dir="rtl" id="grid1" style="width:100%;" align="center">
                  <div id="rsperror"></div>
                  <table id="list" cellpadding="0" cellspacing="0"></table>
                  <div id="pager" style="text-align:center;"></div>
              </div>
              
              
                  <script type="text/javascript">
                      $(document).ready(function () {
              
                          // Responsive jqGrid
                          $(window).bind('resize', function () {
                              var targetContainer = "#grid1";
                              var targetGrid = "#list";
              
                              $(targetGrid).setGridWidth($(targetContainer).width() - 2, true);
                          }).trigger('resize');
              
              
                          $('#list').jqGrid({
                              caption: "آزمایش هفتم",
                              /// .....
                          }).navGrid(
                              /// .....
                          ).jqGrid('gridResize', { minWidth: 400, minHeight: 150 });
                      });
                  </script>
              در اینجا به تغییرات resize صفحه گوش فرا داده شده و سپس به کمک متد توکار setGridWidth، به صورت پویا اندازه‌ی عرض jqGrid تغییر خواهد کرد.
              همچنین اگر می‌خواهید کاربر بتواند اندازه‌ی گرید را دستی تغییر دهد، به انتهای تعاریف گرید، تعریف متد gridResize را نیز اضافه کنید.




              نحوه‌ی تعریف ستونی که قرار است فایل آپلود کند

                              colModel: [
                                  {
                                      name: '@(StronglyTyped.PropertyName<Product>(x=>x.ImageName))',
                                      index: '@(StronglyTyped.PropertyName<Product>(x => x.ImageName))',
                                      align: 'center', width: 220,
                                      editable: true,
                                      edittype: 'file',
                                      formatter: function (cellvalue, options, rowObject) {
                                          return "<img src='/images/" + cellvalue + "?rnd=" + new Date().getTime() + "' />";
                                      },
                                      unformat: function (cellvalue, options, cell) {
                                          return $('img', cell).attr('src').replace('/images/', '');
                                      }
                                  }
                              ],
              edittype ستونی که قرار است فایل آپلود کند، باید به file تنظیم شود. همچنین چون در اینجا این فایل آپلودی، تصویر یک محصول است، از formatter برای تبدیل مسیر فایل به تصویر و از unformat برای بازگشت این مسیر به مقدر اصلی آن استفاده خواهیم کرد. از unformat برای حالت ویرایش اطلاعات استفاده می‌شود. از formatter برای تغییر اطلاعات دریافتی از سرور به فرمت دلخواهی در سمت کلاینت می‌توان کمک گرفت.
              Rnd اضافه شده به انتهای آدرس تصویر، جهت جلوگیری از کش شدن آن تعریف شده‌است.



              کتابخانه‌ی JqGridHelper

              در قسمت‌های قبل مطالب بررسی jqGrid یک سری کلاس مانند JqGridData برای بازگشت اطلاعات مخصوص jqGrid و یا JqGridRequest برای دریافت پارامترهای ارسالی توسط آن به سرور، تهیه کردیم؛ به همراه کلاس‌هایی مانند جستجو و مرتب سازی پویای اطلاعات.
              اگر این کلاس‌ها را از پروژه‌ها و مثال‌های ارائه شده خارج کنیم، می‌توان به کتابخانه‌ی JqGridHelper رسید که فایل‌های آن در پروژه‌ی پیوست موجود هستند.
              همچنین در این پروژه، کلاسی به نام StronglyTyped با متد PropertyName جهت دریافت نام رشته‌ای یک خاصیت تعریف شده‌است. گاهی از اوقات این تنها چیزی است که کدهای سمت کلاینت، جهت سازگار شدن با Refactoring و Strongly typed تعریف شدن نیاز دارند و نه ... محصور کننده‌هایی طویل و عریض که هیچگاه نمی‌توانند تمام قابلیت‌های یک کتابخانه‌ی غنی جاوا اسکریپتی را به همراه داشته باشند.
              با کمی جستجو، برای jqGrid نیز می‌توانید از این دست محصور کننده‌هارا پیدا کنید اما ... هیچکدام کامل نیستند و دست آخر مجبور خواهید شد در بسیاری از موارد مستقیما JavaScript نویسی کنید.



              یکپارچه سازی افزونه‌ی AjaxFileUpload با فرم‌های jqGrid

              پس از این مقدمات، ستون ویژه‌ی actions که inline edit را فعال می‌کند، چنین تعریفی را پیدا خواهد کرد:
                              colModel: [
                                  {
                                      name: 'myac', width: 80, fixed: true, sortable: false,
                                      resize: false, formatter: 'actions',
                                      formatoptions: {
                                          keys: true,
                                          afterSave: function (rowid, response) {
                                              doInlineUpload(response, rowid);
                                          },
                                          delbutton: true,
                                          delOptions: {
                                              url: "@Url.Action("DeleteProduct","Home")"
                                          }
                                      }
                                  }
                              ],
              در اینجا afterSave اضافه شده‌است تا کار ارسال فایل به سرور را در حالت ویرایش inline فعال کند.
              و ویژگی‌های قسمت‌های edit، add و delete فرم‌های پویای jqGrid باید به نحو ذیل تغییر کنند:
                          $('#list').jqGrid({
                              caption: "آزمایش هفتم",
                              // ....
                          }).navGrid(
                              '#pager',
                              //enabling buttons
                              { add: true, del: true, edit: true, search: false },
                              //edit option
                              {
                                  width: 'auto',
                                  reloadAfterSubmit: true, checkOnUpdate: true, checkOnSubmit: true,
                                  beforeShowForm: function (form) {
                                      centerDialog(form, $('#list'));
                                  },
                                  afterSubmit: doFormUpload,
                                  closeAfterEdit: true
                              },
                              //add options
                              {
                                  width: 'auto', url: '@Url.Action("AddProduct","Home")',
                                  reloadAfterSubmit: true, checkOnUpdate: true, checkOnSubmit: true,
                                  beforeShowForm: function (form) {
                                      centerDialog(form, $('#list'));
                                  },
                                  afterSubmit: doFormUpload,
                                  closeAfterAdd: true
                              },
                              //delete options
                              {
                                  url: '@Url.Action("DeleteProduct","Home")',
                                  reloadAfterSubmit: true
                              }).jqGrid('gridResize', { minWidth: 400, minHeight: 150 });
              با اکثر این تنظیمات در مطلب «فعال سازی و پردازش صفحات پویای افزودن، ویرایش و حذف رکوردهای jqGrid در ASP.NET MVC» آشنا شده‌اید. تنها قسمت جدید آن شامل رویدادگردان afterSubmit است. در اینجا است که افزونه‌ی AjaxFileUpload فعال شده و سپس اطلاعات المان فایل را به سرور ارسال می‌کند.
              افزونه‌ی AjaxFileUpload پس از ارسال اطلاعات عناصر غیر فایلی فرم، باید فعال شود. به همین جهت است که از رویداد afterSubmit در حالت نمایش فرم‌های پویا و رویداد afterSave در حالت ویرایش inline استفاده کرده‌ایم.
              در ادامه تعاریف متدهای doInlineUpload و doUpload بکار گرفته شده در رویداد afterSubmit را مشاهده می‌کنید:
                      function doInlineUpload(response, rowId) {
                          return doUpload(response, null, rowId);
                      }
              
                      function doFormUpload(response, postdata) {
                          return doUpload(response, postdata, null);
                      }
              
                      function doUpload(response, postdata, rowId) {
                          // دریافت خروجی متد ثبت اطلاعات از سرور
                          // و استفاده از آی دی رکورد ثبت شده برای انتساب فایل آپلودی به آن رکورد
                          var result = $.parseJSON(response.responseText);
                          if (result.success === false)
                              return [false, "عملیات ثبت موفقیت آمیز نبود", result.id];
              
                          var fileElementId = '@(StronglyTyped.PropertyName<Product>(x=>x.ImageName))';
                          if (rowId) {
                              fileElementId = rowId + "_" + fileElementId;
                          }
              
                          var val = $("#" + fileElementId).val();
                          if (val == '' || val === undefined) {
                              // فایلی انتخاب نشده
                              return [false, "لطفا فایلی را انتخاب کنید", result.id];
                          }
              
                          $('#grid1').block({ message: '<h4>در حال ارسال فایل به سرور</h4>' });
                          $.ajaxFileUpload({
                              url: "@Url.Action("UploadFiles", "Home")", // مسیری که باید فایل به آن ارسال شود
                              secureuri: false,
                              fileElementId: fileElementId, // آی دی المان ورودی فایل
                              dataType: 'json',
                              data: { id: result.id }, // اطلاعات اضافی در صورت نیاز
                              complete: function () {
                                  $('#grid1').unblock();
                              },
                              success: function (data, status) {
                                  $("#list").trigger("reloadGrid");
                              },
                              error: function (data, status, e) {
                                  alert(e);
                              }
                          });
              
                          return [true, "با تشکر!", result.id];
                      }
              امضای رویدادگردان‌های afterSubmit و afterSave یکی نیست. به همین جهت دو متد اضافی به جای یک متد doUpload مورد استفاده قرار گرفته‌اند.
              متد doUpload توسط پارامتر response، اطلاعات بازگشتی پس از ذخیره سازی متداول اطلاعات فرم را دریافت می‌کند. برای مثال ابتدا اطلاعات معمولی یک محصول در بانک اطلاعاتی ذخیره شده و سپس id آن به همراه یک خاصیت به نام success از طرف سرور بازگشت داده می‌شوند.
              اگر success مساوی true بود، ادامه‌ی کار آپلود فایل انجام خواهد شد. در اینجا ابتدا بررسی می‌شود که آیا فایلی از طرف کاربر انتخاب شده‌است یا خیر؟ اگر خیر، یک پیام اعتبارسنجی سفارشی به او نمایش داده خواهد شد.
              خروجی متد doUpload حتما باید به شکل یک آرایه سه عضوی باشد. عضو اول آن true و false است؛ به معنای موفقیت یا عدم موفقیت عملیات. عضو دوم پیام اعتبارسنجی سفارشی است و عضو سوم، Id ردیف.
              در ادامه افزونه‌ی jQuery.BlockUI فعال می‌شود تا ارسال فایل به سرور را به کاربر گوشزد کند.
              سپس فراخوانی متداول افزونه‌ی ajaxFileUpload را مشاهده می‌کنید. تنها نکته‌ی مهم آن فراخوانی متد reloadGrid در حالت success است. به این ترتیب گرید را وادار می‌کنیم تا اطلاعات ذخیره شده در سمت سرور را دریافت کرده و سپس تصویر را به نحو صحیحی نمایش دهد.



              کدهای سمت سرور آپلود فایل

                      [HttpPost]
                      public ActionResult AddProduct(Product postData)
                      {
                          // ...
                          return Json(new { id = postData.Id, success = true }, JsonRequestBehavior.AllowGet);
                      }
              
                      [HttpPost]
                      public ActionResult EditProduct(Product postData)
                      {
                          // ...
                          return Json(new { id = postData.Id, success = true }, JsonRequestBehavior.AllowGet);
                      }
              
              
                      // todo: change `imageName` according to the form's file element name
                      [HttpPost]
                      public ActionResult UploadFiles(HttpPostedFileBase imageName, int id)
                      {
                          // ....
                          return Json(new { FileName = product.ImageName }, "text/html", JsonRequestBehavior.AllowGet);
                      }
              در اینجا تنها نکته‌ی مهم، خروجی‌های JSON این متدها هستند.
              در حالت‌های Add و Edit، نیاز است id رکورد ثبت شده بازگشت داده شود. این id در سمت کلاینت توسط پارامتر response دریافت می‌شود. از آن در افزونه‌ی ارسال فایل به سرور استفاده خواهیم کرد. اگر به متد UploadFiles دقت کنید، این id را دریافت می‌کند. بنابراین می‌توان یک ربط منطقی را بین فایل ارسالی و رکورد متناظر با آن برقرار کرد.
              Content type مقدار بازگشتی از متد UploadFiles حتما باید text/html باشد (افزونه‌ی ارسال فایل‌ها، اینگونه کار می‌کند).


              کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید:
              jqGrid07.zip