نظرات مطالب
آزمایش Web APIs توسط Postman - قسمت ششم - اعتبارسنجی مبتنی بر JWT
با نکته اشاره شده در بالا و تغییرات زیر در سرویس AntiForgeryCookieService این پروژه نیز در مرورگر کوکی درج نشد.
var httpContext = _contextAccessor.HttpContext;
            httpContext.User = new ClaimsPrincipal(new ClaimsIdentity(claims, JwtBearerDefaults.AuthenticationScheme));
            var tokens = _antiforgery.GetAndStoreTokens(httpContext);
            httpContext.Response.Cookies.Append(
                  key: XsrfTokenKey,
                  value: tokens.RequestToken,
                  options: new CookieOptions
                  {
                      SameSite = SameSiteMode.Unspecified,
                      IsEssential = true,
                      Secure = httpContext.Request.IsHttps,
                      HttpOnly = false // Now JavaScript is able to read the cookie
                  });

نظرات مطالب
اثبات قانون مشاهده‌گر در برنامه نویسی
من نیاز مسئله شما را به صورت زیر نوشتم و برای هر شی Person یک مقدار متفاوت دارم.
class Program
    {
        static void Main(string[] args)
        {
            var lst = new List<Person>();
            Random rnd = new Random();
            for (int i = 0; i < 50; i++)
            {
                lst.Add(new Person(rnd));
            }


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

            Console.ReadKey();
        }
    }

    public class Person
    {
        public Person(Random rnd)
        {
            PersonId = this.GetType().Name + rnd.Next(1, int.MaxValue);          
        }

        public string PersonId { get; set; }
    }
البته من فکر میکنم اگر شما نیاز به یک ای دی منحصر به فرد دارید راه بهتر استفاده از GUID باشد.
نظرات مطالب
EF Code First #12
علت این است که یک SaveChanges در اینجا تعریف شده (ضمنا زمان اجرای این‌ها مهم نیست. بحث ما در مورد تعدد تراکنش‌ها بود). چند سطر زیر را اضافه کنید بعد از سطر آخر و مجددا تست کنید:
//...            
uow.SaveChanges();
//...            
var product3 = new Product { Name = "P300", Price = 300 };
var category2 = new Category
{
                Name = "Cat200",
                Title = "Title200",
                Products = new List<Product> { product3 }
};
categoryService.AddNewCategory(category2);
uow.SaveChanges();
من دو تراکنش مجزا رو در برنامه مطمئن و تست شده SQL Server Profiler مشاهده می‌کنم (به ازای هربار فراخوانی SaveChanges).

پاسخ به پرسش‌ها
جستجو Contains روی کلید های ترکیبی
var customUsers = new List<User>
{
    new User{FullName = "Ali Ahmadi", EyeColor = "Brown"},
    new User{FullName = "Milad Rezaei", EyeColor = "Green"}
};
var specificUsers = _context.Users.Where(x => customUsers.Any(xx=> xx.EyeColor == x.EyeColor || xx.FullName == x.FullName)).ToList();

آیا روش بالا به حل مسئله شما کمک میکنه؟ البته بجای اپراتور == میتوانید از متد Contains هم استفاده کنید که در نهایت به Is Exists در Sql Server ترجمه خواهد شد.

نظرات مطالب
وی‍‍ژگی های پیشرفته ی AutoMapper - قسمت دوم
تابع ignore باعث عدم مپ کردن اون پراپرتی میشه ولی همونطور که ذکر شده AutoMapper براساس قراردادها کار میکنه و این یه قرارداده که پراپرتی که در مبدا معادلی براش در مقصد نباشه به صورت دیفالت ignore میشه پس نیازی به ignore نیست.
Mapper.CreateMap<Kala, KalaViewModel>()
           .ForMember(des => des.Brand_Name, op => op.MapFrom(src => src.Brand.Brand_Name));

            var kalas = new[]
                {
                    new Kala
                        {
                            Kala_id = 1,
                            Brand = new Brand {Brand_id = 1, Brand_Name = "Nike"},
                            Fee_Kharid = 150000,
                            Name = "Shoes"
                        },new Kala
                        {
                            Kala_id = 2,
                            Brand = new Brand {Brand_id = 1, Brand_Name = "Nike"},
                            Fee_Kharid = 12000,
                            Name = "Shirt"
                        }
                };

         
 var kalaviewmodel = Mapper.Map<Kala[], KalaViewModel[]>(kalas);


