مطالب
توضیح مثالی از 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  

نظرات مطالب
معرفی کتابخانه PdfReport
با تشکر؛ اگر بخواهیم از pdf report در برنامه‌های سازمانی استفاده کنیم با هر بار درخواست گزارش ،  یک نسخه فایل pdf در کلاینت‌ها دانلود می‌شود  ، و این به مرور باعث میشه فایل‌های زیادی در هر کلاینت بخاطر گزارش‌های مورد درخواستش ذخیره بشه . آیا این روش کار من  اشکالی داره؟ در حالت ویندوزی میشه یک فایل رو بازنویسی کرد و در پوشه اجرائی برنامه فقط یک فایل گزارش تولید میشه ولی در محیط وب  نتونستم راه حلی پیدا کنم ، اگر امکانش هست راهنمائی بفرمائید.
مطالب
اعمال متداول با select (یا همان DropDownList / ComboBox) توسط jQuery

در این مقاله مروری خواهیم داشت بر نحوه‌ی انجام یک سری از اعمال متداول با استفاده از jQuery بر روی select استاندارد HTML. قبل از شروع به عنوان قالب کار، صفحه ساده‌ی زیر را در نظر بگیرید که از یک DropDownList استاندارد ASP.Net تشکیل شده است:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="select.aspx.cs" Inherits="TestJQueryAjax.select" %>

<!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></title>
<script src="js/jquery.js" type="text/javascript"></script>

<script type="text/javascript">
//...
</script>

</head>
<body>
<form id="form1" runat="server">
<asp:DropDownList ID="ddlTest" runat="server">
<asp:ListItem Value="1">آیتم یک</asp:ListItem>
<asp:ListItem Value="2">آیتم دو</asp:ListItem>
<asp:ListItem Value="3">آیتم سه</asp:ListItem>
<asp:ListItem Value="4">آیتم چهار</asp:ListItem>
<asp:ListItem Value="5">آیتم پنج</asp:ListItem>
</asp:DropDownList>
</form>
</body>
</html>
1) بدست آوردن value آیتم انتخاب شده

$("#<%=ddlTest.ClientID %>").val()
2) بدست آوردن text آیتم انتخاب شده

$("#<%=ddlTest.ClientID %> option:selected").text()
3) عکس العمل نشان دادن به انتخاب آیتم‌های مختلف

<script type="text/javascript">
$(document).ready(function() {
$("#<%=ddlTest.ClientID %>").change(function() {
alert($("#<%=ddlTest.ClientID %>").val());
});
});
</script>
4) انتخاب یک آیتم از طریق برنامه نویسی

$("#<%=ddlTest.ClientID %>").val(2);
5) افزودن یک آیتم به لیست از طریق برنامه نویسی

$("<option value='6'>آیتم شش</option>").appendTo("#<%=ddlTest.ClientID %>");

6) خالی و حذف کردن تمامی آیتم‌ها

$("#<%=ddlTest.ClientID %>").html("");

و برای تکمیل بحث می‌توان به این برگه مرجع مراجعه کرد:
jQuery – Select element cheat sheet


مطالب
آموزش LINQ بخش دوم
سبک‌های مختلف نوشتن Query در LINQ
تعریف Query:
عبارتی که اطلاعات را از منبع داده، بازیابی می‌کند، پرس و جو یا Query می‌گوییم. بطور کلی عملیات پرس و جو شامل سه بخش زیر می‌شود:
1- مشخص کردن منبع داده
2- ایجاد پرس و جو (Query)
3- اجرای پرس و جو
// The Three Parts of a LINQ Query:
//  1. منبع داده
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
// 2. ایجاد پرس و جو
// numQuery is an IEnumerable<int>
var numQuery =
    from num in numbers
    where (num % 2) == 0
    select num;
// 3. اجرای پرس و جو
foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}
شکل زیر توصیفی از کد‌های بالا می‌باشد :
 
دو سبک برای نوشتن عبارت‌های جستجو در LINQ وجود دارند :
 1- Fluent Style 
 2- Query Expression Style یا Query Syntax
سبک Fluent از متد‌های الحاقی برای عملیات پرس و جو استفاده می‌کند. در کلیه‌ی کدهای بخش اول این سری آموزشی از سبک Fluent استفاده شده است.
در کلاس‌های زیر متد‌های استاتیک مختلفی برای عملیات بر روی توالی‌ها ارائه شده‌اند:
 • System.Linq.Enumerable
 • System.Linq.Queryable
 • System.Linq.ParallelEnumarable
