اشتراک‌ها
افزونه NET MAUI Community Toolkit C# Markup Extensions

اگر علاقه ای به توسعه برنامه هایی که با Net MAUI. نوشته خواهند شد با استفاده از XAML ندارید به کمک این افزونه میتوانید همان دستورات را به زبان #C بنویسید.

using System;
using CommunityToolkit.Maui.Markup;
using Microsoft.Maui;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Essentials;
using static CommunityToolkit.Maui.Markup.GridRowsColumns;

namespace HelloMauiMarkup;

class MainPage : ContentPge
{
    public MainPage()
    {
        BindingContext = new MainViewModel();

        Content = new Grid
        {
            RowSpacing = 25,
            ColumnSpacing = 0,

            Padding = Device.RuntimePlatform switch
            {
                Device.iOS => new Thickness(30, 60, 30, 30),
                _ => new Thickness(30)
            },

            RowDefinitions = Rows.Define(
                (Row.HelloWorld, 44),
                (Row.Welcome, Auto),
                (Row.Count, Auto),
                (Row.ClickMeButton, Auto),
                (Row.Image, Star)),

            ColumnDefinitions = Columns.Define(
            (Column.Text, Star),
                (Column.Number, Star)),

            Children =
            {
                new Label { Text = "Hello World" }
.Row(Row.HelloWorld).ColumnSpan(All<Column>())
.Font(size: 32)
.CenterHorizontal().TextCenter(),

                new Label { Text = "Welcome to .NET MAUI Markup Community Toolkit Sample" }
                .Row(Row.Welcome).ColumnSpan(All<Column>())
                    .Font(size: 18)
                .CenterHorizontal().TextCenter(),

new Label { Text = "Current Count: " }
                .Row(Row.Count).Column(Column.Text)
.Font(bold: true)
.End().TextEnd(),

new Label()
                .Row(Row.Count).Column(Column.Number)
                    .Font(bold: true)
                    .Start().TextStart()
                    .Bind<Label, int, string>(Label.TextProperty, nameof(MainViewModel.ClickCount), convert: count => count.ToString())

                new Button { Text = "Click Me" }
                .Row(Row.ClickMeButton)
                .Font(bold: true)
                .CenterHorizontal()
.BindCommand(nameof(ViewModel.ClickMeButtonCommand)),

                new Image { Source = "dotnet_bot.png", WidthRequest = 250, HeightRequest = 310 }
.Row(Row.Image).ColumnSpan(All<Column>())
.CenterHorizontal()
}
};
    }

    enum Row { HelloWorld, Welcome, Count, ClickMeButton, Image }
    enum Column { Text, Number }
}


افزونه NET MAUI Community Toolkit C# Markup Extensions
مطالب
بررسی مفاهیم متغیرهای Value Type و Reference Type در سی شارپ
نوع داده(Data Type) ، متغیر‌ها(Variables) ، انواع مقداری(Value Type) ، انواع ارجاعی(Reference Type)

مقدمه :
نوع داده‌ها، اجزای اصلی سازنده‌ی یک زبان برنامه نویسی و شبیه قواعد هر زبانی هستند.
مفاهیمی که در این مطلب بررسی خواهد شد :
 • Data Type نوع داده
 • Variables  متغیرها
 • Naming Convention قرارداد‌های نامگذاری
 • Value Type/Reference Type انواع مقداری و ارجاعی
 • Stack/heap memory  حافظه پشته و هرم

نوع داده

در دنیای واقعی، برای نگهداری مواد مختلف، ظروف مختلفی با اندازه‌های مختلفی طراحی شده است. در دنیای برنامه نویسی، به تناسب اطلاعاتی که می‌خواهیم در حافظه ذخیره کنیم، باید نوع ظرف ذخیره سازی را انتخاب کنیم. نوع ظرف ذخیره سازی را در دنیای برنامه نویسی، نوع داده‌ها مشخص می‌کنند.
در دات نت، همه‌ی نوع داده‌ها (Data Type) بصورت مستقیم و یا غیر مستقیم، از کلاس System.Object مشتق شده‌اند.


متغیرها

متغیر‌ها برای ذخیره‌ی مقادیر (اطلاعات)، استفاده می‌شوند. به این مثال دقت کنید: ما یک کیف داریم که در آن یک کتاب قرار دارد. در اینجا کیف نقش متغیر و کتاب نقش مقدار (value) را ایفا می‌کند. اندازه‌ی کیف همان نوع داده (Data Type) در دنیای برنامه نویسی می‌باشد.


چک کردن سایز نوع داده (Data Type)

ما نیازی به حفظ کردن اندازه‌ی نوع داده‌ها نداریم. در سی شارپ متدی به نام () sizeof مهیا شده است که با چک کردن نوع داده، اندازه‌ی آن را بر حسب بایت نمایش می‌دهد.
به مثال زیر دقت کنید:
Console.WriteLine(sizeof(int));
Console.WriteLine(sizeof(char));
Console.WriteLine(sizeof(bool));
Console.WriteLine(sizeof(decimal));
Console.WriteLine(sizeof(float));
خروجی کد‌های بالا :
 4
2
1
16
4

نکته : متد sizeof فقط برای نمایش اندازه‌ی نوع داده‌های مقداری (value type) می‌تواند مورد استفاده قرار گیرد.


چک کردن نوع داده

ما می‌توانیم نوع داده‌ها را برای بدست آوردن کلاسی که به آن تعلق دارند، چک کنیم.
مثال :
 int a = 23;
float b = 3.14f;
Console.WriteLine(a.GetType());
Console.WriteLine(b.GetType());
خروجی کد‌های بالا : 
System.Int32
System.Single

چک کردن نوع داده‌ی دو شیء

فرض کنید 2 شیء را با نام‌های obj1 و obj2 داریم که هر دو از نوع long هستند. برای اینکه این مقایسه را انجام دهیم، از متد Object.RefrenceEqual می‌توان استفاده کرد.
مثال :
long obj1 = 356;
long obj2 = 54;
float obj3 = 234;
Console.WriteLine(object.ReferenceEquals(obj1.GetType(), obj2.GetType()));
Console.WriteLine(object.ReferenceEquals(obj1.GetType(), obj3.GetType()));
خروجی کد‌های بالا : 
True
False

تعریف یک متغیر ومقدار دهی به آن
سی شارپ یک زبان strongly typed است (البته با در نظر نگرفتن نوع dynamic آن). به این معنا که کلیه‌ی متغیر‌ها، قبل از استفاده باید تعریف و مقدار دهی شوند و بعد از تعریف متغیر، نمی‌توان نوع آن را تغییر داد. رفتار یک متغیر بر اساس نوع انتخابی ما مشخص می‌شود. بطور مثال با انتخاب نوع int تنها می‌توان اعداد صحیح را ذخیره و نگهداری کرد و برای تغییر رفتار متغیر‌ها باید آنها را تبدیل کنیم.

تعریف یک متغیر
برای استفاده از یک متغیر ابتداباید آن را تعریف کنیم :
//<data type> <variable name>;
Int a;

مقداردهی اولیه یک متغیر

مقدار دهی اولیه‌ی یک متغیر با استفاده از عملگر = و نوشتن مقدار مورد نظر برای ذخیره کردن در متغیر، در سمت راست عملگر اتفاق خواهد افتاد.
//<data type> <variable name>=value;
Int a=23;
Int a;//declare تعریف
a=23;//مقدار دهی اولیه initializing
Int a=23;//تعریف و مقدار دهی در یک خط
Int a,b,c=23;//تعریف چند متغیر و مقدار دهی در یک خط


قرار دا‌دهای نام گذاری متغیر‌ها :

در دنیای برنامه نویسی دو نوع قرار داد نام گذاری بسیار متداول وجود دارند:
 1-  camelCase : در این قرار داد، حرف اول کلمه‌ی اول، بصورت کوچک و حرف اول از کلمه‌ی دوم، بصورت بزرگ نوشته خواهد شد. برای مثال: firstName,lastName
 2- PascalCase : در این قرار داد حروف ابتدایی دو کلمه‌ی مجاور، بصورت بزرگ نوشته خواهند شد: FirstName,LastName

چند نکته :
 • نامگذاری متغیر‌ها را می‌توانید با علامت _ و یا @ شروع کنید.
 • کلمات کلیدی (key word) سی شارپ نمی‌توانند به عنوان نام متغیر مورد استفاده قرار بگیرند (مگر آنکه با @ شروع شوند).
 • در بین نام متغیر نباید فضای خالی وجود داشته باشد. کاراکتر‌های سازنده‌ی متغیر می‌توانند اعداد، حروف و زیر خط باشند.
لیستی از نام گذاری‌های مجاز:
 int abc;
long _abcd;
float @abcd;
bool main_button;
decimal piValue;
string firstName;
string first_name;
bool button55_on;
لیستی از نام گذاری‌های غیر مجاز
long _a.5bc5d;
float @ab cd;
decimal pi@Value;
//استفاده از کلمات کلیدی سی شارپ که کامپایلر آنها را مجاز نمی‌داند
bool class;
string namespace;
string string;
int static;
برای مطالعه‌ی کاملتر کلمات کلیدی سی شارپ می‌توانید اینجا را مطالعه کنید.


در ادامه کمی در مورد نوع داده‌ها بحث خواهیم کرد.
در سی شارپ دو مدل نوع داده وجود دارد:
 • انواع مقداری Value Type
 • انواع ارجاعی یا اشاره‌ای Reference Type