اشتراک‌ها
مجموعه نکاتی مفید از #C

This document describes rules and recommendations for developing applications and class libraries using the C# Language. The goal is to define guidelines to enforce consistent style and formatting and help developers avoid common pitfalls and mistakes. Specifically, this document covers Naming Conventions, Coding Style, Language Usage, and Object Model Design. 

مجموعه نکاتی مفید از #C
مطالب
مروری بر قابلیت‌های جدید ES10
از زمان ارائه‌ی نگارش 72 مرورگر chrome، قابلیت‌های استفاده از ES10، میسر شده‌است. برای اینکه از شماره نگارش مرورگر خود مطلع شوید، کافیست به منوی Help و سپس بر روی گزینه‌ی About Google Chrome کلیک کنید تا شماره‌ی نسخه‌ی نصبی بر روی سیستم شما مشخص شود:


تابع ()flat : امکان یکی شدن همه آرایه‌های زیرمجموعه (sub-array) مجموعه را در یک آرایه جدید فراهم میکند و با استفاده از depth، سطح ادغام آرایه را مشخص میکنیم (عملکرد این تابع بصورت بازگشتی میباشد) و سینتکس استفاده از این تابع بشکل زیر است:
var newArray=Array.flat([depth])
بطور مثال:
var arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]


Array.flatMap  : سبب نگاشت المنت‌ها با تابع تعریف شده‌ی برای این متد میشود. سپس نتیجه در آرایه‌ای جدید برگشت داده میشود. این تابع عملکردی شبیه به انجام تابع map و سپس اجرای تابع  ()flat با عمق 1 را دارد:
// Let's say we want to remove all the negative numbers and split the odd numbers into an even number and a 1
let a = [5, 4, -3, 20, 17, -33, -4, 18]
//       |\  \  x   |  | \   x   x   |
//      [4,1, 4,   20, 16, 1,       18]

a.flatMap( (n) =>
  (n < 0) ?      [] :
  (n % 2 == 0) ? [n] :
                 [n-1, 1]
)

// expected output: [4, 1, 4, 20, 16, 1, 18]

()Object.fromEntries : یک لیست را که بصورت  key-value تعریف شده باشد، به یک آبجکت تبدیل میکند. شیء دریافتی این تابع باید از نوع Array و یا map باشد:
const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);

const obj = Object.fromEntries(entries);

console.log(obj);
// expected output: Object { foo: "bar", baz: 42 }

()String.trimStart : تابعی برای حذف کردن فضای خالی سمت چپ یک رشته می‌باشد. نام مستعار دیگر این تابع () trimStart است:
var greeting = '   Hello world!   ';

console.log(greeting);
// expected output: "   Hello world!   ";

console.log(greeting.trimStart());
// expected output: "Hello world!   ";

()String.trimEnd : تابعی برای حذف کردن فضای خالی سمت راست یک رشته می‌باشد و نام مستعار دیگر این تابع () trimRight است:
var greeting = '   Hello world!   ';

console.log(greeting);
// expected output: "   Hello world!   ";

console.log(greeting.trimEnd());
// expected output: "   Hello world!";

Optional Catch Binding : توسط آن توانایی تعریف بلاک try/catch را بدون نیاز به قرار دادن پارامتری برای catch داریم (گاهی نیاز به استفاده‌ی از متغیری که برای خطای رخ داده شده، ارائه می‌شود نیست؛ چرا باید آن را تعریف کنیم ؟!)
try {
  throw new Error('Error');
} catch (error) {
  // do stuff
}
تبدیل می‌شود به:
try {
    throw new Error('Error');
} catch { // removed parameter to catch block
    console.log('Got an error!');
}

Symbol.prototype.description: بوسیله ساختن یک متغیر از نوع  Symbol میتوانیم توضیحاتی را برای استفاده‌ی خطایابی آینده کد استفاده کنیم:
const symbol = Symbol('My Symbol!'); 

console.log(symbol.toString()); // Symbol(My Symbol!)
console.log(symbol.description); // My Symbol!

Function.prototype.toString() Revision : پیاده سازی جدیدی از تابع ()toString می‌باشد. در صورتیکه توسط یک تابع فراخوانی شود کد آن را برگشت میدهد:

// User-defined function object
// This prints "function () { console.log('My Function!'); }"
console.log(function () { console.log('My Function!'); }.toString());