بطور کلی هر نمونه‌ای که اینترفیس <IEnumerable<Tsource را پیاده سازی کرده باشد می‌تواند از این متدهای الحاقی استفاده کند.
عملگرهای جستجو به دو صورت تکی و زنجیره‌ای برای ایجاد پرس و جو‌های پیچیده مورد استفاده قرار می‌گیرند.

پرس و جوی‌های زنجیره‌ای
در ابتدا کلاسی به نام Ingredient را به شکل زیر تعریف می‌کنیم (این کلاس نشان دهنده‌ی نام مواد غذایی و کالری آنهاست):
class Ingredient
{
    public string Name { get; set; }
    public int Calories { get; set; }
}
لیستی از مواد غذایی را ایجاد می‌کنیم:
Ingredient[] ingredients =
{
   new Ingredient {Name = "Suger", Calories = 500},
   new Ingredient {Name = "Egg", Calories = 100 },
   new Ingredient {Name = "Milk", Calories = 150 },
   new Ingredient {Name = "Flour", Calories = 50 },
   new Ingredient {Name = "Butter", Calories = 200 }
};
حال می‌خواهیم بصورت زنجیره‌ای از عملگر‌های پرس و جوی Where,OrderBy,Select استفاده کنیم:
Ingredient[] ingredients =
{
   new Ingredient {Name = "Suger", Calories = 500},
   new Ingredient {Name = "Egg", Calories = 100 },
   new Ingredient {Name = "Milk", Calories = 150 },
   new Ingredient {Name = "Flour", Calories = 50 },
   new Ingredient {Name = "Butter", Calories = 200 }
};

IEnumerable<string> highCalories =
ingredients.Where(x => x.Calories >= 150)
  .OrderBy(x => x.Name)
  .Select(x => x.Name);

foreach (var item in highCalories)
{
   Console.WriteLine(item);
}
خروجی کد بالا به شکل زیر است :
Butter
Milk
Suger
نمودار زیر نحوه‌ی عملکرد عملگرهای پرس و جو را نشان می‌دهد. هر عملگر بر روی توالی خروجی عملگر قبلی کار می‌کند. توجه کنید که توالی ورودی از نوع <IEnumerable<Ingredient می‌باشد و توالی خروجی تولید شده از نوع <IEnumerable<string است.
در این مثال عملگر‌های پرس و جو بر روی توالی ورودی عمل می‌کنند تا به دستور Select برسند. دستور Select هر عنصر را به یک رشته تبدیل می‌کند. این عملیات را Projection می‌گویند.











عبارت Lambda نوشته شده‌ی در بخش Select مشخص می‌کند که خروجی بر اساس چه خصوصیتی از توالی ورودی باشد. در اینجا نام عناصر به صورت رشته در خروجی ظاهر می‌شوند.


سبک Query Expression (عبارت‌های پرس و جو) 

Query Expression یک گرامر زیبا و روان برای نوشتن پرس و جو‌ها را ارائه می‌دهد. در مثال زیر از سبک Query Expression استفاده کرده‌ایم:

Ingredient[] ingredients =
{
    new Ingredient {Name = "Suger", Calories = 500},
    new Ingredient {Name = "Egg", Calories = 100},
    new Ingredient {Name = "Milk", Calories = 150},
    new Ingredient {Name = "Flour", Calories = 50},
    new Ingredient {Name = "Butter", Calories = 200}
};

IEnumerable<string> highCalories =
    from i in ingredients
    where i.Calories >= 150
    orderby i.Name
    select i.Name;

foreach (var item in highCalories)
{
    Console.WriteLine(item);
}

خروجی کد بالا با خروجی کد به سبک Fluent  یکسان است:

Butter
Milk
Suger

همانطور که می‌بینید ترتیب عملیات همانند روش قبل است. عبارت‌های پرس و جوی (from,where,orderby,select) به ترتیب با اصلاح توالی ورودی و تحویل آن به عبارت جستجوی بعدی کار را انجام می‌دهند.

عبارت جستجوی بالا با کلمه‌ی کلیدی from آغاز شده است. هدف from دو چیز است:

1- مشخص کردن توالی ورودی (منبع داده)

2- معرفی متغیر Range  (مشخص کردن عنصر مورد نظر در منبع داده)

متغیر Range همچون متغیر شمارنده در حلقه هاست. 


در ادامه این سری آموزشی درباره متغیر Range بصورت کاملتری بحث خواهیم کرد.   

نظرات مطالب
Bundling and Minifying Inline Css and Js
سلام ، مرسی از مطالب خوبتون
بحث Optimization در لینک زیر کاملتر بررسی شده، البته مطلب شما برایم تازگی داشت.
http://go.microsoft.com/fwlink/?LinkId=254725

نکاتی که میشه گفت
در وب فرم هم قابل استفاده است
برای single download کردن فایل‌های css و js دو روش وجود دارد:
1. تنظیم debug=false در بخش compilation در فایل Web.config
2. نوشتن کد زیر در کلاسی که باندل‌های خود را در bundleTable درج می‌کنید.
BundleTable.EnableOptimizations = true
برای نصب آن با NuGet Package، در کنسول عبارت زیر را وارد کنید
 install-package Microsoft.AspNet.Web.Optimization
که با نصب آن، 4 کتابخانه به reference‌های پروژه اضافه می‌شود.
تفاوتی که در script‌ها ایجاد می‌کند می‌توان به حذف کردن description ها، تغییر در variable‌ها ، و min کردن js‌های شما اشاره کرد.
موفق باشید. 
مطالب
قراردادهای کوئری نویسی در OData و WCF Data Service - قسمت اول
قبل از اینکه با کاربرد‌های OData بیشتر آشنا شوید می‌بایست قراردادهای کوئری نویسی با استفاده از آدرس وب سرویس را فراگیرید. در سمت گیرنده WCF Data Service زمانی که شما یک آدرس وب سرویس را به پروژه خود اضافه می‌نمایید مدل‌ها و روابط موجودیت‌ها بصورت خودکار تولید شده و دیگر لازم نیست از کوئری نویسی با آدرس وب سرویس استفاده نمایید و به جای آن از LINQ براحتی می‌توانید داده‌های خود را جستجو نمایید.اما اگر بخواهید وب سرویس را در بستر‌های دیگر یا در سمت گیرنده وب استفاده نمایید آشنایی با کوئری نویسی به شما امکان جستجو و دسترسی به داده‌های مدنظرتان را می‌دهد گرچه کتابخانه‌های OData موجود این امکان را آسان‌تر می‌سازد.

اجزای OData Url
OData Url شامل سه جزء می‌باشد که ترکیب این سه جرء امکان کوئری نویسی را فراهم می‌سازد
  1. Service Root Url یا ریشه آدرس سرویس که آدرس و نام وب سرویس را مشخص می‌سازد.
  2. Resource Path یا مسیر منابع که امکان دسترسی به منابع موجود وب سرویس را فراهم می‌سازد.
  3. Query Options یا گزینه‌های کوئری که امکان کوئری نویسی با استفاده از عملگرها و پارامترهای گرامر OData را فراهم می‌سازد.
در زیر این اجزا بهتر نشان داده شده است:
http://services.odata.org/OData/OData.svc/Category(1)/Products?$top=2&$orderby=name
\_______________________________________/ \__________________/ \__________________/
               |                                |                    |
         service root URL                  resource path        query options
مسیر منابع در OData
برای دسترسی به موجودیت‌های سرویس در قسمت مسیر منابع با نوشتن نام آن موجودیت این امکان فراهم می‌شود مثلا برای دسترسی به موجودیت Prodacts بعد از نام وب سرویس از یک /  استفاده می‌نامیم که Backslash برای جدا کردن موجودیت‌ها استفاده می‌شود
http://services.odata.org/OData/OData.svc/Products
بروزرسانی:حال اگر بخواهیم به Supplier‌های Product با استفاده از کلید موجودیت به یک product دسترسی یابیم. ( بعد Id و بعد ) برای product و دوباره / و Supplier را می‌نویسیم
http://services.odata.org/OData/OData.svc/Products(1)/Supplier
حتی امکان دسترسی در توابع موجودیت‌ها نیز میسر است 
http://services.odata.org/OData/OData.svc/Products/MostExpensive
اگر بخواهیم پارامتری را مقدار دهی نماییم بصورت زیر عمل می‌کنیم
http://services.odata.org/OData/OData.svc/GetProductsByCategoryId(categoryId=2)
برای دسترسی به property های موجودیت‌ها بصورت زیر عمل می‌کنیم 
http://services.odata.org/OData/OData.svc/Products(1)/Name
گزینه‌های کوئری
OData پنج عملگر دارد که امکان دستکاری موجودیت‌ها را فراهم می‌سازد
  1. filter$ عناصر برگشتی را محدود می‌سازد
  2. orderby$ امکان مرتب سازی عناصر برگشتی را فراهم می‌سازد
  3. skip$ امکان گذشت از تعدادی عناصر را از ابتدای عناصر فراهم می‌سازد
  4. top$ تعداد عناصر برگشتی را محدود می‌سازد
  5. expand$ امکان برگشت محتوای وابسته به عناصر برگشتی را فراهم می‌سازد