انواع مقداری (Value Type) :
 • انواع مقداری مستقیما حاوی داده‌ها هستند. اگر یک متغیر از نوع مقداری را به یک متغیر دیگر تخصیص دهید، مقدار آنها مستقیما کپی می‌شوند؛ برعکس نوع‌های اشاره‌ای که با نخصیص یک متغیر به یک متغیر دیگر، تنها اشاره‌گر به مقدار شیء کپی خواهد شد و نه خود شیء.
 • کلیه نوع‌های مقداری از کلاس ValueType مشتق شده‌اند.
 • در فضای stack  به آنها حافظه تخصیص داده می‌شود.
 • نمی‌توانند مقدار null  بپذیرند. البته با قابلیت nullabletype امکان تخصیص مقدار null به نوع داده‌های مقداری نیز مهیا شده است.
 • همه نوع‌های داده‌های مقداری، یک سازنده پیش فرض دارند که به صورت ضمنی کار مقدار دهی اولیه برای آنها را انجام می‌دهد. برای مطالعه بیشتر درباره مقادیر پیش فرض به اینجا مراجعه کنید.

انواع مقداری به دو دسته‌ی اصلی تقسیم می‌شود :
 • Structs
 • Enumerations

طبقه بندی Structs به صورت زیر است :
 • Numeric Type
* Integral Type : sbyte,short,ushort,int,uint,long,ulong,char
* Floating-Point Types : float,double
* Decimal : decimal
 •  Bool دو مقدار true و false
 • User Defined Struct


نوع داده نال (تهی) پذیر (nullable Type) و چگونگی تعریف آن

در ابتدای معرفی نوع داده‌های مقداری گفتیم همیشه باید وضعیت متغیر مشخص و مقدار دهی اولیه‌ی آن یا به صورت ضمنی و یا آشکار انجام شود. هیچ یک از نوع داده‌های مقداری نمی‌توانند بصورت null تعریف شوند. برای تبدیل یک نوع داده مقداری به صورتی که قابلیت ذخیره‌ی مقدار null را داشته باشد، بعد از نوشتن نوع داده، علامت سوال ؟ قرار می‌دهیم.
 < data type >? < variable name >= null; //syntax

int? a = null; //assigning null
int? b = 55; //assigning null and a value
var? c = 55 //it will give error

نکته :  var نمی‌تواند بصورت nullable تعریف شود.

برای چک کردن مقدار در انواع تهی پذیر (nullable) دو خصوصیت وجود دارد:
 • HasValue
اگر مقداری در متغیر وجود داشته باشد ارزش true  بازگردانده می‌شود؛ در غیر اینصورت ارزش false
 • Value
مقدار واقعی متغیر را باز می‌گرداند.

مثال :
 int? a = null;
int? b = 22;
Console.WriteLine(a.HasValue);
//------------
Console.WriteLine(b.HasValue);
Console.WriteLine(b.Value);
خروجی کد بالا :
 False
True
22

انواع ارجاعی Reference Type

انواع ارجاعی مستقیما حاوی اطلاعات نیستند و ارجاعی هستند به آدرسی از حافظه که حاوی اطلاعات واقعی است. به بیانی دیگر، اشاره‌گری به آدرسی از حافظه هستند.
 • انواع ارجاعی بصورت غیر مستقیم حاوی داده‌ها هستند.
 • در بخشی از حافظه که به آن heap می‌گوییم، به آنها فضا اختصاص داده می‌شود.
 • می‌توانند بصورت null (بدون مقدار) باشند.

انواع ارجاعی نیز به دو دسته‌ی کلی تقسیم می‌شوند :

 • انواع از پیش تعریف شده
  Object,string,dynamic
 • انواع تعریف شده توسط کاربر
        class,interface,delegate

نکته : آدرس مکانی از حافظه که داده‌ها در آن قرار دارند، در بخش پشته یا Stack ذخیره می‌شود و داد‌ه‌ها در فضای heap ذخیره می‌شوند.
مثال :
 test obj; //allocating reference on stack
obj= new test(55);//allocating object on heap

نکته : دو متغیر از نوع ارجاعی می‌توانند به یک آدرس از حافظه اشاره کنند. در شکل زیر این موضوع نشان داده شده است.

 
در شکل زیر طبقه بندی نوع داده‌ها در سی شارپ نشان داده شده است :


• عملیات کپی در نوع داده مقداری
وقتی از یک متغیر مقداری را به یک متغیر دیگر تخصیص می‌دهیم، یک کپی جدید از آن در فضای stack  ایجاد می‌شود. بدین معنی که محتوای دو متغیر یکسان هستند، ولی در دو بخش مجزای در حافظه‌ی Stack قرار دارند. به همین خاطر تغییر  محتوای یک متغیر، محتوای متغیر دیگر را تغییر نمی‌دهد.
مثال :
 int a = 55;//declare a and initialize
int copya = a;//copya contains the copy of value a
دیاگرام حافظه کد بالا :

 

• عملیات کپی، در نوع داده‌ی ارجاعی
وقتی یک متغیر از نوع ارجاعی را به یک متغیر دیگر تخصیص می‌دهیم، دو اشاره‌گر در فضای Stack ایجاد می‌شود که به یک مقدار واحد در حافظه‌ی heap اشاره می‌کنند. آدرس‌های ذخیره شده‌ی در stack  یکسان هستند.
مثال : در اینجا فرض بر این است کهtest یک کلاس تعریف شده‌ی توسط کاربر می‌باشد.
test obj;
obj=new test(23);
test objCopy;
objCopy = obj;

دیاگرام حافظه‌ی قطعه کد بالا به شکل زیر است :



تخصیص حافظه در بخش Stack  و Heap به متغیر‌ها

سیستم عامل و net CLR. حافظه را به دو بخش stack و heap تقسیم بندی می‌کنند.
زمانی که یک متد را فراخوانی می‌کنیم، در بخش پشته به پارامتر‌های متد فضا تخصیص داده می‌شود و بعد از پایان کار متد، فضای اشغال شده‌ی بوسیله GC یا همان Garbage collection  آزاد می‌شود.
تخصیص حافظه در Stack  بر اساس قانون LIFO انجام و به ترتیب و پشت سر هم، حافظه تخصیص داده می‌شود. دیاگرام تخصیص حافظه به stack:


تخصیص حافظه در Heap بصورت تصادفی است؛ بر عکس پشته (stack) که به ترتیب و متوالی انجام می‌شد. انواع ارجاعی در Stack  ذخیره می‌شوند؛ ولی داده‌ی واقعی در heap قرار می‌گیرد.
حافظه‌های پویا در بخش heap و حافظه‌های استاتیک در بخش stack تخصیص داده می‌شوند.
 
نظرات مطالب
آشنایی با مفاهیم نوع داده Enum و توسعه آن - قسمت دوم
وقتی کلاس Description در   فضای نام  System.ComponentModel وجود داره دلیلی نداره  کلاس مشابه ای تعریف کنیم.
بخاطر اینکه مصرف کننده محض نباشیم یک متد الحاقی به نام ()GetEnumList اضافه کردم که لیست اعضای یک Enum  رو برای استفاده در کمبو باکس و ... بر می‌گرودنه :
ابتدا کلاس زیر به کلاس ExtensionMethodCls اضافه می‌کنیم :
 public class EnumObject
        {
            public Enum ValueMember { get; set; }
            public int intValueMember
            {
                get { return int.Parse(ValueMember.ToString("D")); }
            }
            public string stringValueMember
            {
                get { return ValueMember.ToString(""); }
            }
            public string DisplayMember
            {
                get { return ValueMember.GetDescription(); }
            }
        }
و متد الحاقی زیر رو برای گرفتن لیست تعریف می‌کنیم:
      public static List<EnumObject> GetEnumList(this Enum enu)
        {
            List<EnumObject> li = new List<EnumObject>();
            foreach (var item in enu.GetType().GetEnumValues())
            {
                li.Add(new EnumObject { ValueMember = (Enum)item });
            }
            return li;
        }
نحوه استفاده  :
          comboBox1.DataSource = Grade.VeryGood.GetEnumList();
          comboBox1.DisplayMember = "DisplayMember";
          comboBox1.ValueMember = "ValueMember";
همون طوری که در کد بالا می‌بینید برای گرفتن لیست مجبور شدیم یکی از اعضای enum  رو انتخاب کنیم (Grade.VeryGood.GetEnumList()) شاید انتخاب یکی از اعضا و بعد درخواست لیست اعضا رو کردن کار قشنگی نباشه به همین دلیل متد زیر رو تعریف کردیم :
        public static List<EnumObject> EnumToList<T>()
        {
            Type enumType = typeof(T);
            if (enumType.BaseType != typeof(Enum))
                throw new ArgumentException("T must be of type System.Enum");

            List<EnumObject> li = new List<EnumObject>();
            foreach (var item in enumType.GetEnumValues())
            {
                li.Add(new EnumObject { ValueMember = (Enum)item });
            }
            return li;
        }
نحوه استفاده :
    comboBox1.DataSource =ExtensionMethodCls.EnumToList<Grade>();
          comboBox1.DisplayMember = "DisplayMember";
          comboBox1.ValueMember = "ValueMember";