// Build-in function object
// This prints "function parseInt() { [native code] }"
console.log(Number.parseInt.toString());

// Bound function object
// This prints "function () { [native code] }"
console.log(function () { }.bind(0).toString());

// Built-in callable function object
// This prints "function Symbol() { [native code] }"
console.log(Symbol.toString());

// Dynamically generated function object #1
// This prints "function anonymous() {}" (using V8 engine)
console.log(Function().toString());

// Dynamically generated function object #2
// This prints the followng (using V8 engine):
// function () { return __generator(this, function (_a) {
//     return [2 /*return*/];
// }); }
console.log(function* () { }.toString());

// This throws a TypeError: "Function.prototype.toString requires that 'this' be a Function"
Function.prototype.toString.call({});

()Array.sort : مرتب کردن عناصر یک آرایه را انجام میدهد. پیش‌تر برای مرتب سازی یک آرایه، کدی را مانند زیر داشتیم:
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
  return a - b;
});
console.log(numbers);

// [1, 2, 3, 4, 5]

 ES2015یا در

let numbers = [4, 2, 5, 1, 3];
numbers.sort((a, b) => a - b);
console.log(numbers);

// [1, 2, 3, 4, 5]
و اکنون میتوانیم با فراخوانی این تابع بدون نیاز به تابعی برای compare نمودن، مرتب سازی المنتها را انجام دهیم:
var months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months);
// expected output: Array ["Dec", "Feb", "Jan", "March"]

var array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);
// expected output: Array [1, 100000, 21, 30, 4]
مطالب
OpenCVSharp #13
تشخیص قسمت‌های مشابه تصاویر در OpenCV

در شکل زیر، دو تصویر سمت چپ و راست، اندکی با هم تفاوت دارند و در تصویر سوم، نقاط مشابه یافت شده‌ی توسط OpenCV ترسیم شده‌اند:


کدهای مثال فوق را در ذیل مشاهده می‌کنید:
var img1 = new Mat(@"..\..\Images\left.png", LoadMode.GrayScale);
Cv2.ImShow("Left", img1);
Cv2.WaitKey(1); // do events
 
var img2 = new Mat(@"..\..\Images\right.png", LoadMode.GrayScale);
Cv2.ImShow("Right", img2);
Cv2.WaitKey(1); // do events
 
 
// detecting keypoints
// FastFeatureDetector, StarDetector, SIFT, SURF, ORB, BRISK, MSER, GFTTDetector, DenseFeatureDetector, SimpleBlobDetector
// SURF = Speeded Up Robust Features
var detector = new SURF(hessianThreshold: 400); //SurfFeatureDetector
var keypoints1 = detector.Detect(img1);
var keypoints2 = detector.Detect(img2);
 
// computing descriptors, BRIEF, FREAK
// BRIEF = Binary Robust Independent Elementary Features
var extractor = new BriefDescriptorExtractor();
var descriptors1 = new Mat();
var descriptors2 = new Mat();
extractor.Compute(img1, ref keypoints1, descriptors1);
extractor.Compute(img2, ref keypoints2, descriptors2);
 
// matching descriptors
var matcher = new BFMatcher();
var matches = matcher.Match(descriptors1, descriptors2);
 
// drawing the results
var imgMatches = new Mat();
Cv2.DrawMatches(img1, keypoints1, img2, keypoints2, matches, imgMatches);
Cv2.ImShow("Matches", imgMatches);
Cv2.WaitKey(1); // do events
 
Cv2.WaitKey(0);
 
Cv2.DestroyAllWindows();
img1.Dispose();
img2.Dispose();
در ابتدا نیاز به یک تشخیص دهنده یا Detector داریم. در OpenCVSharp، الگوریتم‌ها و کلاس‌های FastFeatureDetector, StarDetector, SIFT, SURF, ORB, BRISK, MSER, GFTTDetector, DenseFeatureDetector, SimpleBlobDetector برای اینکار قابل استفاده هستند. برای مثال در اینجا از الگوریتم SURF آن استفاده شده‌است.
کار این تشخیص دهنده، تشخیص نقاط کلیدی تصاویر است. برای مثال تشخیص گوشه‌ها، لبه‌ها و غیره.
سپس اطلاعات نواحی اطراف هر نقطه‌ی کلیدی را تحت عنوان descriptors استخراج می‌کنیم.
بعد از محاسبه‌ی نقاط کلیدی هر تصویر، اینبار نیاز است این نقاط را بین دو تصویر با هم مقایسه کرد و مشابه‌ها را یافت. برای مثال الگوریتم BFMatcher یک Brute force matcher است که بر اساس اطلاعات نواحی اطراف هر نقطه‌ی کلیدی، سعی در یافتن نقاط مشابه می‌کند.
پس از یافتن نقاط مشابه، نیاز است بر اساس آن‌ها نگاشتی بین دو تصویر صورت گیرد و مشابه‌ها ترسیم شوند. متد DrawMatches این‌کار را انجام می‌شود.