در زیر مثال‌های از این گزینه‌ها آورده شده است
//filter
http://services.odata.org/OData/OData.svc/Products?$filter=Name eq 'Milk'
//orderby
http://services.odata.org/OData/OData.svc/Products?$orderby=Name
//skip
http://services.odata.org/OData/OData.svc/Products?$skip=5
//top
http://services.odata.org/OData/OData.svc/Products?$top=10
//expand
http://services.odata.org/OData/OData.svc/Products?$expand=Category
ادامه دارد...
مطالب
تولید پویای ستون‌ها در PdfReport
همانطور که در نکته انتهای قسمت قبل «کار با بانک‌های اطلاعاتی مختلف در PdfReport» عنوان شد، ذکر قسمت MainTableColumns و تمام تعاریف مرتبط با آن در PdfReports اختیاری است. برای تهیه یک گزارش توسط PdfReport فقط کافی است تا منبع داده را جهت تولید ستون‌های گزارش مشخص کنید.
این مورد انعطاف پذیری زیادی را به همراه خواهد داشت؛ اما ... پس از مدتی این سؤالات مطرح می‌شوند: آیا می‌شود در این ستون‌های خودکار، فیلدهای DateTime، با تاریخ شمسی نمایش داده شوند؟ آیا امکانپذیر است که ستونهای عددی، جمع پایین صفحه داشته باشند؟ و مواردی از این دست که در مورد نحوه مدیریت این نوع ستون‌های خودکار در ادامه بحث خواهد شد.

ابتدا سورس کامل مثال جاری را در ادامه ملاحظه خواهید کرد. تقریبا همان مثال قسمت قبل است که تعاریف ستون‌های آن حذف شده است:
using System;
using PdfRpt;
using PdfRpt.Core.Contracts;
using PdfRpt.Core.Helper;
using PdfRpt.FluentInterface;