کد کامل :
 public static class ExtensionMethodCls
    {
        public class EnumObject
        {
            public Enum ValueMember { get; set; }
            public int intValueMember
            {
                get { return int.Parse(ValueMember.ToString("D")); }
            }
            public string stringValueMember
            {
                get { return ValueMember.ToString(""); }
            }
            public string DisplayMember
            {
                get { return ValueMember.GetDescription(); }
            }
        }
 
    

        public static List<EnumObject> EnumToList<T>()
        {
            Type enumType = typeof(T);
            if (enumType.BaseType != typeof(Enum))
                throw new ArgumentException("T must be of type System.Enum");

            List<EnumObject> li = new List<EnumObject>();
            foreach (var item in enumType.GetEnumValues())
            {
                li.Add(new EnumObject { ValueMember = (Enum)item });
            }
            return li;
        }

        public static List<EnumObject> GetEnumList(this Enum enu)
        {
            List<EnumObject> li = new List<EnumObject>();
            foreach (var item in enu.GetType().GetEnumValues())
            {
                li.Add(new EnumObject { ValueMember = (Enum)item });
            }
            return li;
        }

        public static string GetDescription(this Enum enu)
        {

            Type type = enu.GetType();

            MemberInfo[] memInfo = type.GetMember(enu.ToString());

            if (memInfo != null && memInfo.Length > 0)
            {

                object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attrs != null && attrs.Length > 0)
                    return ((DescriptionAttribute)attrs[0]).Description;
            }

            return enu.ToString();

        }
    }
مطالب دوره‌ها
نگاهی به SignalR Clients
در قسمت قبل موفق به ایجاد اولین Hub خود شدیم. در ادامه، برای تکمیل برنامه نیاز است تا کلاینتی را نیز برای آن تهیه کنیم.
مصرف کنندگان یک Hub می‌توانند انواع و اقسام برنامه‌های کلاینت مانند jQuery Clients و یا حتی یک برنامه کنسول ساده باشند و همچنین Hubهای دیگر نیز قابلیت استفاده از این امکانات Hubهای موجود را دارند. تیم SignalR امکان استفاده از Hubهای آن‌را در برنامه‌های دات نت 4 به بعد، برنامه‌های WinRT، ویندوز فون 8، سیلورلایت 5، jQuery و همچنین برنامه‌های CPP نیز مهیا کرده‌اند. به علاوه گروه‌های مختلف نیز با توجه به سورس باز بودن این مجموعه، کلاینت‌های iOS Native، iOS via Mono و Android via Mono را نیز به این لیست اضافه کرده‌اند.


بررسی کلاینت‌های jQuery

با توجه به پروتکل مبتنی بر JSON سیگنال‌آر، استفاده از آن در کتابخانه‌های جاوا اسکریپتی همانند jQuery نیز به سادگی مهیا است. برای نصب آن نیاز است در کنسول پاور شل نوگت، دستور زیر را صادر کنید:
 PM> Install-Package Microsoft.AspNet.SignalR.JS
برای نمونه به solution پروژه قبل، یک برنامه وب خالی دیگر را اضافه کرده و سپس دستور فوق را بر روی آن اجرا نمائید. در این حالت فقط باید دقت داشت که فرامین بر روی کدام پروژه اجرا می‌شوند:


با استفاده از افزونه SignalR jQuery، به دو طریق می‌توان به یک Hub اتصال برقرار کرد:
الف) استفاده از فایل proxy تولید شده آن (این فایل، در زمان اجرای برنامه تولید می‌شود و یا امکان استفاده از آن به کمک ابزارهای کمکی نیز وجود دارد)
نمونه‌ای از آن‌را در قسمت قبل ملاحظه کردید؛ همان فایل تولید شده در مسیر /signalr/hubs برنامه. به نوعی به آن Service contract نیز گفته می‌شود (ارائه متادیتا و قراردادهای کار با یک سرویس Hub). این فایل همانطور که عنوان شد به صورت پویا در زمان اجرای برنامه ایجاد می‌شود.
امکان تولید آن توسط برنامه کمکی signalr.exe نیز وجود دارد؛ برای دریافت آن می‌توان از طریق NuGet اقدام کرد (بسته Microsoft.AspNet.SignalR.Utils) که نهایتا در پوشه packages قرار خواهد گرفت. نحوه استفاده از آن نیز به صورت زیر است:
 Signalr.exe ghp http://localhost/
در این دستور ghp مخفف generate hub proxy است و نهایتا فایلی را به نام server.js تولید می‌کند.

ب) بدون استفاده از فایل proxy و به کمک روش late binding (انقیاد دیر هنگام)

برای کار با یک Hub از طریق jQuery مراحل ذیل باید طی شوند:
1) ارجاعی به Hub باید مشخص شود.
2) روال‌های رخدادگردان تنظیم گردند.
3) اتصال به Hub برقرار گردد.
4) متدی فراخوانی شود.

در اینجا باید دقت داشت که امکانات Hub به صورت خواص
 $.connection
در سمت کلاینت جی‌کوئری، در دسترس خواهند بود. برای مثال:
 $.connection.chatHub
و نام‌های بکارگرفته شده در اینجا مطابق روش‌های متداول نام گذاری در جاوا اسکریپت، camel case هستند.

خوب، تا اینجا فرض بر این است که یک پروژه خالی ASP.NET را آغاز و سپس فرمان نصب Microsoft.AspNet.SignalR.JS را نیز همانطور که عنوان شد، صادر کرده‌اید. در ادامه یک فایل ساده html را به نام chat.htm، به این پروژه جدید اضافه کنید (برای استفاده از کتابخانه جاوا اسکریپتی SignalR الزامی به استفاده از صفحات کامل پروژه‌های وب نیست).
<!DOCTYPE>
<html>
<head>
    <title></title>
    <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script>
    <script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script>
</head>
<body>
    <div>
        <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" />
        <ul id="messages">
        </ul>
    </div>
    <script type="text/javascript">
        $(function () {
            var chat;
            $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ می‌کند
            chat = $.connection.chat; //این نام مستعار پیشتر توسط ویژگی نام هاب تنظیم شده است
            chat.client.hello = function (message) {
                //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است
                //به این ترتیب سرور می‌تواند کلاینت را فراخوانی کند
                $("#messages").append("<li>" + message + "</li>");
            };
            $.connection.hub.start(/*{ transport: 'longPolling' }*/); // فاز اولیه ارتباط را آغاز می‌کند

            $("#send").click(function () {
                // Hub's `SendMessage` should be camel case here
                chat.server.sendMessage($("#txtMsg").val());
            });
        });
    </script>
</body>
</html>
کدهای آن‌را به نحو فوق تغییر دهید.
توضیحات:
همانطور که ملاحظه می‌کنید ابتدا ارجاعاتی به jquery و jquery.signalR-1.0.1.min.js اضافه شده‌اند. سپس نیاز است مسیر دقیق فایل پروکسی هاب خود را نیز مشخص کنیم. اینکار با تعریف مسیر signalr/hubs انجام شده است.
<script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script>
در ادامه توسط تنظیم connection.hub.logging سبب خواهیم شد تا اطلاعات بیشتری در javascript console مرورگر لاگ شود.
سپس ارجاعی به هاب تعریف شده، تعریف گردیده است. اگر از قسمت قبل به خاطر داشته باشید، توسط ویژگی HubName، نام chat را برگزیدیم. بنابراین connection.chat ذکر شده دقیقا به این هاب اشاره می‌کند.
سپس سطر chat.client.hello مقدار دهی شده است. متد hello، متدی dynamic و تعریف شده در سمت هاب برنامه است. به این ترتیب می‌توان به پیام‌های رسیده از طرف سرور گوش فرا داد. در اینجا، این پیام‌ها، به li ایی با id مساوی messages اضافه می‌شوند.
سپس توسط فراخوانی متد connection.hub.start، فاز negotiation شروع می‌شود. در اینجا حتی می‌توان نوع transport را نیز صریحا انتخاب کرد که نمونه‌ای از آن را به صورت کامنت شده جهت آشنایی با نحوه تعریف آن مشاهده می‌کنید. مقادیر قابل استفاده در آن به شرح زیر هستند:
 - webSockets
- forverFrame
- serverSentEvents
- longPolling
سپس به رویدادهای کلیک دکمه send گوش فرا داده و در این حین، اطلاعات TextBox ایی با id مساوی txtMsg را به متد SendMessage هاب خود ارسال می‌کنیم. همانطور که پیشتر نیز عنوان شد، در سمت کلاینت، تعریف متد SendMessage باید camel case باشد.

اکنون به صورت جداگانه یکبار برنامه hub را در مرورگر باز کنید. سپس بر روی فایل chat.htm کلیک راست کرده و گزینه مشاهده آن را در مرورگر نیز انتخاب نمائید (گزینه View in browser منوی کلیک راست).

خوب! پروژه کار نمی‌کند! برای اینکه مشکلات را بهتر بتوانید مشاهده کنید نیاز است به JavaScript Console مرورگر خود مراجعه نمائید. برای مثال در مرورگر کروم دکمه F12 را فشرده و برگه Console آن‌را باز کنید. در اینجا اعلام می‌کند که فاز negotiation قابل انجام نیست؛ چون مسیر پیش فرضی را که انتخاب کرده است، همین مسیر پروژه دومی است که اضافه کرده‌ایم (کلاینت ما در پروژه دوم قرار دارد و نه در همان پروژه اول هاب).
برای اینکه مسیر دقیق hub را در این حالت مشخص کنیم، سطر زیر را به ابتدای کدهای جاوا اسکریپتی فوق اضافه نمائید:
 $.connection.hub.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم
اکنون اگر مجدا سعی کنید، باز هم برنامه کار نمی‌کند و پیام می‌دهد که امکان دسترسی به این سرویس از خارج از دومین آن میسر نیست. برای اینکه این مجوز را صادر کنیم نیاز است تنظیمات مسیریابی پروژه هاب را به نحو ذیل ویرایش نمائیم:
using System;
using System.Web;
using System.Web.Routing;
using Microsoft.AspNet.SignalR;

namespace SignalR02
{
    public class Global : HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            // Register the default hubs route: ~/signalr
            RouteTable.Routes.MapHubs(new HubConfiguration
            {
                EnableCrossDomain = true
            });
        }
    }
}
با تنظیم EnableCrossDomain به true اینبار فاز‌های آغاز ارتباط با سرور برقرار می‌شوند:
 SignalR: Auto detected cross domain url. jquery.signalR-1.0.1.min.js:10
SignalR: Negotiating with 'http://localhost:1072/signalr/negotiate'. jquery.signalR-1.0.1.min.js:10
SignalR: SignalR: Initializing long polling connection with server. jquery.signalR-1.0.1.min.js:10
SignalR: Attempting to connect to 'http://localhost:1072/signalr/connect?transport=longPolling&connectionToken…NRh72omzsPkKqhKw2&connectionData=%5B%7B%22name%22%3A%22chat%22%7D%5D&tid=3' using longPolling. jquery.signalR-1.0.1.min.js:10
SignalR: Longpolling connected jquery.signalR-1.0.1.min.js:10
مطابق این لاگ‌ها ابتدا فاز negotiation انجام می‌شود. سپس حالت long polling را به صورت خودکار انتخاب می‌کند.

در برگه شبکه، مطابق شکل فوق، امکان آنالیز اطلاعات رد و بدل شده مهیا است. برای مثال در حالتیکه سرور پیام دریافتی را به کلیه کلاینت‌ها ارسال می‌کند، نام متد و نام هاب و سایر پارامترها در اطلاعات به فرمت JSON آن به خوبی قابل مشاهده هستند.

یک نکته:
اگر از ویندوز 8 (یعنی IIS8) و VS 2012 استفاده می‌کنید، برای استفاده از حالت Web socket، ابتدا فایل وب کانفیگ برنامه را باز کرده و در قسمت httpRunTime، مقدار ویژگی targetFramework را بر روی 4.5 تنظیم کنید. اینبار اگر مراحل negotiation را بررسی کنید در همان مرحله اول برقراری اتصال، از روش Web socket استفاده گردیده است.


تمرین 1
به پروژه ساده و ابتدایی فوق یک تکست باکس دیگر به نام Room را اضافه کنید؛ به همراه دکمه join. سپس نکات قسمت قبل را در مورد الحاق به یک گروه و سپس ارسال پیام به اعضای گروه را پیاده سازی نمائید. (تمام نکات آن با مطلب فوق پوشش داده شده است و در اینجا باید صرفا فراخوانی متدهای عمومی دیگری در سمت هاب، صورت گیرد)

تمرین 2
در انتهای قسمت دوم به نحوه ارسال پیام از یک هاب به هابی دیگر اشاره شد. این MonitorHub را ایجاد کرده و همچنین یک کلاینت جاوا اسکریپتی را نیز برای آن تهیه کنید تا بتوان اتصال و قطع اتصال کلیه کاربران سیستم را مانیتور و مشاهده کرد.


پیاده سازی کلاینت jQuery بدون استفاده از کلاس Proxy

در مثال قبل، از پروکسی پویای مهیای در آدرس signalr/hubs استفاده کردیم. در اینجا قصد داریم، بدون استفاده از آن نیز کار برپایی کلاینت را بررسی کنیم.
بنابراین یک فایل جدید html را مثلا به نام chat_np.html به پروژه دوم برنامه اضافه کنید. سپس محتویات آن‌را به نحو زیر تغییر دهید:
<!DOCTYPE>
<html>
<head>
    <title></title>
    <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script>
</head>
<body>
    <div>
        <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" />
        <ul id="messages">
        </ul>
    </div>
    <script type="text/javascript">
        $(function () {                        
            $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ می‌کند

            var connection = $.hubConnection();
            connection.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم
            var proxy = connection.createHubProxy('chat');

            proxy.on('hello', function (message) {
                //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است
                //به این ترتیب سرور می‌تواند کلاینت را فراخوانی کند
                $("#messages").append("<li>" + message + "</li>");
            });

            $("#send").click(function () {
                // Hub's `SendMessage` should be camel case here
                proxy.invoke('sendMessage', $("#txtMsg").val());
            });

            connection.start();
        });
    </script>
</body>
</html>
در اینجا سطر مرتبط با تعریف مسیر اسکریپت‌های پویای signalr/hubs را دیگر در ابتدای فایل مشاهده نمی‌کنید. کار تشکیل proxy اینبار از طریق کدنویسی صورت گرفته است. پس از ایجاد پروکسی، برای گوش فرا دادن به متدهای فراخوانی شده از طرف سرور از متد proxy.on و نام متد فراخوانی شده سمت سرور استفاده می‌کنیم و یا برای ارسال اطلاعات به سرور از متد proxy.invoke به همراه نام متد سمت سرور استفاده خواهد شد.


کلاینت‌های دات نتی SignalR
تا کنون Solution ما حاوی یک پروژه Hub و یک پروژه وب کلاینت جی‌کوئری است. به همین Solution، یک پروژه کلاینت کنسول ویندوزی را نیز اضافه کنید.
سپس در خط فرمان پاور شل نوگت دستور زیر را صادر نمائید تا فایل‌های مورد نیاز به پروژه کنسول اضافه شوند:
 PM> Install-Package Microsoft.AspNet.SignalR.Client
در اینجا نیز باید دقت داشت تا دستور بر روی default project صحیحی اجرا شود (حالت پیش فرض، اولین پروژه موجود در solution است).
پس از نصب آن اگر به پوشه packages مراجعه کنید، نگارش‌های مختلف آن‌را مخصوص سیلورلایت، دات نت‌های 4 و 4.5، WinRT و ویندوز فون8 نیز می‌توانید در پوشه Microsoft.AspNet.SignalR.Client ملاحظه نمائید. البته در ابتدای نصب، انتخاب نگارش مناسب، بر اساس نوع پروژه جاری به صورت خودکار صورت می‌گیرد.
مدل برنامه نویسی آن نیز بسیار شبیه است به حالت عدم استفاده از پروکسی در حین استفاده از jQuery که در قسمت قبل بررسی گردید و شامل این مراحل است:
1) یک وهله از شیء HubConnection را ایجاد کنید.
2) پروکسی مورد نیاز را جهت اتصال به Hub از طریق متد CreateProxy تهیه کنید.
3) رویدادگردان‌ها را همانند نمونه کدهای جاوا اسکریپتی قسمت قبل، توسط متد On تعریف کنید.
4) به کمک متد Start، اتصال را آغاز نمائید.
5) متدها را به کمک متد Invoke فراخوانی نمائید.

using System;
using Microsoft.AspNet.SignalR.Client.Hubs;

namespace SignalR02.WinClient
{
    class Program
    {
        static void Main(string[] args)
        {
            var hubConnection = new HubConnection(url: "http://localhost:1072/signalr");
            var chat = hubConnection.CreateHubProxy(hubName: "chat");

            chat.On<string>("hello", msg => {
                Console.WriteLine(msg);
            });

            hubConnection.Start().Wait();

            chat.Invoke<string>("sendMessage", "Hello!");

            Console.WriteLine("Press a key to terminate the client...");
            Console.Read();
        }
    }
}
نمونه‌ای از این پیاده سازی را در کدهای فوق ملاحظه می‌کنید که از لحاظ طراحی آنچنان تفاوتی با نمونه ذهنی جاوا اسکریپتی ندارد.

نکته مهم
کلیه فراخوانی‌هایی که در اینجا ملاحظه می‌کنید غیرهمزمان هستند.
به همین جهت پس از متد Start، متد Wait ذکر شده‌است تا در این برنامه ساده، پس از برقراری کامل اتصال، کار invoke صورت گیرد و یا زمانیکه callback تعریف شده توسط متد chat.On فراخوانی می‌شود نیز این فراخوانی غیرهمزمان است و خصوصا اگر نیاز است رابط کاربری برنامه را در این بین به روز کنید باید به نکات به روز رسانی رابط کاربری از طریق یک ترد دیگر دقت داشت.
مطالب
روش بازگشت به قالب‌های کلاسیک پروژه‌ها در دات نت 6
نگارش نهایی دات نت 6، حدود یک ماه دیگر منتشر می‌شود و اگر برای نمونه RC2 آن‌را نصب کرده باشید، با ایجاد یک پروژه‌ی کنسول جدید مبتنی بر آن ... شگفت زده خواهید شد!  شاید انتظار داشته باشید که با چنین فایلی مواجه شوید:
using System; 
 
namespace MyVerboseApp 
{ 
    public class Program 
    { 
        public static void Main(string[] args) 
        { 
            Console.WriteLine("Hello World!"); 
        } 
    } 
}
اما یک چنین خروجی تولید می‌شود:
 // See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