اگر علاقمند هستید که با ریز جزئیات ریاضی الگوریتم‌های استفاده شده نیز آشنا شوید، سری مطالب descriptors را دنبال نمائید.


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.
مطالب دوره‌ها
آشنایی با مدل برنامه نویسی TAP
تاریخچه‌ی اعمال غیر همزمان در دات نت فریم ورک

دات نت فریم ورک، از زمان ارائه نگارش یک آن، از اعمال غیرهمزمان و API خاص آن پشتیبانی می‌کرده‌است. همچنین این مورد یکی از ویژگی‌های Win32 نیز می‌باشد. نوشتن کدهای همزمان متداول بسیار ساده است. در این نوع کدها هر عملیات خاص، پس از پایان عملیات قبلی انجام می‌شود.
        public string TestNoneAsync()
        {
            var webClient = new WebClient();
            return webClient.DownloadString("http://www.google.com");
        }
در این مثال متداول، متد DownloadString به صورت همزمان یا synchronous عمل می‌کند. به این معنا که تا پایان عملیات دریافت اطلاعات از وب، منتظر مانده و ترد جاری را قفل می‌کند. مشکل از جایی آغاز می‌شود که مدت زمان دریافت اطلاعات، طولانی باشد. چون این عملیات در ترد UI در حال انجام است، کل رابط کاربری برنامه تا پایان عملیات نیز قفل شده و دیگر پاسخگوی سایر اعمال رسیده نخواهد بود. در این حالت عموما ویندوز در نوار عنوان برنامه، واژه‌های Not responding را نمایش می‌دهد.
این مورد همچنین در برنامه‌های سمت سرور نیز حائز اهمیت است. با قفل شدن تعداد زیادی ترد در حال اجرا، عملا قدرت پاسخ‌دهی سرور نیز کاهش می‌یابد. بنابراین در این نوع موارد، برنامه‌های چند ریسمانی هرچند در سمت کلاینت ممکن است مفید واقع شوند و برای مثال ترد UI را آزاد کنند، اما اثر آنچنانی بر روی برنامه‌های سمت سرور ندارند. زیرا در آن‌ها می‌توان هزاران ترد را ایجاد کرد که همگی دارای کدهای اصطلاحا blocking باشند. برای حل این مساله استفاده از API غیرهمزمان توصیه می‌شود.
برای نمونه کلاس WebClient توکار دات نت، دارای متدی به نام DownloadStringAsync نیز می‌باشد. این متد به محض فراخوانی، ترد جاری را آزاد می‌کند. به این معنا که فراخوانی آن سبب توقف ترد جاری برای دریافت نتیجه‌ی دریافت اطلاعات از وب نمی‌شود. به این نوع API، یک Asynchronous API گفته می‌شود؛ زیرا با سایر کدهای نوشته شده، هماهنگ و همزمان اجرا نمی‌شود.
هر چند این کد جدید مشکل عدم پاسخ دهی برنامه را برطرف می‌کند، اما مشکل دیگری را به همراه دارد؛ چگونه باید حاصل عملیات آن‌را پس از پایان کار دریافت کرد؟ چگونه باید خطاها و مشکلات احتمالی را مدیریت کرد؟
برای مدیریت این مساله، رخدادی به نام DownloadStringCompleted تعریف شده‌است. روال رویدادگردان آن پس از پایان کار دریافت اطلاعات از وب، فراخوانی می‌گردد.
        public void TestAsync()
        {
            var webClient = new WebClient();
            webClient.DownloadStringAsync(new Uri("http://www.google.com"));
            webClient.DownloadStringCompleted += webClientDownloadStringCompleted;
        }

        void webClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            // use e.Result
        }
در اینجا همچنین توسط آرگومان DownloadStringCompletedEventArgs، موفقیت یا شکست عملیات نیز گزارش می‌شود و مقدار e.Result حاصل عملیات است.