namespace PdfReportSamples.AdHocColumns
{
    public class AdHocColumnsPdfReport
    {
        public IPdfReportData CreatePdfReport()
        {
            return new PdfReport().DocumentPreferences(doc =>
            {
                doc.RunDirection(PdfRunDirection.RightToLeft);
                doc.Orientation(PageOrientation.Portrait);
                doc.PageSize(PdfPageSize.A4);
                doc.DocumentMetadata(new DocumentMetadata { Author = "Vahid", Application = "PdfRpt", Keywords = "Test", Subject = "Test Rpt", Title = "Test" });
            })
             .DefaultFonts(fonts =>
             {
                 fonts.Path(AppPath.ApplicationPath + "\\fonts\\irsans.ttf",
                            Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\verdana.ttf");
             })
             .PagesFooter(footer =>
             {
                 footer.DefaultFooter(printDate: DateTime.Now.ToString("MM/dd/yyyy"));
             })
             .PagesHeader(header =>
             {
                 header.DefaultHeader(defaultHeader =>
                 {
                     defaultHeader.ImagePath(AppPath.ApplicationPath + "\\Images\\01.png");
                     defaultHeader.Message("گزارش جدید ما");
                 });
             })
             .MainTableTemplate(template =>
             {
                 template.BasicTemplate(BasicTemplate.SilverTemplate);
             })
             .MainTablePreferences(table =>
             {
                 table.ColumnsWidthsType(TableColumnWidthType.Relative);
             })
             .MainTableDataSource(dataSource =>
             {
                 dataSource.GenericDataReader(
                    providerName: "System.Data.SQLite",
                    connectionString: "Data Source=" + AppPath.ApplicationPath + "\\data\\blogs.sqlite",
                    sql: @"SELECT [url] as 'آدرس', [name] as 'نام', [NumberOfPosts] as 'تعداد مطالب', [AddDate] as 'تاریخ ارسال'
                           FROM [tblBlogs]
                           WHERE [NumberOfPosts]>=@p1",
                    parametersValues: new object[] { 10 }
                );
             })
             .MainTableSummarySettings(summary =>
             {
                 summary.OverallSummarySettings("جمع کل");
                 summary.PreviousPageSummarySettings("نقل از صفحه قبل");
                 summary.PageSummarySettings("جمع صفحه");
             })
             .MainTableAdHocColumnsConventions(adHocColumns =>
             {
                 //We want sum of the int columns
                 adHocColumns.AddTypeAggregateFunction(
                     typeof(Int64),
                     new AggregateProvider(AggregateFunction.Sum)
                     {
                         DisplayFormatFormula = obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)
                     });

                 //We want to dispaly all of the dateTimes as ShamsiDateTime
                 adHocColumns.AddTypeDisplayFormatFormula(
                     typeof(DateTime),
                     data => { return PersianDate.ToPersianDateTime((DateTime)data); }
                 );
                 adHocColumns.ShowRowNumberColumn(true);
                 adHocColumns.RowNumberColumnCaption("ردیف");
             })
             .MainTableEvents(events =>
             {
                 events.DataSourceIsEmpty(message: "There is no data available to display.");
             })
             .Export(export =>
             {
                 export.ToExcel();
                 export.ToXml();
             })
             .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\AdHocColumnsSampleRpt.pdf"));
        }
    }
}

توضیحات:

- با توجه به اینکه تعاریف ستون‌ها را حذف کرده‌ایم و به این ترتیب ستون‌ها به صورت خودکار بر اساس فیلدهای معرفی شده در منبع داده تشکیل می‌شوند، نیاز است سر ستون‌ها را بتوانیم فارسی نمایش دهیم. به همین جهت اینبار کوئری SQL ما با استفاده از aliasها، نامی فارسی را جهت فیلدها تدارک دیده است:
SELECT [url] as 'آدرس', [name] as 'نام', [NumberOfPosts] as 'تعداد مطالب', [AddDate] as 'تاریخ ارسال'
FROM [tblBlogs]
WHERE [NumberOfPosts]>=@p1
- در مرحله بعد توسط متد MainTableAdHocColumnsConventions، یک سری روال را جهت پردازش و نمایش این ستون‌های پویا مشخص می‌کنیم. برای مثال علاقمندیم در این نوع گزارشات هم ستون خودکار ردیف ظاهر شود:
adHocColumns.ShowRowNumberColumn(true);
adHocColumns.RowNumberColumnCaption("ردیف");
همچنین هر ستونی که نوع داده‌اش DateTime بود، از طریق فرمولی که مشخص می‌کنیم، به صورت شمسی نمایش داده شود:
adHocColumns.AddTypeDisplayFormatFormula(
                     typeof(DateTime),
                     data => { return PersianDate.ToPersianDateTime((DateTime)data); }
                 );
به علاوه می‌خواهیم تمام ستون‌هایی از نوع Int64، دارای جمع پایین صفحه هم باشند:
adHocColumns.AddTypeAggregateFunction(
                     typeof(Int64),
                     new AggregateProvider(AggregateFunction.Sum)
                     {
                         DisplayFormatFormula = obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)
                     });
نوع int در بانک اطلاعاتی SQLite معادل نوع Int64 در دات نت است.
مطالب
ارتقاء به HTTP Client در Angular 4.3
عموما در برنامه‌های SPA، اطلاعات از طریق HTTP و از طرف سرور دریافت می‌شوند. از نگارش‌های ابتدایی Angular، اینکار از طریق HTTP Module آن مسیر بود و هست. در Angular 4.3 روش بهبودیافته‌ای نسبت به این روش متداول ارائه شده‌است که در ادامه تعدادی از ویژگی‌های مقدماتی آن‌را بررسی می‌کنیم.
هرچند ارتقاء به HttpClient الزامی نیست و کدهای پیشین، هنوز هم به خوبی کار می‌کنند؛ اما طراحی جدید آن شامل ویژگی‌های توکاری است که به سختی توسط HTTP Module پیشین قابل پیاده سازی هستند.