این مورد قابلیتی است که به همراه C# 9.0 به نام «Top Level Programs» ارائه شد و اکنون در تمام قالب‌های پیش‌فرض پروژه‌های مبتنی بر دات نت 6، استفاده شده‌است. این قالب شاید برای تازه‌کارها، جالب باشد و کم حجم و کم سطر، اما «ما آن‌را درخواست نداده بودیم!».


روش بازگشت به قالب‌های قبلی

در حال حاضر و در نگارش فعلی و حتی رسمی دات نت 6، روشی برای بازگشت به حالت قبلی وجود ندارد که به احتمال زیاد در نگارش‌های پس از RTM لحاظ خواهد شد (می‌توانید در اینجا ^ و ^ به آن رای دهید). تنها راه حل موجود، استفاده از دستور زیر است:
dotnet new console --framework net5.0 --target-framework-override net6.0
این دستور در اصل به این معنا است که پروژه‌ی من را بر اساس قالب پروژه‌های NET 5.0. تولید کن؛ اما در فایل csproj آن، بجای net5.0 از net6.0 به عنوان target framework استفاده شود:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
    <OutputType>Exe</OutputType>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
</Project>
در اینجا سطر net5.0 را حذف و با net6.0 جایگزین کنید.
مطالب
پشتیبانی از انقیاد پویا در سی‌شارپ
زبان سی‌شارپ strongly typed و type safe است. کامپایلر بیشتر کد را از نظر صحت نوع (Type) بررسی میکند و در صورت بروز خطا، روند کامپایل متوقف خواهد شد. با این وجود سی‌شارپ اجازه میدهد که کدهای داینامیک نیز داشته باشیم؛ کدهایی که در زمان کامپایل برای کامپایلر ناشناس هستند و اگر خطای نوع در آنها وجود داشته باشد، در زمان اجرا مشخص شده و باعث توقف برنامه میشود. 

Type Safety

ایمنی نوع، قاعده‌ای است در زبانهای برنامه‌نویسی که اجازه نمیدهد متغیرها، مقادیری را دریافت کنند که متفاوت با نوع تعریف شده‌ی آنها باشد. اگر این بررسی وجود نداشت، در زمان اجرا مقادیر خوانده شده از حافظه باعث رفتاری غیر قابل پیش‌بینی میشد؛ مثلا در یک متغیر عددی، مقدار رشته‌ای ذخیره و در زمان اجرا با یک مقدار عددی دیگر جمع بسته و نمایش داده شود. کامپایلر همچنین بررسی اعضای اعلان نشده‌ی متغیرها را نیز انجام میدهد که در قطعه کد زیر آمده‌است:
string text = “String value”;
int textLength = text.Length;
int textMonth = text.Month; // won’t compile
با این حال ایمنی نوع در سی‌شارپ کاملا قابل اعتماد نیست و میشود به روشی آن را دور زد!  
public interface IGeometricShape
{
     double Circumference { get; }
     double Area { get; }
}
public class Square : IGeometricShape
{
     public double Side { get; set; }
     public double Circumference => 4 * Side;
     public double Area => Side * Side;
}
public class Circle : IGeometricShape
{
     public double Radius { get; set; }
     public double Circumference => 2 * Math.PI * Radius;
     public double Area => Math.PI * Radius * Radius;
}

IGeometricShape circle = new Circle { Radius = 1 };
Square square = ((Square)circle); // no compiler error
var side = square.Side;
در خط کدی که با کامنت مشخص شده، هر چند که دیده میشود نوع circle نمیتواند به نوع square تبدیل شود، اما این کد بدون خطا کامپایل و خطای InvalidCastException  در زمان اجرا رخ خواهد داد. به دلیل اینکه هر دو نوع circle و square از نوع پایه IGeometricShape هستند، کامپایلر خطایی نخواهد گرفت؛ اما در زمان اجرا و زمانیکه برنامه میخواهد اجزاء circle را به square تبدیل کند، مشخص میشود که امکان تبدیل کامل circle به square نیست و خطا رخ خواهد داد.

Dynamic Binding

توسط انقیاد پویا در سی‌شارپ، کامپایلر بررسی نوع را در زمان کامپایل انجام نخواهد داد. کامپایلر فرض را بر این میگیرد که کد معتبر است و تمام متغیرها به درستی قابل دسترسی هستند. بررسی‌ها در زمان اجرا خواهند بود و زمانی خطا رخ خواهد داد که مثلا دسترسی به یک عضو از یک متغیر امکانپذیر نباشد؛ به این دلیل که آن عضو برای آن نوع وجود ندارد. 
توسط کلمه کلیدی dynamic میتوان متغیرهایی را تعریف کرد که در زمان کامپایل از نظر نوع بررسی نشوند؛ مانند مثال زیر.
dynamic text = “String value”;
int textLength = text.Length;
int textMonth = text.Month; // throws exception at runtime
واضح است که مثال بالا بی‌فایده است؛  اولا خطا در زمان کامپایل مشخص نمیشود و ثانیا مدیریت خطا در زمان اجرا بر کارآیی برنامه تاثیر خواهد داشت. روش دیگر استفاده از dynamic که کارآیی پایینی دارد در مثال زیر آمده.  
public dynamic GetAnonymousType()
{
  return new
    {
        Name = “John”,
        Surname = “Doe”,
        Age = 42
    };
}

dynamic value = GetAnonymousType();
Console.WriteLine($”{value.Name} {value.Surname}, {value.Age}”);
در مثال بالا نوع بازگشتی متد و متغیری که برای نگهداری نوع بازگشتی تعریف شده از نوع dynamic هستند. هر چند که در زمان کامپایل میشود هر مقداری و نوعی را از متد بازگشت داد، اما مانند مثال قبل، تا زمان اجرا، صحت اینکه آیا واقعا چنین نوعی جهت بازگشت وجود دارد یا نه و همچنین اساسا نوع بازگشت داده شده قابل استفاده و تبدیل هست یا نه، بررسی نخواهد شد. مضاف بر این مشکلات، IntelliSense نخواهیم داشت و اگر بخواهیم از یک اسمبلی دیگر به متد بالا دسترسی پیدا کنیم با خطای RuntimeBinderException مواجه خواهیم شد؛ علت این است که  نوع‌های anonymous به صورت internal اعلان می‌شوند. اما میشود استفاده‌های بهتری از نوع dynamic داشت؛ برای مثال زمان استفاده از کتابخانه‌ی JSON.NET که نمونه‌ای از آن در زیر آمده.
string json = @"
{
     ""name"": ""John"",
     ""surname"": ""Doe"",
     ""age"": 42
}";

dynamic value = JObject.Parse(json);
Console.WriteLine($"{ value.name} { value.surname}, { value.age}");
مانند نوع anonymous در مثال قبل، متد Parse میتواند مقادیر را به صورت پویا برگشت دهد و میتوان از این مقادیر مانند خصوصیات شیء ایجاد شده، از JSON استفاده کرد، بدون آنکه کامپایلر از وجود آنها اطلاعی داشته باشد. به این ترتیب در زمان اجرا میشود اشیاء JSON را به برنامه داد و از مقادیر آن مانند دسترسی به یک property استفاده کرد؛ کاری که نمیشود با نوعهای anonymous که در مثال بالاتر آورده شد انجام داد. برای حل این مسئله میتوان از دو شیء کمکی در کتابخانه NET Framework. استفاده کرد.

ExpandoObject

بین این دو شیء، ExpandoObject ساده‌تر است. به همراه کلمه کلیدی dynamic، این شیء اجازه میدهد که به نوع ساخته شده از آن در زمان اجرا و به صورت پویا، عضوی اضافه یا حذف کنیم؛ این اعضا میتوانند متد هم باشند.
dynamic person = new ExpandoObject();
person.Name = "John";
person.Surname = "Doe";
person.Age = 42;
person.ToString = (Func<string>)(() => $”{person.Name} {person.Surname}, {person. Age}”);

Console.WriteLine($"{ person.Name}{ person.Surname}, { person.Age}");

  برای اینکه ببینیم در زمان اجرا چه اعضایی به این شی اضافه شده، می‌توان نمونه ساخته شده از آن را به نوع <IDictionary<string, object تبدیل و در یک حلقه به آنها دسترسی پیدا کرد. از همین طریق هم میشود عضوی را حذف کرد.

var dictionary = (IDictionary<string, object>)person;
foreach (var member in dictionary)
{
     Console.WriteLine($”{member.Key} = {member.Value}”);
}
dictionary.Remove(“ToString”);

DynamicObject

از آنجایی که ExpandoObject برای سناریو‌های ساده کاربرد دارد و کنترل کمتری بر روی اعضا و نمونه‌های ایجاد شده‌ی توسط آن داریم، می‌توان از شیء DynamicObject استفاده کرد؛ البته نیاز به کدنویسی بیشتری دارد. پیاده‌سازی اعضا برای شیء DynamicObject در یک کلاس صورت میگیرد که در زیر آورده شده‌است:

class MyDynamicObject : DynamicObject
{
       private readonly Dictionary<string, object> members = new Dictionary<string, object>();

       public override bool TryGetMember(GetMemberBinder binder, out object result)
       {
              if (members.ContainsKey(binder.Name))
              {
                  result = members[binder.Name];
                  return true;
              }
              else
              {
                  result = null;
                  return false;
             }
       }