مشکل!
ما سادگی یک عملیات همزمان را از دست دادیم. متد TestNoneAsync از لحاظ پیاده سازی و همچنین خواندن و نگهداری آن در طول زمان، بسیار ساده‌تر است از نمونه‌ی TestAsync نوشته شده. در کدهای غیرهمزمان فوق، یک متد ساده، به دو متد مجزا خرد شده‌است و نتیجه‌ی نهایی، درون یک روال رخدادگردان بدست می‌آید.
به این مدل، EAP یا Event based asynchronous pattern نیز گفته می‌شود. EAP در دات نت 2 معرفی شد. روال‌های رخدادگردان در این حالت، در ترد اصلی برنامه اجرا می‌شوند. اما اگر به حالت اصلی اعمال غیرهمزمان موجود از دات نت یک کوچ کنیم، اینطور نیست. در WinForms و WPF برای به روز رسانی رابط کاربری نیاز است اطلاعات دریافت شده در همان تردی که رابط کاربری ایجاد شده است، تحویل گرفته شده و استفاده شوند. در غیراینصورت استثنایی صادر شده و برنامه خاتمه می‌یابد.


آشنایی با Synchronization Context

ابتدا یک برنامه‌ی WinForms ساده را آغاز کرده و یک دکمه‌ی جدید را به نام btnGetInfo و یک تکست باکس را به نام txtResults، به آن اضافه کنید. سپس کدهای فرم اصلی آن‌را به نحو ذیل تغییر دهید:
using System;
using System.Linq;
using System.Net;
using System.Windows.Forms;

namespace Async02
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnGetInfo_Click(object sender, EventArgs e)
        {
            var req = (HttpWebRequest)WebRequest.Create("http://www.google.com");
            req.Method = "HEAD";
            req.BeginGetResponse(
                asyncResult =>
                {
                    var resp = (HttpWebResponse)req.EndGetResponse(asyncResult);
                    var headersText = formatHeaders(resp.Headers);
                    txtResults.Text = headersText;
                }, null);
        }

        private string formatHeaders(WebHeaderCollection headers)
        {
            var headerString = headers.Keys.Cast<string>()
                                      .Select(header => string.Format("{0}:{1}", header, headers[header]));
            return string.Join(Environment.NewLine, headerString.ToArray());
        }
    }
}
در اینجا از روش دیگری برای دریافت اطلاعات از وب استفاده کرده‌ایم. با استفاده از امکانات HttpWebRequest، کوئری‌های پیشرفته‌تری را می‌توان تهیه کرد. برای مثال می‌توان نوع متد را به HEAD تنظیم نمود؛ تا صرفا مقادیر هدر آدرس درخواستی از سرور، دریافت شوند.
همچنین در این مثال از متد غیرهمزمان BeginGetResponse نیز استفاده شده‌است. در این نوع API خاص، کار با BeginGetResponse آغاز شده و سپس در callback نهایی توسط EndGetResponse، نتیجه‌ی عملیات به دست می‌آید.
اگر برنامه را اجرا کنید، با استثنای زیر مواجه خواهید شد:
 An exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Cross-thread operation not valid: Control 'txtResults' accessed from a thread other than the thread it was created on.
علت اینجا است که asyncResult دریافتی، در تردی دیگر نسبت به ترد اصلی برنامه که UI را اداره می‌کند، اجرا می‌شود. یکی از راه حل‌های این مشکل و انتقال اطلاعات به ترد اصلی برنامه، استفاده از Synchronization Context است:
        private void btnGetInfo_Click(object sender, EventArgs e)
        {
            var sync = SynchronizationContext.Current;
            var req = (HttpWebRequest)WebRequest.Create("http://www.google.com");
            req.Method = "HEAD";
            req.BeginGetResponse(
                asyncResult =>
                {
                    var resp = (HttpWebResponse)req.EndGetResponse(asyncResult);
                    var headersText = formatHeaders(resp.Headers);
                    sync.Post(delegate { txtResults.Text = headersText; }, null);
                }, null);
        }
SynchronizationContext.Current در اینجا چون در ابتدای متد دریافت اطلاعات اجرا می‌شود، به ترد UI، یا ترد اصلی برنامه اشاره می‌کند. به همین جهت این زمینه را نباید داخل Async callback دریافت کرد؛ زیرا ترد جاری آن، ترد UI مدنظر ما نیست. سپس همانطور که ملاحظه می‌کنید، توسط متد Post آن می‌توان اطلاعات را در زمینه‌ی تردی که SynchronizationContext به آن اشاره می‌کند اجرا کرد.