به روز رسانی وابستگی‌های پروژه

پیش از هرکاری نیاز است وابستگی‌های پروژه را به روز رسانی کرد و یکی از روش‌های ساده‌ی یافتن شماره نگارش‌های جدید بسته‌های تعریف شده‌ی در فایل package.json برنامه، استفاده از بسته‌ی npm-check-updates است:
npm install npm-check-updates -g
ncu
دستور اول، این بسته را به صورت عمومی نصب کرده و صدور دستور دوم در ریشه‌ی پروژه، سبب می‌شود تا گزارشی از آخرین به روز رسانی‌ها، نمایش داده شود (بدون هیچگونه تغییری در پروژه):


در اینجا شماره نگارش‌های جدید مشخص شده‌اند و همچنین روش سریع ارتقاء به آن‌ها نیز ذکر شده‌است. فقط کافی است دستورات ذیل را صادر کنیم تا این به روز رسانی‌ها توسط ncu انجام شوند:
ncu -a
npm update
دستور اول صرفا شماره نگارش‌های جدید را در فایل package.json، به صورت خودکار اصلاح می‌کند و دستور دوم سبب دریافت، نصب و اعمال آن‌ها خواهد گردید.


تغییرات مورد نیاز جهت معرفی ماژول HttpClient

این ماژول جدید از طریق اینترفیس HttpClientModule ارائه می‌شود. بنابراین اولین تغییر در جهت ارتقاء به نگارش 4.3، اصلاح importهای لازم است:
از:
 import { HttpModule } from '@angular/http';
به:
 import { HttpClientModule } from '@angular/common/http';

پس از آن، این HttpClientModule را به لیست imports ماژول اصلی برنامه اضافه می‌کنیم؛ تا در کل برنامه قابل دسترسی شود:
@NgModule({
    imports: [
        // ...
        HttpClientModule,
        // ...
    ],
declarations: [ ... ],
providers: [ ... ],
exports: [ ... ]
})
export class AppModule { }


تغییرات مورد نیاز در سازنده‌ها و تزریق وابستگی‌ها

پس از تغییرات فوق، دیگر دسترسی به HttpModule پیشین را نداریم. بنابراین نیاز است هر جائی را که سرویس Http به سازنده‌ی کلاسی تزریق شده‌است، یافته و به صورت ذیل تغییر دهیم:
از:
 constructor(private http: Http) { }
به:
import { HttpClient } from '@angular/common/http';
// ...
constructor(private http: HttpClient) { }


تغییرات مورد نیاز در کدهای سرویس‌ها جهت کار با HTTP Verbs

یکی از اهداف HTTP Client جدید، سادگی کار با اطلاعات دریافتی از سرور است. برای مثال در HTTP Module پیشین، روش دریافت اطلاعات از سرور به صورت ذیل است:
public get(): Observable<MyType> => {
    return this.http.get(url)
        .map((response: Response) => <MyType>response.json());
}
ابتدا درخواستی ارسال شده و سپس نتیجه‌ی آن به JSON تبدیل گشته و در آخر به نوع بازگشتی متد تبدیل می‌شود.
در HTTP Client جدید دیگر نیازی نیست تا متد ()json. فراخوانی شود. در اینجا به صورت پیش‌فرض نوع بازگشتی از سرور JSON فرض می‌شود. همچنین اکنون متدهای get/put/post و امثال آن برخلاف HTTP Client قبلی، جنریک هستند. یعنی در همینجا می‌توان نوع بازگشتی را هم مشخص کرد. به این ترتیب، قطعه کد قدیمی فوق، به کد ساده‌ی ذیل تبدیل می‌شود که در آن خبری از map و همچنین یک cast اضافی نیست:
get<T>(url: string): Observable<T> {
    return this.http.get<T>(url);
}
برای نمونه شبیه به همین نکته برای post نیز صادق است:
post<T>(url: string, body: string): Observable<T> {
   return this.http.post<T>(url, body);
}

نکته 1: در اینجا اگر خروجی از سرور، نوع دیگری را داشت، نیاز است responseType را به صورت صریحی به شکل ذیل مشخص کرد:
 getData() {
  this.http.get(this.url, { responseType: 'text' }).subscribe(res => {
       this.data = res;
  });
}
در این‌حالت خروجی متنی <Observable<string را دریافت می‌کنیم و نیازی به ذکر <get<string نیست.