      public override bool TrySetMember(SetMemberBinder binder, object value)
      {
               members[binder.Name] = value;
              return true;
      }

      public bool RemoveMember(string name)
      {
            return members.Remove(name);
      }

}

dynamic person = new MyDynamicObject();
person.Name = “John”;
person.Surname = “Doe”;
person.Age = 42;
person.AsString = (Func<string>)(() => $”{person.Name} {person.Surname}, {person.
Age}”);
یک نکته در قطعه کد بالا وجود دارد. در شیء ExpandoObject، متد ToString را اضافه کردیم، اما برای شیء DynamicObject نام آن را تغییر داده و مثلا AsString گذاشتیم. اگر از نام ToString استفاده میکردیم در زمان فراخوانی، متد پیش‌فرض کلاس DynamicObject فراخوانی میشد. DynamicObject زمانی یک عضو پویا را فراخوانی میکند که آن عضو جدید از قبل وجود نداشته باشد. از آنجا که خود کلاس، متد ToString را دارد متد TryGetMember برای فراخوانی کردن آن اجرا نخواهد شد.
نظرات مطالب
فعال سازی عملیات CRUD در Kendo UI Grid
در مثال دات نت کور این مساله در بخش Sample2 داخل کنترلر دو پارامتر رو(string param1, string param2) ذکر کردین که تو View همون کنترلر اشاره شده که میشه ارسال اطلاعات اضافی کرد به سمت سرور توسط این بخش ارسال اطلاعات اضافی و سفارشی به سرور در حین درخواست  . ولی من میخوام یه ای دی از نوع int رو این شکلی ارسال کنم ولی هر بار که چک میکنم نال میفرسته داخل کنترلر.

این متد برای kendo grid بخش read  هستش داخل کنترلر :

public IActionResult CurrentPostTags(int blogID)
{
    var dataString = this.HttpContext.GetJsonDataFromQueryString();
    var request = JsonConvert.DeserializeObject<DataSourceRequest>(dataString);
    var tags = _blogRepository.GetAllTags(blogID).OrderBy(s => s.Id);
    var currenttags = _mapper.Map<IList<TagsViewModel>>(tags);
            if (currenttags == null)
            {
                return NotFound();

            }
            return Json(currenttags.AsQueryable()
                       .ToDataSourceResult(request.Take, request.Skip, request.Sort, 
                                                       request.Filter));
}

که میخوام blogID رو دریافت کنم از سمت کندو گرید ولی نال میگیره هر بار. حتی یه رشته هم میفرستم باز نال برمیگردونه.

اینم گرید بنده :

 $(function() {
            var currentTagsDataSource = new kendo.data.DataSource({
                transport: {
                    read: {
                        url: "@Url.Action("CurrentPostTags", "Admin")",
                        dataType: "json",
                        contentType: 'application/json; charset=utf-8',
                        type: 'GET',
                        data : { blogID: @BlogID }
                        
                    },

مطالب
خلاصه‌ای از LINQ to XML

در این مقاله مروری سریع و کاربردی خواهیم داشت بر توانایی‌های مقدماتی LINQ to XML .

فایل Employee.XML را با محتویات زیر در نظر بگیرید:

<Employees>
<Employee>
<Name>Vahid</Name>
<Phone>11111111</Phone>
<Department>IT</Department>
<Age>52</Age>
</Employee>
<Employee>
<Name>Farid</Name>
<Phone>124578963</Phone>
<Department>Civil</Department>
<Age>35</Age>
</Employee>
<Employee>
<Name>Mehdi</Name>
<Phone>1245788754</Phone>
<Department>HR</Department>
<Age>30</Age>
</Employee>
</Employees>

1- چگونه یک فایل XML را جهت استفاده توسط LINQ بارگذاری کنیم؟

قبل از شروع، اسمبلی System.Xml.Linq باید به ارجاعات برنامه اضافه شود. سپس:

using System.Xml.Linq;

XDocument xDoc = XDocument.Load("Employee.xml");

2- اگر محتویات XML دریافتی به صورت رشته بود (مثلا از یک دیتابیس دریافت شد)، اکنون چگونه باید آن‌را بارگذاری کرد؟

این‌کار را با استفاده از یک StringReader به صورت زیر می‌توان انجام داد:

// loading XML from string
StringReader sr = new StringReader(stringXML);
XDocument xDoc = XDocument.Load(sr);

3- چگونه یک کوئری ساده شامل تمامی رکوردهای Employee مجموعه Employees را تهیه کنیم؟

using System.Collections;

IEnumerable<XElement> empList = from e in xDoc.Root.Elements("Employee") select e;
توسط کوئری فوق، تمامی رکوردهای کارکنان در یک Collection در اختیار ما خواهند بود. نکته‌ی مهم عبارت LINQ فوق، xDoc.Root.Elements("Employee") می‌باشد. به این صورت از xDoc بارگذاری شده، ابتدا Root و یا همان محتوای فایل XML را جهت بررسی انتخاب کرده و سپس گره‌های مرتبط با کارکنان را انتخاب می‌کنیم.
اکنون که مجموعه کارکنان توسط متغیر empList در اختیار ما است، دسترسی به محتویات آن به سادگی زیر خواهد بود:

foreach (XElement employee in empList)
{
foreach (XElement e in employee.Elements())
{
Console.WriteLine(e.Name + " = " + e.Value);
}
}
در این‌جا حلقه خارجی اطلاعات کلی تمامی کارکنان را باز می‌گرداند و حلقه داخلی اطلاعات یک گره دریافت شده را نمایش می‌دهد.

4- کوئری بنویسید که اطلاعات تمامی کارکنان بخش HR را باز گرداند.

IEnumerable<XElement> hrList = from e in xDoc.Root.Elements("Employee")
where e.Element("Department").Value == "HR"
select e;

همانطور که ملاحظه می‌کنید همانند عبارات SQL ، در تمامی عناصر متعلق به کارکنان، عناصری که دپارتمان آن‌ها مساوی HR است بازگشت داده می‌شود.

5- کوئری بنویسید که لیست تمامی کارکنان بالای 30 سال را ارائه دهد.

IEnumerable<XElement> tList = from e in xDoc.Root.Elements("Employee")
where int.Parse(e.Element("Age").Value) > 30
select e;

چون حاصل e.Element("Age").Value یک رشته است، برای اعمال فیلترهای عددی باید این رشته‌ها تبدیل به عدد شوند. به همین جهت از int.Parse استفاده شده است.

6- کوئری بنویسید که لیست تمامی کارکنان بالای 30 سال را مرتب شده بر اساس نام باز گرداند.

IEnumerable<XElement> tList = from e in xDoc.Root.Elements("Employee")
where int.Parse(e.Element("Age").Value) > 30
orderby e.Element("Name").Value
select e;
در اینجا همانند عبارات SQL از orderby جهت مرتب سازی بر اساس عناصر نام استفاده شده است.

7- تبدیل نتیجه‌ی یک کوئری LINQ به لیستی از اشیاء

مفهومی به سی شارپ 3 اضافه شده است به نام anonymous types . برای مثال:



توسط این قابلیت می‌توان یک شیء را بدون نیاز به تعریف ابتدایی آن ایجاد کرد و حتی از intelliSense موجود در IDE نیز بهره مند شد. این نوع‌های ناشناس توسط واژه‌های کلیدی new و var تولید می‌شوند. کامپایلر به صورت خودکار برای هر anonymous type یک کلاس ایجاد می‌کند.
دقیقا از همین توانایی در LINQ نیز می‌توان استفاده نمود:

var empList = from e in xDoc.Root.Elements("Employee")
orderby e.Element("Name").Value
select new
{
Name = e.Element("Name").Value,
Phone = e.Element("Phone").Value,
Department = e.Element("Department").Value,
Age = int.Parse(e.Element("Age").Value)
};
در این‌جا حاصل کوئری، تبدیل به لیستی از اشیاءanonymous می‌شود. اکنون برای نمایش آن‌ها نیز می‌توان از واژه کلیدی var استفاده نمود که از هر لحاظ نسبت به روش اعمال foreach بر روی Xelement ها که در مثال 3 مشاهده کردیم خواناتر است:

foreach (var employee in empList)
{
Console.WriteLine("Name = " + employee.Name);
Console.WriteLine("Dep = " + employee.Department);
Console.WriteLine("Phone = " + employee.Phone);
Console.WriteLine("Age = " + employee.Age);
}
و البته بدیهی است که می‌توان از anonymous types استفاده نکرد و دقیقا تعریف شیء را پیش از انتخاب آن نیز مشخص نمود. برای مثال:

public class Employee
{
public string Name { get; set; }
public string Phone { get; set; }
public string Department { get; set; }
public int Age { get; set; }
}
در این حالت، قسمت select new عبارت LINQ ما به select new Employee تغییر خواهد کرد.
برای مثال اگر بخواهیم لیست دریافتی را به صورت یک لیست جنریک بازگشت دهیم خواهیم داشت:

public class Employee
{
public string Name { get; set; }
public string Phone { get; set; }
public string Department { get; set; }
public int Age { get; set; }
}

List<Employee> Get()
{
XDocument xDoc = XDocument.Load("Employee.xml");
var items =
from e in xDoc.Root.Elements("Employee")
orderby e.Element("Name").Value
select new Employee
{
Name = e.Element("Name").Value,
Phone = e.Element("Phone").Value,
Department = e.Element("Department").Value,
Age = int.Parse(e.Element("Age").Value)
};
return items.ToList();
}

مطالب
نحوه نمایش فیلدهای تصویری و همچنین بارگذاری تصاویر از مسیری مشخص در PdfReport
عموما برای نمایش تصاویر در گزارشات، یکی از دو حالت زیر وجود دارد:
الف) مسیر تصاویر موجود در فایل سیستم، در بانک اطلاعاتی ذخیره شده‌اند و قرار است گزارش نهایی در ستونی مشخص شامل این تصاویر باشد.
ب) محتوای بایناری تصاویر در خود بانک اطلاعاتی ذخیره شده‌اند و نیاز است آن‌ها را در گزارشات نمایش دهیم.

هر دو حالت فوق در PdfReport پشتیبانی می‌شوند و در ادامه نحوه انجام این موارد را بررسی خواهیم کرد.

الف) بارگذاری تصاویر از فایل سیستم

ابتدا مدل زیر را در نظر بگیرید:
namespace PdfReportSamples.Models
{
    public class ImageRecord
    {
        public int Id { set; get; }
        public string ImagePath { set; get; }
        public string Name { set; get; }
    }
}
توسط آن تعدادی رکورد را که ImagePath آن‌ها فایل‌هایی بر روی سیستم هستند، خواهیم ساخت:
using System;
using System.Collections.Generic;
using iTextSharp.text.pdf;
using PdfReportSamples.Models;
using PdfRpt.Core.Contracts;
using PdfRpt.FluentInterface;

namespace PdfReportSamples.ImageFilePath
{
    public class ImageFilePathPdfReport
    {
        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(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.SnowyPineTemplate);
            })
            .MainTablePreferences(table =>
            {
                table.ColumnsWidthsType(TableColumnWidthType.Relative);
            })
            .MainTableDataSource(dataSource =>
            {
                var listOfRows = new List<ImageRecord>
                                             {
                                                 new ImageRecord
                                                     {
                                                         Id=1,
                                                         ImagePath =  AppPath.ApplicationPath + "\\Images\\01.png",
                                                         Name = "Rnd"
                                                     },
                                                 new ImageRecord
                                                     {
                                                         Id=2,
                                                         ImagePath =  AppPath.ApplicationPath + "\\Images\\02.png",
                                                         Name = "Bug"
                                                     },
                                                 new ImageRecord
                                                     {
                                                         Id=3,
                                                         ImagePath =  AppPath.ApplicationPath + "\\Images\\03.png",
                                                         Name = "Stuff"
                                                     },
                                                 new ImageRecord
                                                     {
                                                         Id=4,
                                                         ImagePath =  AppPath.ApplicationPath + "\\Images\\04.png",
                                                         Name = "Sun"
                                                     }
                                             };
                dataSource.StronglyTypedList(listOfRows);
            })
            .MainTableColumns(columns =>
            {
                columns.AddColumn(column =>
                {
                    column.PropertyName("rowNo");
                    column.IsRowNumber(true);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(0);
                    column.Width(1);
                    column.HeaderCell("ردیف");
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName<ImageRecord>(x => x.Id);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(1);
                    column.Width(3);
                    column.HeaderCell("شماره");
                    column.ColumnItemsTemplate(t => t.Barcode(new Barcode39()));
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName<ImageRecord>(x => x.ImagePath);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(2);
                    column.Width(3);
                    column.HeaderCell("تصویر");
                    column.ColumnItemsTemplate(t => t.ImageFilePath(defaultImageFilePath: string.Empty, fitImages: false));
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName<ImageRecord>(x => x.Name);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(3);
                    column.Width(2);
                    column.HeaderCell("نام");
                });
            })
            .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\\RptImageFilePathSample.pdf"));
        }
    }
}