برای درک بهتر آن، سه break point را پیش از متد BeginGetResponse، داخل  Async calback و داخل delegate متد Post قرار دهید. پس از اجرای برنامه، از منوی دیباگ در VS.NET گزینه‌ی Windows و سپس Threads را انتخاب کنید.
در اینجا همانطور که مشخص است، کد داخل delegate تعریف شده، در ترد اصلی برنامه اجرا می‌شود و نه یکی از Worker threadهای ثانویه.
هر چند استفاده از متدهای تو در تو و lambda syntax، نیاز به تعریف چندین متد جداگانه را برطرف کرده‌است، اما باز هم کد ساده‌ای به نظر نمی‌رسد. در سی شارپ 5، برای مدیریت بهتر تمام مشکلات یاد شده، پشتیبانی توکاری از اعمال غیرهمزمان، به هسته‌ی زبان اضافه شده‌است.


Syntax ابتدایی یک متد Async

در ابتدا کلاس و متد Async زیر را در نظر بگیرید:
using System;
using System.Threading.Tasks;

namespace Async01
{
    public class AsyncExample
    {
        public async Task DoWorkAsync(int parameter)
        {
            await Task.Delay(parameter);
            Console.WriteLine(parameter);
        }
    }
}
شیوه‌ی نگارش آن بر اساس راهنمای نوشتن برنامه‌های Async یا Task asynchronous programming model یا به اختصار TAP است:
- در مدل برنامه نویسی TAP، متدهای غیرهمزمان باید یک Task را بازگشت دهند؛ یا نمونه‌ی جنریک آن‌را. البته کامپایلر، async void را نیز پشتیبانی می‌کند ولی در قسمت‌های بعدی بررسی خواهیم کرد که چرا استفاده از آن مشکل‌زا است و باید از آن پرهیز شود.
- همچنین مطابق TAP، اینگونه متدها باید به پسوند Async ختم شوند تا استفاده کننده در حین کار با Intellisense، بتواند آ‌ن‌ها را از متدهای معمولی سریعتر تشخیص دهد.
- از واژه‌ی کلیدی async نیز استفاده می‌گردد تا کامپایلر از وجود اعمال غیر همزمان مطلع گردد.
- await به کامپایلر می‌گوید، عبارت پس از من، یک وظیفه‌ی غیرهمزمان است و ادامه‌ی کدهای نوشته شده، تنها زمانی باید اجرا شوند که عملیات غیرهمزمان معرفی شده، تکمیل گردد.

در متد DoWorkAsync، ابتدا به اندازه‌‌ای مشخص توقف حاصل شده و سپس سطر بعدی یعنی Console.WriteLine اجرا می‌شود.


یک اشتباه عمومی! استفاده از واژه‌های کلیدی async و await متد شما را async نمی‌کنند.

برخلاف تصور ابتدایی از بکارگیری واژه‌های کلیدی async و await، این کلمات نحوه‌ی اجرای متد شما را async نمی‌کنند. این کلمات صرفا برای تشکیل متدهایی که هم اکنون غیرهمزمان هستند، مفید می‌باشند. برای توضیح بیشتر آن به مثال ذیل دقت کنید:
        public async Task<double> GetNumberAsync()
        {
            var generator = new Random();
            await Task.Delay(generator.Next(1000));

            return generator.NextDouble();
        }
در این متد با استفاده از Task.Delay، انجام یک عملیات طولانی شبیه سازی شده‌است؛ مثلا دریافت یک عدد یا نتیجه از یک وب سرویس. سپس در نهایت، عددی را بازگشت داده است. برای بازگشت یک خروجی double، در اینجا از نمونه‌ی جنریک Task استفاده شده‌است.
در ادامه برای استفاده از آن خواهیم داشت:
        public async Task<double> GetSumAsync()
        {
            var leftOperand = await GetNumberAsync();
            var rightOperand = await GetNumberAsync();

            return leftOperand + rightOperand;
        }