نکته 2: ممکن است اطلاعات بازگشتی از سمت سرور، داخل یک فیلد محصور شده باشند:
{
  "results": [
    "Item 1",
    "Item 2",
  ]
}
در این حالت برای دسترسی به اطلاعات این فیلد می‌توان از حالت key/value بودن اشیاء جاوا اسکریپتی به شکل زیر برای دسترسی به خاصیت results استفاده کرد:
this.http.get('/api/items').subscribe(data => {
   this.results = data['results'];
});


نکاتی را که باید حین کار با یک RxJS Observable-based API در نظر داشت

این API جدید نیز همانند قبل مبتنی بر RxJS Observables است. بنابراین نکات ذیل در مورد آن نیز صادق است:
- اگر متد subscribe بر روی این observables فراخوانی نشود، اتفاقی رخ نخواهد داد.
- اگر چندین بار مشترک این observables شویم، چندین درخواست HTTP صادر می‌شوند.
- این نوع خاص از observables، تنها یک مقدار را بازگشت می‌دهند. اگر درخواست HTTP موفقیت آمیز باشد، این observables یک نتیجه را بازگشت داده و سپس خاتمه پیدا می‌کنند.
- این observables اگر در حین درخواست HTTP با خطایی مواجه شوند، سبب صدور استثنایی می‌شوند.


تغییرات مورد نیاز در کدهای سرویس‌ها جهت کار با HTTP Headers

در اینجا برای تعریف headers می‌توان به صورت ذیل عمل کرد:
import { HttpHeaders } from "@angular/common/http";

const headers = new HttpHeaders({ "Content-Type": "application/json" });
و یا به صورت fluent به شکل زیر:
 const headers = new HttpHeaders().set("Accept", "application/json").set('Content-Type', 'application/json');

سپس آن‌را به عنوان پارامتر سوم، به متدهای http ارسال می‌کنیم. یک مثال:
  updateAppProduct(id: number, item: AppProduct): Observable<AppProduct> {
    const header = new HttpHeaders({ "Content-Type": "application/json" });
    return this.http
      .put<AppProduct>(
        `${this.baseUrl}/UpdateProduct/${id}`,
        JSON.stringify(item),
        { headers: header }
      )
      .map(response => response || {});
  }

تعریف پارامتر options اینبار به صورت یک شیء دارای چندین خاصیت درآمده‌است. به همین جهت است که در اینجا یک {} را نیز مشاهده می‌کنید:
(method) HttpClient.post(url: string, body: any, options?: {
          headers?: HttpHeaders;
          observe?: "body";
          params?: HttpParams;
          reportProgress?: boolean;
          responseType?: "json";
          withCredentials?: boolean;
}): Observable<Object>

یک نکته: شیء HttpHeaders به صورت immutable طراحی شده‌است. یعنی اگر آن‌را به صورت ذیل فراخوانی کنیم:
const headers = new HttpHeaders();
headers = headers.set('Content-Type', 'application/json');
headers = headers.set('Accept', 'application/json');
headers تولیدی ... خالی خواهد بود. به همین جهت روش صحیح تشکیل آن به صورت ذیل و زنجیروار است:
 const headers = new HttpHeaders()
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
;


امکان تعریف HttpParams

اگر به شیء options در تعریف فوق دقت کنید، دارای خاصیت اختیاری params نیز هست. از آن می‌توان جهت تعریف کوئری استرینگ‌ها استفاده کرد. برای مثال درخواست ذیل:
http
  .post('/api/items/add', body, {
      params: new HttpParams().set('id', '3'),
  })
  .subscribe();
سبب تولید یک چنین URL ایی می‌گردد:
  /api/items/add?id=3

یک نکته: شیء HttpParams به صورت immutable طراحی شده‌است. یعنی اگر آن‌را به صورت ذیل فراخوانی کنیم:
const params = new HttpParams();
params.set('orderBy', '"$key"')
params.set('limitToFirst', "1");
params تولیدی ... خالی خواهد بود. به همین جهت روش صحیح تشکیل آن به صورت ذیل و زنجیروار است:
const params = new HttpParams()
.set('orderBy', '"$key"')
.set('limitToFirst', "1");
به علاوه روش تعریف ذیل نیز برای کار با HttpParams مجاز است:
 const params = new HttpParams({fromString: 'orderBy="$key"&limitToFirst=1'});