توضیحات:
- در متد MainTableDataSource، یک سری رکورد دلخواه را بر اساس تعدادی تصویر مشخص ایجاد کرده‌ایم. این تصاویر در پوشه bin\images مثال‌های PdfReport قرار دارند. سپس آن‌ها را توسط متد dataSource.StronglyTypedList، در اختیار PdfReport قرار داده‌ایم.
- در مرحله بعد، توسط متد MainTableColumns، ستون‌های دلخواه گزارش را ایجاد کرده‌ایم.
دو نکته در اینجا مهم هستند:
الف) برای نمایش یک تصویر از فایل سیستم، فقط کافی است از ColumnItemsTemplate متناظر با آن استفاده کرد (حالت پیش فرض، نمایش متنی اطلاعات است). برای نمونه قالب پیش فرض ImageFilePath به نحو زیر قابل استفاده است:
 column.ColumnItemsTemplate(t => t.ImageFilePath(defaultImageFilePath: string.Empty, fitImages: false));
در اینجا در آرگومان اول آن می‌توان مسیر تصویری را مشخص کرد که در صورت موجود نبودن تصویر مشخص شده در منبع داده، بهتر است نمایش داده شود. اگر string.Empty وارد شد، از این مورد صرفنظر خواهد شد. آرگومان دوم آن مشخص می‌کند که آیا تصویر نمایش داده شده باید با ابعاد سلول متناظر با آن هماهنگ شده و نمایش داده شود یا خیر.
ب) در کتابخانه iTextSharp که پایه PdfReport است، امکان تهیه بارکد هم وجود دارد. برای مثال این بارکدها توسط قالب زیر، قابل استفاده خواهند شد:
 column.ColumnItemsTemplate(t => t.Barcode(new Barcode39()));
تصویری از حاصل این گزارش را در ذیل مشاهده می‌کنید:


ب) بارگذاری محتوای تصاویر از بانک اطلاعاتی

ابتدا کدهای کامل این مثال را در نظر بگیرید:
using System;
using PdfRpt.Core.Contracts;
using PdfRpt.FluentInterface;

namespace PdfReportSamples.DbImage
{
    public class DbImagePdfReport
    {
        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(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.AutumnTemplate);
             })
             .MainTablePreferences(table =>
             {
                 table.ColumnsWidthsType(TableColumnWidthType.FitToContent);
             })
             .MainTableDataSource(dataSource =>
             {
                 dataSource.GenericDataReader(
                   providerName: "System.Data.SQLite",
                   connectionString: "Data Source=" + AppPath.ApplicationPath + "\\data\\blogs.sqlite",
                   sql: @"SELECT [url], [name], [NumberOfPosts], [AddDate], [thumbnail]
                               FROM [tblBlogs]
                               WHERE [NumberOfPosts]>=@p1",
                   parametersValues: new object[] { 10 }
               );
             })
             .MainTableEvents(events =>
             {
                 events.DataSourceIsEmpty(message: "There is no data available to display.");
             })
             .MainTableSummarySettings(summary =>
             {
                 summary.OverallSummarySettings("جمع کل");
                 summary.PreviousPageSummarySettings("نقل از صفحه قبل");
             })
             .MainTableColumns(columns =>
             {
                 columns.AddColumn(column =>
                 {
                     column.PropertyName("rowNo");
                     column.IsRowNumber(true);
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(0);
                     column.HeaderCell("ردیف");
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName("url");
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(1);
                     column.HeaderCell("آدرس");
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName("name");
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(2);
                     column.HeaderCell("نام");
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName("NumberOfPosts");
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(3);
                     column.HeaderCell("تعداد پست");
                     column.ColumnItemsTemplate(template =>
                     {
                         template.TextBlock();
                         template.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                     });
                     column.AggregateFunction(aggregateFunction =>
                     {
                         aggregateFunction.NumericAggregateFunction(AggregateFunction.Sum);
                         aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                     });
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName("AddDate");
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(4);
                     column.HeaderCell("تاریخ ثبت");
                     column.ColumnItemsTemplate(template =>
                     {
                         template.TextBlock();
                         template.DisplayFormatFormula(obj => obj == null ? string.Empty : ((DateTime)obj).ToString("dd/MM/yyyy HH:mm"));
                     });
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName("thumbnail");
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(5);
                     column.HeaderCell("تصویر");
                     column.ColumnItemsTemplate(t => t.ByteArrayImage(defaultImageFilePath: string.Empty, fitImages: false));
                 });
             })
             .Export(export =>
             {
                 export.ToXml();
                 export.ToExcel();
             })
             .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\RptDbImageSample.pdf"));
        }
    }
}