خروجی این متد تنها زمانی بازگشت داده می‌شود که نتایج leftOperand و rightOperand از وب سرویس فرضی، دریافت شده باشند و در اختیار مصرف کننده قرارگیرند. بنابراین همانطور که ملاحظه می‌کنید از واژه‌ی کلیدی await جهت تشکیل یک عملیات غیرهمزمان و مدیریت ساده‌تر کدهای نهایی، شبیه به کدهای معمولی همزمان استفاده شده‌است.
در کدهای همزمان متداول، سطر اول ابتدا انجام می‌شود و بعد سطر دوم و الی آخر. با استفاده از واژه‌ی کلیدی await یک چنین عملکردی را با اعمال غیرهمزمان خواهیم داشت. پیش از این برای مدیریت اینگونه اعمال از یک سری callback و یا رخداد استفاده می‌شد. برای مثال ابتدا عملیات همزمانی شروع شده و سپس نتیجه‌ی آن در یک روال رخ‌داد گردان جایی در کدهای برنامه دریافت می‌شد (مانند مثال ابتدای بحث). اکنون تصور کنید که قصد داشتید جمع نهایی حاصل دو عملیات غیرهمزمان را از دو روال رخدادگردان جدا از هم، جمع آوری کرده و بازگشت دهید. هرچند اینکار غیرممکن نیست، اما حاصل کار به طور قطع آنچنان زیبا نبوده و قابلیت نگهداری پایینی دارد. واژه‌ی کلیدی await، انجام اینگونه امور غیرهمزمان را طبیعی و همزمان جلوه می‌دهد. به این ترتیب بهتر می‌توان بر روی منطق و الگوریتم‌های مورد استفاده تمرکز داشت، تا اینکه مدام درگیر مکانیک اعمال غیرهمزمان بود.

امکان استفاده از واژه‌ی کلیدی await در هر جایی از کدها وجود دارد. برای نمونه در مثال زیر، برای ترکیب دو عملیات غیرهمزمان، از await در حین تشکیل عملیات ضرب نهایی، دقیقا در جایی که مقدار متد باید بازگشت داده شود، استفاده شده‌است:
        public async Task<double> GetProductOfSumAsync()
        {
            var leftOperand = GetSumAsync();
            var rightOperand = GetSumAsync();

            return await leftOperand * await rightOperand;
        }
اگر await را از این مثال حذف کنیم، خطای کامپایل زیر را دریافت خواهیم کرد:
 Operator '*' cannot be applied to operands of type 'System.Threading.Tasks.Task<double>' and 'System.Threading.Tasks.Task<double>'
خروجی متد GetSumAsync صرفا یک Task است و نه یک عدد. پس از استفاده از await، عملیات آن انجام شده و بازگشت داده می‌شود.


اگر متد DownloadString همزمان ابتدای بحث را نیز بخواهیم تبدیل به نمونه‌ی async سی‌شارپ 5 کنیم، می‌توان از متد الحاقی جدید آن به نام DownloadStringTaskAsync کمک گرفت:
        public async Task<string> DownloadAsync()
        {
            var webClient = new WebClient();
            return await webClient.DownloadStringTaskAsync("http://www.google.com");
        }
نکته‌ی مهم این کد علاوه بر ساده سازی اعمال غیر همزمان، برای استفاده از نتیجه‌ی نهایی آن، نیازی به SynchronizationContext معرفی شده در تاریخچه‌ی ابتدای بحث نیست. نتیجه‌ی دریافتی از آن در ترد اصلی برنامه تحویل داده شده و به سادگی قابل استفاده است.


سؤال: آیا استفاده از await نیز ترد جاری را قفل می‌کند؟

اگر به کدها دقت کنید، استفاده از await به معنای صبر کردن تا پایان عملیات async است. پس اینطور به نظر می‌رسد که در اینجا نیز ترد اصلی، همانند قبل قفل شده‌است.
        public void TestDownloadAsync()
        {
            Debug.WriteLine("Before DownloadAsync");
            DownloadAsync();
            Debug.WriteLine("After DownloadAsync");
        }
اگر این متد را اجرا کنید (در آن await بکار نرفته)، بلافاصله خروجی ذیل را مشاهده خواهید کرد:
 Before DownloadAsync
After DownloadAsync
به این معنا که در اصل، همانند سایر روش‌های async موجود از دات نت یک، در اینجا نیز فراخوانی متد async ترد اصلی را بلافاصله آزاد می‌کند و ترد آن‌را قفل نخواهد کرد. استفاده از await نیز عملکرد کدها را تغییر نمی‌دهد. تنها کامپایلر در پشت صحنه همان کدهای لازم جهت مدیریت روال‌های رخدادگردان و callbackها را تولید می‌کند، به نحوی که صرفا نحوه‌ی کدنویسی ما همزمان به نظر می‌رسد، اما در پشت صحنه، نحوه‌ی اجرای آن غیرهمزمان است.