تغییرات مورد نیاز در کدهای سرویس‌ها جهت مدیریت خطاها

در اینجا اینبار خطای بازگشتی، از نوع ویژه‌ی HttpErrorResponse است که شامل اطلاعات شماره کد و متن خطای حاصل می‌باشد:
import { HttpClient, HttpHeaders, HttpErrorResponse } from "@angular/common/http";

postData() { 
  this.http.post(this.url, this.payload).subscribe( 
    res => { 
      console.log(res); 
    }, 
    (err: HttpErrorResponse) => { 
      console.log(err.error); 
      console.log(err.name); 
      console.log(err.message); 
      console.log(err.status); 

        if (err.error instanceof Error) { 
          console.log("Client-side error occured."); 
        } else { 
          console.log("Server-side error occured."); 
        }
    } 
  ); 
}


امکان سعی مجدد در اتصال توسط HTTP Client

ممکن است در اولین سعی در اتصال به سرور، خطایی رخ دهد و یا سرور در دسترس نباشد. در اینجا توسط متد retry می‌توان درخواست سعی مجدد در اتصال را صادر کرد.
برای این منظور ابتدا عملگر retry مربوط به RxJS را import می‌کنیم:
 import 'rxjs/add/operator/retry';
سپس:
http
  .get<ItemsResponse>('/api/items')
  .retry(3)
  .subscribe(...);
این کد در صورت بروز خطایی، این عملیات را سه بار تکرار می‌کند. در انتها اگر بازهم خطایی دریافت شد، این خطا را به برنامه بازگشت می‌دهد.


امکان درخواست کل Response بجای Body

اگر به امضای پارامتر اختیاری options دقت کنید، خاصیت observe آن به صورت پیش فرض به body تنظیم شده‌است. به این معنا که تنها body یک response را تبدیل به یک شیء JSON می‌کند:
(method) HttpClient.post(url: string, body: any, options?: {
          headers?: HttpHeaders;
          observe?: "body";
          params?: HttpParams;
          reportProgress?: boolean;
          responseType?: "json";
          withCredentials?: boolean;
}): Observable<Object>
اما گاهی از اوقات نیاز است تا به کل Response دسترسی داشته باشیم. در این حالت باید نوع observe را به response تنظیم کرد:
http
  .get<MyJsonData>('/data.json', {observe: 'response'})
  .subscribe(resp => {
    console.log(resp.headers.get('X-Custom-Header'));
    console.log(resp.body.someField);
  });
به این ترتیب اینبار resp از نوع <HttpResponse<MyJsonData خواهد بود که توسط آن می‌توان به خواص headers و یا body، به صورت جداگانه‌ای دسترسی یافت.


یک نکته‌ی تکمیلی: کدهای سری کار با فرم‌ها در Angular را اگر به HttpClient ارتقاء دهیم، خلاصه‌ی تغییرات آن‌ها به این صورت خواهند بود.
نظرات مطالب
ASP.NET MVC #14
سلام و خسته نباشید
من فایل _ViewStart.cshtml رو به ریشه منتقل کردم اما خطای زیر رو دریافت کردم ممنون میشم اگه راهنمایی بفرمایید.
Unable to cast object of type 'ASP._Page__ViewStart_cshtml' to type 'System.Web.WebPages.StartPage'. 
و سوال دیگر این که امکان استفاده از master page‌های تو در تو شبیه به ASP.NET Web Forms وجود دارد؟
بازخوردهای پروژه‌ها
چگونگی قرار دادن عکس به صورت Watermark(پس زمینه گزارش)
با عرض سلام و خسته نباشید، ببخشید استاد من برای ساختن گزارشات نرم افزار خودم از کتابخانه PdfReport استفاده کردم . حال می‌خواهم در یکی از گزارشاتم که به صورت یک فاکتور هست آرم شرکت را به صورت پس زمینه قرار دهم ولی قسمت Watermark رو مطالعه کردم دیدم فقط متن می‌تونم به صورت پس زمینه قرار دهم ممنون میشم راهنمایی کنید که برای این کار دقیقاً چه تنظیماتی باید انجام دهم. ممنون