توضیحات:
بانک اطلاعاتی SQLite موجود در پوشه bin\data سورس PdfReport، حاوی فیلدی است به نام thumbnail که در آن محتوای یک سری تصویر ذخیره شده‌اند.
در اینجا توسط کوئری زیر قصد داریم این اطلاعات را خوانده و نمایش دهیم:
SELECT [url], [name], [NumberOfPosts], [AddDate], [thumbnail]
FROM [tblBlogs]
WHERE [NumberOfPosts]>=@p1
همانطور که ملاحظه می‌کنید، در اینجا نیز تنها کافی است قالب ستون مرتبطی را انتخاب کنیم:
column.ColumnItemsTemplate(t => t.ByteArrayImage(defaultImageFilePath: string.Empty, fitImages: false));
قالب ByteArrayImage، اطلاعات باینری فیلد thumbnail را خوانده و تبدیل به تصاویر قرار داده شده در فایل گزارش نهایی می‌کند. پارامترهای آن، با پارامترهای قالب ImageFilePath که پیشتر توضیح داده شد، یکی هستند. 
مطالب
استفاده از Awesomium.NET در برنامه‌های وب
برای تهیه تصاویر سایت‌های معرفی شده در قسمت اشتراک‌های سایت، پیشتر از کنترل WebBrowser دات نت که در پشت صحنه از امکانات IE کمک می‌گیرد، استفاده می‌کردم. بسیار ناپایدار است؛ به روز رسانی مشکلی داشته و وابسته است به سیستم عامل جاری سیستم. برای مثال مرتبا برای تهیه تصاویر بند انگشتی (Thumbnails) سایت‌های تهیه شده با بوت استرپ کرش می‌کرد و این کرش چون از نوع unmaged code است، عملا پروسه IIS وب سایت را از کار می‌انداخت و در این حالت سایت تا ری‌استارت بعدی IIS در دسترس نبود. برای حل این مشکل و بهبود کیفیت تصاویر تهیه شده، از پروژه Awesomium که در حقیقت مرورگر کروم را جهت استفاده در انواع و اقسام زبان‌های برنامه نویسی محصور می‌کند، کمک گرفته شد؛ که شرح آن‌را در ادامه ملاحظه خواهید کرد.


دریافت و نصب Awesomium

پروژه Awesomium دارای یک SDK است که از اینجا قابل دریافت می‌باشد. بعد از نصب آن در مسیر Awesomium SDK\1.7.3.0\wrappers\Awesomium.NET\Assemblies\Packed می‌توانید محصور کننده‌ی دات نتی آن‌را مشاهده کنید. منظور از Packed در اینجا، استفاده از DLLهای فشرده شده‌ی native آن است که در مسیر Awesomium SDK\1.7.3.0\build\bin\packed کپی شده‌اند. بنابراین برای توزیع این نوع برنامه‌ها نیاز است اسمبلی دات نتی Awesomium.Core.dll به همراه دو فایل بومی icudt.dll و awesomium.dll ارائه شوند.


تهیه تصاویر سایت‌ها به کمک Awesomium.NET

پس از نصب Awesomium اگر به مسیر Documents\Awesomium SDK Samples\1.7.3.0\Awesomium.NET\Samples\Core\CSharp\BasicSample مراجعه کنید، مثالی را در مورد تهیه تصاویر سایت‌ها به کمک Awesomium.NET، مشاهده خواهید کرد. خلاصه‌ی آن چند سطر ذیل است:
            try
            {
                using (WebSession mywebsession = WebCore.CreateWebSession(
new WebPreferences() { CustomCSS = "::-webkit-scrollbar { visibility: hidden; }" }))
                {
                    using (var view = WebCore.CreateWebView(1240, 1000, mywebsession))
                    {
                        view.Source = new Uri("https://site.com/");

                        bool finishedLoading = false;
                        view.LoadingFrameComplete += (s, e) =>
                        {
                            if (e.IsMainFrame)
                                finishedLoading = true;
                        };

                        while (!finishedLoading)
                        {
                            Thread.Sleep(100);
                            WebCore.Update();
                        }

                        using (var surface = (BitmapSurface)view.Surface)
                        {
                            surface.SaveToJPEG("result.jpg");
                        }
                    }
                }
            }
            finally
            {
                WebCore.Shutdown();
            }
کار با ایجاد یک WebSession شروع می‌شود. سپس با مقدار دهی CustomCSS، اسکرول بار صفحات را جهت تهیه تصاویری بهتر مخفی می‌کنیم. سپس یک WebView آغاز شده و منبع آن به Url مدنظر تنظیم می‌شود. در ادامه باید اندکی صبر کنیم تا بارگذاری سایت خاتمه یافته و نهایتا می‌توانیم سطح این View را به صورت یک تصویر ذخیره کنیم.


مشکل! این روش در برنامه‌های ASP.NET کار نمی‌کند!

مثال همراه آن یک مثال کنسول ویندوزی است و به خوبی کار می‌کند؛ اما در برنامه‌های وب پس از چند روز سعی و خطا مشخص شد که:
الف) WebCore.Shutdown فقط باید در پایان کار یک برنامه فراخوانی شود. یعنی اصلا نیازی نیست تا در برنامه‌های وب فراخوانی شود.
 System.InvalidOperationException: You are attempting to re-initialize the WebCore.
The WebCore must only be initialized once per process and must be shut down only when the process exits.
ب) Awesomium فقط در یک ترد کار می‌کند. به این معنا که اگر کدهای فوق را در یک صفحه‌ی وب فراخوانی کنید، بار اول کار خواهد کرد. بار دوم برنامه کرش می‌کند؛ با این پیغام خطا:
 System.AccessViolationException: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt. at Awesomium.Core.NativeMethods.WebCore_CreateWebView_1(HandleRef jarg1, Int32 jarg2, Int32 jarg3, HandleRef jarg4)
چون هر صفحه‌ی وب در یک ترد مجزا اجرا می‌شود، عملا استفاده‌ی مستقیم از Awesomium در آن ممکن نیست.
خطای فوق هم از آن نوع خطاهایی است که پروسه‌ی IIS را درجا خاموش می‌کند.


استفاده از Awesomium در یک ترد پس زمینه

راه حلی که نهایتا پاسخ داد و به خوبی و پایدار کار می‌کند، شامل ایجاد یک ترد مجزای Awesomium در زمان آغاز برنامه‌ی وب و زنده نگه داشتن آن تا زمان پایان کار برنامه است.
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using System.Web;
using Awesomium.Core;

namespace AwesomiumWebModule
{
    public class AwesomiumModule : IHttpModule
    {
        private static readonly Thread WorkerThread = new Thread(awesomiumWorker);
        private static readonly ConcurrentQueue<AwesomiumRequest> TaskQueue = new ConcurrentQueue<AwesomiumRequest>();
        private static bool _isRunning = true;

        static AwesomiumModule()
        {
            WorkerThread.Start();
        }       

        private static void awesomiumWorker()
        {
            while (_isRunning)
            {
                if (TaskQueue.Count != 0)
                {
                    AwesomiumRequest outRequest;
                    if (TaskQueue.TryDequeue(out outRequest))
                    {
                        var img = AwesomiumThumbnail.FetchWebPageThumbnail(outRequest);
                        File.WriteAllBytes(outRequest.SavePath, img);
                        Thread.Sleep(500);
                    }
                }
                Thread.Sleep(5);
            }
        }

        public void Dispose()
        {
            _isRunning = false;
            WebCore.Shutdown();
        }

        public void Init(HttpApplication context)
        {
            context.EndRequest += endRequest;
        }

        static void endRequest(object sender, EventArgs e)
        {
            var url = HttpContext.Current.Items[Constants.AwesomiumRequest] as AwesomiumRequest;
            if (url!=null)
            {
                TaskQueue.Enqueue(url);
            }
        }
    }
}
در اینجا اگر در برنامه‌های وب فرم، از طریق HttpContext.Current.Items.Add و یا در برنامه‌های MVC به کمک this.HttpContext.Items.Add یک آیتم جدید، با کلید Constants.AwesomiumRequest و از نوع کلاس AwesomiumRequest دریافت گردد، مقدار آن به یک ConcurrentQueue اضافه خواهد شد. این صف در یک ترد مجزا مدام در حال بررسی است. اگر مقداری به آن اضافه شده‌است، از صف خارج شده و پردازش خواهد شد.
نمونه‌ی استفاده از آن، در سمت یک برنامه‌ی وب نیز به صورت زیر است. ابتدا ماژول تهیه شده باید در برنامه ثبت شود:
<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
    <httpModules>
      <add name="AwesomiumWebModule" type="AwesomiumWebModule.AwesomiumModule"/>
    </httpModules>
  </system.web>

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules>
      <add name="AwesomiumWebModule" type="AwesomiumWebModule.AwesomiumModule"/>
    </modules>
  </system.webServer>
</configuration>
سپس باید تنها مدیریت  HttpContext.Current.Items در سمت برنامه صورت گیرد:
        protected void btnStart_Click(object sender, EventArgs e)
        {
            var host = new Uri(txtUrl.Text).Host;
            HttpContext.Current.Items.Add(Constants.AwesomiumRequest, new AwesomiumRequest
            {
                Url = txtUrl.Text,
                SavePath = Path.Combine(HttpRuntime.AppDomainAppPath, "App_Data\\Thumbnails\\" + host + ".jpg"),
                TempDir = Path.Combine(HttpRuntime.AppDomainAppPath, "App_Data\\Temp")
            });
            lblInfo.Text = "Please wait. Your request will be served shortly.";
        }
در اینجا کاربر درخواست خود را در صف قرار می‌دهد. پس از مدتی کار آن در WorkerThread ماژول تهیه شده انجام گردیده و تصویر نهایی تهیه می‌شود.
Url، آدرس وب سایتی است که می‌خواهید تصویر آن تهیه شود. SavePath مسیر کامل فایل jpg نهایی است که قرار است دریافت و ذخیره گردد. TempDir محل ذخیره سازی فایل‌های موقتی Awesomium است. Awesomium یک سری کوکی، تصاویر و فایل‌های هر سایت را به این ترتیب کش کرده و در دفعات بعدی سریعتر عمل می‌کند.

پروژه‌ی کامل آن‌را از اینجا می‌توانید دریافت کنید:
AwesomiumWebApplication_V1.0.zip