برنامه‌های Async و نگارش‌های مختلف دات نت

شاید در ابتدا به نظر برسد که قابلیت‌های جدید async و await صرفا متعلق هستند به دات نت 4.5 به بعد؛ اما خیر. اگر کامپایلری را داشته باشید که از این واژه‌های کلیدی را پشتیبانی کند، امکان استفاده از آن‌ها را با دات نت 4 نیز خواهید داشت. برای این منظور تنها کافی است از VS 2012 به بعد استفاده نمائید. سپس در کنسول پاورشل نیوگت دستور ذیل را اجرا نمائید (فقط برای برنامه‌های دات نت 4 البته):
 PM> Install-Package Microsoft.Bcl.Async
این روال متداول VS.NET بوده است تا به امروز. برای مثال اگر VS 2010 را نصب کنید و سپس یک برنامه‌ی دات نت 3.5 را ایجاد کنید، امکان استفاده‌ی کامل از تمام امکانات سی‌شارپ 4، مانند آرگومان‌های نامدار و یا مقادیر پیش فرض آرگومان‌ها را در یک برنامه‌ی دات نت 3.5 نیز خواهید داشت. همین نکته در مورد async نیز صادق است. VS 2012 (یا نگارش‌های جدیدتر) را نصب کنید و سپس یک پروژه‌ی دات نت 4 را آغاز کنید. امکان استفاده از async و await را خواهید داشت. البته در این حالت دسترسی به متدهای الحاقی جدید را مانند DownloadStringTaskAsync نخواهید داشت. برای رفع این مشکل باید بسته‌ی  Microsoft.Bcl.Async را نیز توسط نیوگت نصب کنید.
بازخوردهای دوره
تزریق خودکار وابستگی‌ها در برنامه‌های ASP.NET Web forms
من برای تزریق وابستگی استفاده کردم به درستی کار میکنه
ولی در یک موضوع به بن بست خوردم

public class EfOurServiceService : IOurServiceService
    {
        readonly IUnitOfWork _uow;
        readonly IDbSet<OurService> _ourServices;
        readonly IDbSet<OurServiceCategory> _ourServiceCategories;
        public EfOurServiceService(IUnitOfWork uow)
        {
            _uow = uow;
            _ourServices = _uow.Set<OurService>();
            _ourServiceCategories = _uow.Set<OurServiceCategory>();
        }


public IList GetMasterDetailsFilterLang(string language)
        {
            var query = (_ourServiceCategories
                           .Where(c => (c.Language == language))
                           .Select(
                              c =>
                                 new
                                 {
                                     CatId=c.Id,
                                     CatName=c.Title,
                                     OurServices = c.OurServices
                                        .Select(
                                           o =>
                                              new
                                              {
                                                 ServId= o.Id,
                                                 ServName= o.Title
                                              }
                                        )
                                 }
                           ));

            var ss = query.ToList();

//در اینجا که در لایه سرویس در پروژه ای جداگانه است به ایتم‌های کوئری دسترسی کامل دارم
            foreach (var master in ss)
            {
                var s = master.CatId;
                foreach (var details in master.OurServices)
                {
                    var cc = details.ServName;
                }
            }

            return ss;
        }
}

در اینجا که در لایه سرویس در پروژه ای جداگانه است به ایتم‌های کوئری دسترسی کامل دارم
اما زمانی که در پروژه UI که وب سایت Webform قرار داره به شکل زیر از این متود استفاده میکنم :

public string CreateServiceMnu()
    {
        var ds = OurServiceService.GetMasterDetailsFilterLang(_LangSar);

        foreach (var master in ds)
        {
            var s = master.CatId;
            foreach (var details in master.OurServices)
            {
                var cc = details.ServName;
            }
        }
        return string.Empty;
    }

اما به Property‌ها دسترسی ندارم : از IList استفاده کردم (پر میشه اما قابل دسترس نیست)

Linq to Sql

نمیدونم دلیلش چیه ؟

تو لایه Service به Property‌ها دسترسی دارم اما در لایه UI که وابستگی‌ها تزریق میشه Property‌ها قابل دسترس نیست.