مطالب
استفاده از قالب ویژوال استودیو 2013 برای برنامه‌های ویندوز فرم
در این نوشتار قصد داریم تا Theme ویژوال استودیو 2013 را برای برنامه‌های ویندوز شبیه سازی کنیم. در مرحله اول یک پروژه از نوع ClassLibrary می‌سازیم و پس از آن یک کلاس که از کلاس ToolStripProfessionalRenderer ارث بری کند را ایجاد می‌کنیم. در اینجا ما نام کلاس را BlueMenuStrip انتخاب می‌کنیم. از این کلاس برای تغییر رنگ منوها استفاده می‌شود. سپس متد OnRenderMenuItemBackground آن‌را Override می‌کنیم. 
using System.Drawing;
using System.Windows.Forms;

namespace Navasser.Theme.VisualStudio
{
    public class BlueMenuStrip:ToolStripProfessionalRenderer
    {
        protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e)
        {
            var borderColor = ColorTranslator.FromHtml("#E5C365");//Menu Item Border
            var selectedMenuBackColor = ColorTranslator.FromHtml("#FDF4BF");//Menu Item Background
            var menuOpenedBackColor = ColorTranslator.FromHtml("#EAF0FF");
            var borderPen = new Pen(borderColor);   

            if (e.Item.Selected)//اگر آیتمی از منوها انتخاب شد
            {
                var selectedMenuBrush = new SolidBrush(selectedMenuBackColor);
                var selectedItemBounds = new Rectangle(Point.Empty, e.Item.Size);//اقدام به پر کردن یک مستطیل به اندازه ابعاد آیتم انتخاب شده می‌کند
                e.Graphics.FillRectangle(selectedMenuBrush,selectedItemBounds);
                e.Graphics.DrawRectangle(borderPen,0,0,selectedItemBounds.Width-1,selectedItemBounds.Height-1);// بوردر آیتم را رسم میکند
                e.Item.BackColor = menuOpenedBackColor;
            }
            else
            {
                base.OnRenderMenuItemBackground(e);
                e.ToolStrip.BackColor = ColorTranslator.FromHtml("#EAF0FF");
            }           
        }     
    }
}
تکه کد بالا فقط برای تغییر رنگ زمینه‌ی منوها بکار می‌رود. اما برای تغییر رنگ ToolStrip‌ها یک کلاس جدید ایجاد می‌کنیم که از کلاس ToolStripProfessionalRenderer ارث بری کرده باشد و متدهای OnRenderToolStripBackground و OnRenderButtonBackground  آن‌را Override میکنیم.
using System.Drawing;
using System.Windows.Forms;

namespace Navasser.Theme.VisualStudio
{
    public class BlueToolStrip : ToolStripProfessionalRenderer
    {
        protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e)
        {
            var toolStripBackColor = ColorTranslator.FromHtml("#D6DBE9");
            var toolStripBrush = new SolidBrush(toolStripBackColor);
            e.Graphics.FillRectangle(toolStripBrush,0,0,e.AffectedBounds.Width + 10,e.AffectedBounds.Height);
        }

        protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e)
        {
            var borderColor = ColorTranslator.FromHtml("#E5C365");//For border
            var selectedToolItemBackColor = ColorTranslator.FromHtml("#FDF4BF");
            var selectedItemBrush = new SolidBrush(selectedToolItemBackColor);
            var pressedItemBackColor = ColorTranslator.FromHtml("#FFF29D");
            var pressedItemBrush = new SolidBrush(pressedItemBackColor);
            var borderPen = new Pen(borderColor);            

            if (e.Item.Selected)
            {
                e.Graphics.FillRectangle(selectedItemBrush,0,0,e.Item.Width,e.Item.Height);
                e.Graphics.DrawRectangle(borderPen,0,0,e.Item.Width-1,e.Item.Height-1);
            }

            if (e.Item.Pressed)
            {
                e.Graphics.FillRectangle(pressedItemBrush, 0, 0, e.Item.Width, e.Item.Height);
                e.Graphics.DrawRectangle(borderPen, 0, 0, e.Item.Width - 1, e.Item.Height - 1);
            }
        }
    }
}
سپس Dll ایجاد شده را در برنامه خود Reference دهید و از Themeهای ایجاد شده استفاده نمایید. نتیجه‌ی کدهای بالا به شکل زیر است:


 همچنین می‌توانید برای انتخاب رنگ‌های دلخواه خودتان از ابزار ColorSchemer Studio استفاده کنید.

مطالب
بستن یک پنجره از طریق ViewModel با استفاده از خصوصیت های پیوست شده هنگام استفاده از الگوی MVVM
 در نظر بگیرید که یک پروژه WPF را با الگوی MVVM پیاده سازی کرده اید و نیاز پیدا می‌کنید تا یک پنجره را از طریق کد ببندید. از آنجایی که به کنترل Window درون ViewModel دسترسی ندارید، نمی‌توانید از متد Close آن برای اینکار استفاده کنید. راه‌های مختلفی برای اینکار وجود دارند، مثلا اگر از MVVM Light Toolkit استفاده می‌کنید با ارسال یک Message و نوشتن یک تکه کد در CodeBehind پنجره می‌توانید اینکار را انجام بدهید.
اما برای اینکار یک راه حل ساده‌تری بدون نیاز به نوشتن کد در CodeBehind و استفاده از Toolkit خاصی وجود دارد و آن استفاده ازخاصیت‌های پیوست شده یا Attached Properties است. برای اینکار یک خاصیت از نوع Boolean مانند زیر تعریف می‌کنیم و آن را به پنجره ای که می‌خواهیم Colse شود پیوست می‌کنیم.
namespace TestProject.XamlServices
{
    public class CloseBehavior
    {
        public static readonly DependencyProperty CloseProperty = 
DependencyProperty.RegisterAttached("Close", typeof(bool), typeof(CloseBehavior), new UIPropertyMetadata(false, OnClose));

        private static void OnClose(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (!(e.NewValue is bool) || !((bool) e.NewValue)) return;
            var win = GetWindow(sender);
            if (win != null)
                win.Close();
        }

        private static Window GetWindow(DependencyObject sender)
        {
            Window w = null;
            if (sender is Window)
                w = (Window)sender;
            return w ?? (w = Window.GetWindow(sender));
        }

        public static bool GetClose(Window target)
        {
            return (bool)target.GetValue(CloseProperty);
        }

        public static void SetClose(DependencyObject target, bool value)
        {
            target.SetValue(CloseProperty, value);
        }
    }
} 
در تکه کد بالا یک خصوصیت از نوع  Boolean  ایجاد کردیم که می‌تواند به هر پنجره ای که قرار است از طریق کد بسته شود، پیوست شود. خصوصیت‌های پیوست شده یک  Callback مربوط به تغییر مقدار دارند که یک  متداستاتیک است و مقدار جدید، از طریق  EventArg و   شیءایی که این خاصیت به آن پیوست شده نیز بعنوان Source  به آن  ارسال می‌شود. هر وقت مقدار خصوصیت، تغییر کند این متد فراخوانی می‌گردد. در کد بالا متد OnClose ایجاد شده است و زمانی که مقدار این خصوصیت برابر true می‌شود پنجره close خواهد شد.
برای استفاده از این خصوصیت و اتصال آن باید یک خصوصیت از نوع Boolean  نیز در ViewModel مربوط به Window ایجاد کنید:  
 private bool _isClose;
 public bool IsClose
   {
      get { return _isClose; }
      set
      {
       _isClose = value;
      OnClosed();
      RaisePropertyChanged("IsClose");
      }
}
و آن را به صورت زیر Bind کنید:  
<Window x:Class="TestProject.TestView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:xamlServices="clr-namespace:TestProject.XamlServices;assembly=TestProject.XamlServices"
       xamlServices:CloseBehavior.Close="{Binding IsClose}">
       ...
</Window>
پس از انجام اتصالات فوق، کافیست   هر جایی از ViewModel که نیاز است پنجره بسته شود،مقدار این خصوصیت برابر False بشود.
مطالب
چرا نباید از کوئری‌های select * استفاده کرد؟

عموما اولین پاسخی که به این سؤال داده می‌شود این است که این نوع کوئری‌ها اطلاعات زیادی را باز می‌گردانند و در نتیجه ترافیک شبکه بی‌جهت افزایش خواهد یافت. اما اگر طراحی دیتابیس صحیح بوده و اصول نرمال سازی در آن پیاده سازی شده باشد، این پاسخ آنچنان صادق نخواهد بود (زیرا جداول اینگونه دیتابیس‌ها از تعداد فیلدهای بسیاری تشکیل نخواهند شد). برای مثال به نتیجه اجرای کوئری‌های زیر بر روی دیتابیس AdventureWorks با 89 هزار رکورد، دقت بفرمائید:
SELECT * FROM Production.TransactionHistoryArchive
WHERE ReferenceOrderID < 100

SELECT ReferenceOrderLineID FROM Production.TransactionHistoryArchive
WHERE ReferenceOrderID < 100

اختلاف ترافیک شبکه در این مثال تنها 15K یا حدودا 10 درصد است (180K در مقابل 165K). هر چند ارزش بررسی و برطرف کردن را دارد اما تفاوت حاصل آنچنان قابل ملاحظه نیست.
مهم‌ترین دلیلی که اینجا باید به آن دقت داشت، تفاوت چشمگیر execution plan این دو کوئری (Ctrl-L) و بحث index coverage است. اس کیوال سرور برای اجرای بهینه کوئری‌ها از ایندکس‌های موجود استفاده خواهد کرد. اگر ایندکس تعریف شده از تمامی فیلدهای درخواستی شما تشکیل شده باشد، دیگر حتی به سراغ جدول هم نخواهد رفت (به این مفهوم، پوشش ایندکسی گفته می‌شود).



برای تولید تصویر فوق، کلیدهای Ctrl+L را در management studio فشار دهید.
این دیتابیس را از آدرس زیر می‌توانید دریافت کنید:
http://www.codeplex.com/MSFTDBProdSamples

کوئری اول از مزایای پوشش ایندکسی برخودار نخواهد بود (از روش جستجوی Clustered Index استفاده می‌کند) و در حالت دوم از Index Seek استفاده می‌گردد. حالت Index Seek یک‌صد بار بهینه‌تر از استفاده از Clustered Index عمل می‌کند زیرا در حالت کوئری اول باید تمامی رکوردهای جدول بررسی شوند (این عدد از مقایسه نتایج execution plan بدست آمده است).
تنها در صورتیکه بر روی تمامی فیلدهای جدول ایندکس تعریف کرده باشید (که اصلا توصیه نمی‌شود)، کوئری اول توسط ایندکس‌ها پوشش داده شده و سریع اجرا خواهد شد.
بنابراین اگر از کندی اجرای کوئری‌ها با تعداد رکورد بالا شکایت دارید بهتر است نگاهی به نحوه تعریف آنها داشته باشید و تنها فیلدهایی را در کوئری تعریف کنید که به آنها نیاز دارید. در این حالت از مزایای پوشش ایندکسی برخودار شده ، کوئری‌های سریعتری را خواهید داشت و همچنین در این حالت میزان مصرف CPU و حافظه نیز بر روی سرور کمتر خواهد بود.
همچنین در حالت کوئری‌هایی از نوع دوم ذکر شده، موتور بهینه ساز اس کیوال سرور پیشنهادات بهتری را برای ایجاد ایندکس‌های جدید و گوشزد نمودن کمبود آنها با ارائه included columns مناسب، ارائه می‌دهد.
بعلاوه مشخص ساختن تعداد دقیق فیلدهای مورد نیاز، نگهداری برنامه را ساده‌تر ساخته و فیلدهای اضافه شده آتی سبب تغییر رفتار کوئری‌ها برنامه نخواهند شد و استفاده نکردن از آن نشانه این است که هیچ برآوردی از ابعاد واقعی کار در دست نیست.

مآخذ:
Speed Up Your Site! 8 ASP.NET Performance Tips
The real reason SELECT * queries are bad: index coverage


مطالب دوره‌ها
ارائه کاربری ساده‌تر انتخاب چندین آیتم از یک لیست به کمک افزونه TagIt در ASP.NET MVC
چندی قبل مطلبی را در مورد بررسی تفصیلی رابطه چند به چند در این سایت مطالعه کردید. در آن مطلب صرفا به بحث ذخیره سازی اطلاعات دریافتی از کاربر اشاره شد. برای مثال اگر مطلبی چندین برچسب دارد، چگونه باید این‌ها را در بانک اطلاعاتی به نحو صحیحی ذخیره کرد.
در مطلب جاری قصد داریم با نحوه ارائه یک UI کاربر پسند برای این منظور آشنا شویم و سؤال مهم هم این است: «چگونه می‌توان کار کاربر را در حین وارد کردن تعدادی از برچسب‌های مرتبط با یک مطلب ساده‌تر کرد؟». برای این منظور یکی از راه حل‌هایی که در بسیاری از سایت‌ها مرسوم شده است، استفاده از افزونه‌هایی مانند jQuery TagIt می‌باشد که در ادامه با نحوه استفاده از آن در ASP.NET MVC آشنا خواهیم شد.


پیشنیازها:
دریافت افزونه TagIt
همچنین دریافت jQuery UI (افزونه TagIt برای نمایش لیست Auto Complete آیتم‌ها از jQuery UI در پشت صحنه استفاده می‌کند)
<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/TagIt/jquery-ui-1.8.23.custom.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/TagIt/tagit-simple-blue.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Content/TagIt/jquery-ui-1.8.23.custom.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Content/TagIt/tagit.js")" type="text/javascript"></script>
    @RenderSection("JavaScript", required: false)
</head>
که نهایتا نیاز است یک چنین تعاریفی را به فایل layout برنامه اضافه کنیم.

آشنایی با مدل برنامه

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace jQueryMvcSample04.Models
{
    public class BlogPostViewModel
    {
        [DisplayName("عنوان"), Required(ErrorMessage = "*")]
        public string Title { set; get; }

        [DisplayName("متن"), Required(ErrorMessage = "*")]
        public string Body { set; get; }

        /// <summary>
        /// آرایه‌ای محدود از برچسب‌های این مطلب خاص به صورت جی‌سون که پیشتر ثبت شده است
        /// هدف استفاده در حین ویرایش مطلب
        /// </summary>
        public string InitialTags { set; get; }

        /// <summary>
        /// آرایه‌ای جی‌سونی از تمام برچسب‌های موجود در سیستم
        /// هدف نمایش منوی انتخاب برچسب‌ها از لیست
        /// </summary>
        public string TagsSource { set; get; }

        /// <summary>
        /// آرایه‌ای از برچسب‌های وارد شده توسط کاربر در حین ثبت مطلب
        /// </summary>
        [DisplayName("برچسب‌ها"), Required(ErrorMessage = "*")]
        public string[] Tags { set; get; }

        public int? Id { set; get; }
    }
}
اگر به نام این کلاس دقت کنید، به ViewModel ختم شده است. از این لحاظ که حاوی خواصی می‌باشد که عموما جهت رندر کردن صحیح UI مورد استفاده قرار می‌گیرند و معادلی در سمت بانک اطلاعاتی نخواهند داشت.
افزونه TagIt برای نمایش اطلاعات خود به دو منبع داده نیاز دارد:
الف) TagsSource : لیستی است به فرمت JSON، از هر آنچه که در سیستم پیشتر به عنوان یک برچسب ثبت شده است. از این لیست برای نمایش منوی خودکار انتخاب آیتم‌ها استفاده می‌شود.
ب) InitialTags : لیستی است به فرمت JSON، از تمام برچسب‌های مرتبط با یک مطلب. از این اطلاعات در حین ویرایش یک مطلب استفاده خواهد شد.

در این ViewModel یک خاصیت دیگر به شکل آرایه، به نام Tags تعریف شده است که لیست برچسب‌های وارد شده توسط کاربر را دریافت خواهد کرد.


معرفی کنترلر برنامه

using System.Web.Mvc;
using jQueryMvcSample04.Extensions;
using jQueryMvcSample04.Models;

namespace jQueryMvcSample04.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index(int? id)
        {
            //در ابتدای کار تمام تگ‌های موجود در سیستم از بانک اطلاعاتی دریافت خواهند شد
            //از این تگ‌ها برای تشکیل منوی انتخاب برچسب‌ها استفاده می‌شود
            var tagsSource = new[] { "C#", "C++", "C", "ASP.NET", "MVC" }.ToJson();

            //همچنین صرفا برچسب‌های مطلب جاری که پیشتر ثبت شده‌اند نیز باید از بانک اطلاعاتی دریافت گردند
            //از این برچسب‌ها برای ویرایش یک مطلب موجود استفاده خواهد شد
            var init = new[] { "ASP.NET" }.ToJson();

            var model = new BlogPostViewModel
            {
                TagsSource = tagsSource,
                InitialTags = init,
                Id = id
            };
            return View(model);
        }

        [HttpPost]
        public ActionResult Index(BlogPostViewModel data)
        {
            if (this.ModelState.IsValid)
            {
                //todo: save data
                // ...
                return RedirectToAction(actionName: "index", controllerName: "home");
            }

            //در صورت بروز خطا مجددا اطلاعات موجود نمایش داده خواهند شد
            data.TagsSource = new[] { "C#", "C++", "C", "ASP.NET", "MVC" }.ToJson();
            data.InitialTags = data.Tags.ToJson();
            return View(data);
        }
    }
}


با توجه به توضیحاتی که ارائه شد، کنترلر برنامه ساختار واضح‌تری را یافته است. در اولین بار نمایش صفحه، لیست منبع داده تگ‌ها و همچنین تگ‌های مرتبط با یک مطلب (در صورت وجود) به View ارائه خواهند شد.
از همین ViewModel، در عملیات Post نیز استفاده گردیده و اطلاعات دریافت می‌گردد.
تعریف متد الحاقی ToJson مورد استفاده را نیز در ادامه ملاحظه می‌نمائید:
using System.Linq;
using System.Web.Script.Serialization;

namespace jQueryMvcSample04.Extensions
{
    public static class JsonExt
    {
        public static string ToJson(this string[] initialTags)
        {            
            if (initialTags == null || !initialTags.Any())
                return "[]";
            else
                return new JavaScriptSerializer().Serialize(initialTags);
        }
    }
}

و مرحله آخر تعریف View متناظر است

@model jQueryMvcSample04.Models.BlogPostViewModel
@{
    ViewBag.Title = "Index";
}
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>ثبت مطلب جدید</legend>
        @Html.HiddenFor(model => model.Id)
        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Body)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Body)
            @Html.ValidationMessageFor(model => model.Body)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Tags)
        </div>
        <div class="editor-field">
            <ul id="tagsList" dir="ltr" name="Tags">
            </ul>
            @Html.ValidationMessageFor(model => model.Tags)
        </div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}
@section JavaScript
{
    <script type="text/javascript">
    $(document).ready(function () {
            var tagsSource = @Html.Raw(Model.TagsSource);
            $('#tagsList').tagit({
                 tagSource: tagsSource, 
                 select: true, 
                 triggerKeys: ['enter', 'comma', 'tab'],
                 initialTags:  @Html.Raw(Model.InitialTags) 
              });
});
    </script>
}
در این View دو نکته حائز اهمیت هستند:
الف) برای نمایش افزونه TagIt از یک ul با id مساوی tagsList استفاده شده است.
ب) خواص اضافی موجود در ViewModel که اطلاعات JSON ایی مورد نیاز را بازگشت می‌دهند در قسمت اسکریپت صفحه مورد استفاده قرار گرفته‌اند. در اینجا نیاز است از Html.Raw استفاده شود تا اطلاعات مرتبط با JSON اشتباها encode نشده و قابل استفاده باشند.

دریافت مثال و پروژه کامل این قسمت
jQueryMvcSample04.zip
مطالب
معرفی OLTP درون حافظه‌ای در SQL Server 2014
OLTP درون حافظه‌ای، مهم‌ترین ویژگی جدید SQL Server 2014 است. موتور بانک اطلاعاتی disk based اس کیوال سرور، حدود 15 تا 20 سال قبل تهیه شد‌ه‌است و موتور جدید درون حافظه‌ای OLTP آن، بزرگترین بازنویسی این سیستم از زمان ارائه‌ی آن می‌باشد و شروع این پروژه به 5 سال قبل بر می‌گردد. علت تهیه‌ی آن نیز به نیازهای بالای پردازش‌های همزمان مصرف کنندگان این محصول در سال‌های اخیر، نسبت به 15 سال قبل مرتبط است. با استفاده از امکانات OLTP درون حافظه‌ای، امکان داشتن جداول معمولی disk based و جداول جدید memory optimized با هم در یک بانک اطلاعاتی میسر است؛ به همراه مهیا بودن تمام زیرساخت‌هایی مانند تهیه بک آپ، بازیابی آن‌ها، امنیت و غیره برای آن‌ها.



آیا جداول بهینه سازی شده‌ی برای حافظه، همان DBCC PINTABLE منسوخ شده هستند؟

در نگارش‌های قدیمی‌تر اس کیوال سرور، دستوری وجود داشت به نام DBCC PINTABLE که سبب ثابت نگه داشتن صفحات جداول مبتنی بر دیسک یک دیتابیس، در حافظه می‌شد. به این ترتیب تمام خواندن‌های مرتبط با آن جدول، از حافظه صورت می‌گرفت. مشکل این روش که سبب منسوخ شدن آن گردید، اثرات جانبی آن بود؛ مانند خوانده شدن صفحات جدیدتر (با توجه به اینکه ساختار پردازشی و موتور بانک اطلاعاتی تغییری نکرده بود) و نیاز به حافظه‌ی بیشتر تا حدی که کل کش بافر سیستم را پر می‌کرد و امکان انجام سایر امور آن مختل می‌شدند. همچنین اولین ارجاعی به یک جدول، سبب قرار گرفتن کل آن در حافظه می‌گشت. به علاوه ساختار این سیستم نیز همانند روش مبتنی بر دیسک، بر اساس همان روش‌های قفل گذاری، ذخیره سازی اطلاعات و تهیه ایندکس‌های متداول بود.
اما جداول بهینه سازی شده‌ی برای حافظه، از یک موتور کاملا جدید استفاده می‌کنند؛ با ساختار جدیدی برای ذخیره سازی اطلاعات و تهیه ایندکس‌ها. دسترسی به اطلاعات آن‌ها شامل قفل گذاری‌های متداول نیست و در آن حداقل زمان دسترسی به اطلاعات درنظر گرفته شده‌است. همچنین در آن‌ها data pages یا index pages و کش بافر نیز وجود ندارد.


نحوه‌ی ذخیره سازی و مدیریت اطلاعات جداول بهینه سازی شده برای حافظه

جداول بهینه سازی شده برای حافظه، فرمت ردیف‌های کاملا جدیدی را نیز به همراه دارند و جهت قرارگرفتن در حافظه ودسترسی سریع به آن‌ها بهینه سازی شده‌اند. برخلاف جداول مبتنی بر دیسک سخت که اطلاعات آن‌ها در یک سری صفحات خاص به نام‌های data or index pages ذخیره می‌شوند، اینگونه جداول، دارای ظروف مبتنی بر صفحه نیستند و از مفهوم چند نگارشی برای ذخیره سازی اطلاعات استفاده می‌کنند؛ به این معنا که ردیف‌ها به ازای هر تغییری، دارای یک نگارش جدید خواهند بود و بلافاصله در همان نگارش اصلی به روز رسانی نمی‌شوند.
در اینجا هر ردیف دارای یک timestamp شروع و یک timestamp پایان است. timestamp شروع بیانگر تراکنشی است که ردیف را ثبت کرده و timestamp پایان برای مشخص سازی تراکنشی بکار می‌رود که ردیف را حذف کرده است. اگر timestamp پایان، دارای مقدار بی‌نهایت باشد، به این معنا است که ردیف متناظر با آن هنوز حذف نشده‌است. به روز رسانی یک ردیف در اینجا، ترکیبی است از حذف یک ردیف موجود و ثبت ردیفی جدید. برای یک عملیات فقط خواندنی، تنها نگارش‌هایی که timestamp معتبری داشته باشند، قابل مشاهده خواهند بود و از مابقی صرفنظر می‌گردد.
در OLTP درون حافظه‌ای که از روش چندنگارشی همزمانی استفاده می‌کند، برای یک ردیف مشخص، ممکن است چندین نگارش وجود داشته باشند؛ بسته به تعداد باری که یک رکورد به روز رسانی شده‌است. در اینجا یک سیستم garbage collection همیشه فعال، نگارش‌هایی را که توسط هیچ تراکنشی مورد استفاده قرار نمی‌گیرند، به صورت خودکار حذف می‌کند؛ تا مشکل کمبود حافظه رخ ندهد.


آیا می‌توان به کارآیی جداول بهینه سازی شده برای حافظه با همان روش متداول مبتنی بر دیسک اما با بکارگیری حافظه‌ی بیشتر و استفاده از یک SSD RAID رسید؟
خیر! حتی اگر کل بانک اطلاعاتی مبتنی بر دیسک را در حافظه قرار دهید به کارآیی روش جداول بهینه سازی شده‌ی برای حافظه نخواهید رسید. زیرا در آن هنوز مفاهیمی مانند data pages و index pages به همراه یک buffer pool پیچیده وجود دارند. در روش‌های مبتنی بر دیسک، ردیف‌ها از طریق page id و row offset آن‌ها قابل دسترسی می‌شوند. اما در جداول بهینه سازی شده‌ی برای حافظه، ردیف‌های جداول با یک B-tree خاص به نام Bw-Tree در دسترس هستند.


میزان حافظه‌ی مورد نیاز برای جداول بهینه سازی شده‌ی برای حافظه

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


چه برنامه‌هایی بهتر است از امکانات OLTP درون حافظه‌ای SQL Server 2014 استفاده کنند؟

- برنامه‌هایی که در آن‌ها تعداد زیادی تراکنش کوتاه مدت وجود دارد به همراه درجه‌ی بالایی از تراکنش‌های همزمان توسط تعداد زیادی کاربر.
- اطلاعاتی که توسط برنامه زیاد مورد استفاده قرار می‌گیرند را نیز می‌توان در جداول بهینه سازی شده جهت حافظه قرار داد.
- زمانیکه نیاز به اعمال دارای write بسیار سریع و با تعداد زیاد است. چون در جداول بهینه سازی شده‌ی برای حافظه، صفحات داده‌ها و ایندکس‌ها وجود ندارند، نسبت به حالت مبتنی بر دیسک، بسیار سریعتر هستند. در روش‌های متداول، برای نوشتن اطلاعات در یک صفحه، مباحث همزمانی و قفل‌گذاری آن‌را باید در نظر داشت. در صورتیکه در روش بهینه سازی شده‌ی برای حافظه، به صورت پیش فرض از حالتی همانند snapshot isolation و همزمانی مبتنی بر نگارش‌های مختلف رکورد استفاده می‌شود.
- تنظیم و بهینه سازی جداولی با تعداد Read بالا. برای مثال، جداول پایه سیستم که اطلاعات تعاریف محصولات در آن قرار دارند. این نوع جداول عموما با تعداد Readهای بالا و تعداد Write کم شناخته می‌شوند. چون طراحی جداول مبتنی بر حافظه از hash tables و اشاره‌گرهایی برای دسترسی به رکوردهای موجود استفاده می‌کند، اعمال Read آن نیز بسیار سریعتر از حالت معمول هستند.
- مناسب جهت کارهای data warehouse و ETL Staging Table. در جداول مبتنی بر حافظه امکان عدم ذخیره سازی اطلاعات بر روی دیسک سخت نیز پیش بینی شده‌است. در این حالت فقط اطلاعات ساختار جدول، ذخیره‌ی نهایی می‌گردد و اگر سرور نیز ری استارت گردد، مجددا می‌تواند اطلاعات خود را از منابع اصلی data warehouse تامین کند.


محدودیت‌های جداول بهینه سازی شده‌ی برای حافظه در SQL Server 2014

- تغیر اسکیما و ساختار جداول بهینه سازی شده‌ی برای حافظه مجاز نیست. به بیان دیگر دستور ALTER TABLE برای اینگونه جداول کاربردی ندارد. این مورد جهت ایندکس‌ها نیز صادق است. همان زمانیکه جدول ایجاد می‌شود، باید ایندکس آن نیز تعریف گردد و پس از آن این امکان وجود ندارد.
تنها راه تغییر اسکیمای اینگونه جداول، Drop و سپس ایجاد مجدد آن‌ها است.
البته باید درنظر داشت که SQL Server 2014، اولین نگارش این فناوری را ارائه داده‌است و در نگارش‌های بعدی آن، بسیاری از این محدودیت‌ها قرار است که برطرف شوند.
- جداول بهینه سازی شده‌ی برای حافظه حتما باید دارای یک ایندکس باشند. البته اگر یک primary key را برای آن‌ها تعریف نمائید، کفایت می‌کند.
- از unique index‌ها پشتیبانی نمی‌کند، مگر اینکه از نوع primary key باشد.
- حداکثر 8 ایندکس را می‌توان بر روی اینگونه جداول تعریف کرد.
- امکان تعریف ستون identity در آن وجود ندارد. اما می‌توان از قابلیت sequence برای رسیدن به آن استفاده کرد.
- DML triggers را پشتیبانی نمی‌کند.
- کلیدهای خارجی و قیود را پشتیبانی نمی‌کند.
- حداکثر اندازه‌ی یک ردیف آن 8060 بایت است. بنابراین از نوع‌های داده‌‌ای max دار و XML پشتیبانی نمی‌کند.
این مورد در حین ایجاد جدول بررسی شده و اگر اندازه‌ی ردیف محاسبه‌ی شده‌ی آن توسط SQL Server 2014 بیش از 8060 بایت باشد، جدول را ایجاد نخواهد کرد.


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

حالت DURABILTY انتخاب شده‌ی در حین ایجاد جدول بهینه سازی شده‌ی برای حافظه، تعیین کننده‌ای این مساله است. اگر SCHEMA_ONLY انتخاب شده باشد، کل اطلاعات شما با ری استارت سرور از دست خواهد رفت؛ البته اطلاعات ساختار جدول حفظ خواهد گردید. اگر حالت SCHEMA_AND_DATA انتخاب شود، اطلاعات شما پس از ری‌استارت سرور نیز در دسترس خواهد بود. این اطلاعات به صورت خودکار از لاگ تراکنش‌ها بازیابی شده و مجددا در حافظه قرار می‌گیرند.
حالت SCHEMA_ONLY برای مصارف برنامه‌های data warehouse بیشتر کاربرد دارد. جایی که اطلاعات قرار است از منابع داده‌ی مختلفی تامین شوند.



برای مطالعه بیشتر
SQL Server 2014: NoSQL Speeds with Relational Capabilities  
SQL Server 2014 In-Memory OLTP Architecture and Data Storage
Overview of Applications, Indexes and Limitations for SQL Server 2014 In-Memory OLTP Tables
Microsoft SQL Server 2014: In-Memory OLTP Overview
SQL Server in Memory OLTP for Database Developers
Exploring In-memory OLTP Engine (Hekaton) in SQL Server 2014 CTP1

مطالب
ارث بری prototype ای در جاوا اسکریپت به زبان خیلی ساده

انگیزه اصلی این نوشته شروع کار با AngularJs و استفاده از scope در این کتابخانه است. بیشتر دوستانی که کار با این کتابخانه را شروع می‌کنند و تجربه زیادی با جاوا اسکریپت ندارند، با مفهوم ارث بری scope مشکل پیدا می‌کنند.

ارث بری در scope ‌های AngularJs  موضوع پیچیده و عجیب و غریبی نیست. در واقع همان ارث بری prototype  ای است که جاوا اسکریپت پشتیبانی می‌کند.

این روش توضیح خیلی ساده ای دارد.

در هنگام دسترسی به مقدار یک خصوصیت روی یک شی اگر آن خصوصیت در شی مورد نظر وجود نداشته باشد جاوا اسکریپت یک سطح در زنجیره‌ی prototype ‌ها بالا رفته و به شی پدر دسترسی پیدا کرده و در آن به دنبال مقدار خصوصیت می‌گردد. این کار را آن قدر ادامه می‌دهد تا به بالاترین سطح برسد و دیگر چیزی پیدا نکند.

این بالا رفتن در زنجیره‌ی prototype ‌ها عملا با دسترسی به خصوصیت prototype انجام می‌شود.

فرض کنید دو شی (دقت کنید که می‌گویم شی) به نام‌های employee و person داریم. این دو شی را به صورت زیر تعریف می‌کنیم.

var person = { type: '', name:'No Name' };
var employee = {  };

شی employee الان هیچ خصوصیت ای ندارد. و دسترسی به هر خصوصیت ای از آن هیچ نتیجه‌ای در بر نخواهد داشت.

console.log('Before Inheritance -> employee.name = ' + employee.name);

با مقدار دهی کردن خصوصیت  prototype مربوط به employee به person این شی را از person ارث بری می‌کنیم. 

employee.__proto__ = person;

بعد از اجرا شدن این خط از برنامه هنگام دسترسی پیدا کردن به مقدار name، مقدار اصلی آن که در شی  person وارد شده بود را خواهیم دید. 

ملاحظه کردید که وقتی خصوصیت name در شی مورد نظر وجود نداشت به شی پدر رجوع شد و مقدار خصوصیت مربوطه از آن بدست آمد.

الان فرض کنید که در قسمتی از برنامه خواستیم مقدار name در شی employee را به مقدار مشخصی تغییر دهیم. به طور مثال:

employee.name = 'farid';
console.log('After Assiginig -> employee.name = ' + employee.name);
console.log('After Assiginig -> person.name = ' + person.name);

با چاپ کردن مقادیر person.name و employee.name انتظار دارید چه نتیجه ای ببینید؟ 

اگر از زبان‌های شی گرایی مانند #C آمده باشید احتمالا خواهید گفت مقادیر یکسان خواهند بود. ولی در واقع  این گونه نیست. مقدار person.name همان مقدار اولیه ما خواهد بود و مقدار employee.name نیز ‘farid’.

دلیل این رفتار یک نکته ساده و اساسی است.

جاوا اسکریپت فقط در زمان دسترسی  به یک خصوصیت در صورت پیدا نکردن آن در شی مورد نظر ما به سطوح بالاتر prototype ای رفته و دنبال آن خصوصیت می‌گردد. 

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

در واقع در مثال ما هنگام مقدار دهی به employee.name آن خصوصیت در شی موجود نبود و یک نسخه محلی به نام name در شی ایجاد شد و دفعه بعدی که دسترسی به مقدار این خصوصیت اتفاق افتد این خصوصیت به صورت محلی وجود خواهد داشت و جاوا اسکریپت به سطوح بالاتر نخواهد رفت.

تمام کد‌های بالا در bin زیر موجود هستند.

الان فرض کنید شی‌ءهای ما به این صورت هستند: 
var person = { 
  info : { name: 'No Name', type: '' }
};
var employee = {};
به همان صورت بالا ارث بری می‌کنیم. 
employee.__proto__ = person;
و سپس name را مقداردهی می‌کنیم. 
employee.info.name = 'farid';
و مقادیر را چاپ می‌کنیم. 
console.log('After Assiginig -> employee.name = ' + employee.info.name);
console.log('After Assiginig -> person.name = ' + person.info.name);
ملاحظه خواهید کرد که مقادیر مساوی هستند.
دلیل این امر به زبان ساده این است که وقتی اقدام به مقدار دهی name در شی employee کردیم در واقع قبل از مقدار دهی اصلی name یک دسترسی به شی info نیاز بود و دسترسی به شیء با استفاده از همان قانونی که مطرح کردیم انجام شده و شیء مربوط به person برگردانده شده است. چون name یک خصوصیت از info است نه employee.
سوالی که می‌توان مطرح کرد این است که در صورت نوشتن این خط کد چه اتفاقی خواهد افتاد؟
employee.info = { 
  name: 'farhad'  
};
Prototypal Inheritance with objects 
با توجه به مطالب گفته شده باید قادر به حدس زدن نتیجه خواهید بود.
نکته: روش‌های کار با prototype در این نوشته فقط جنبه آموزشی و توضیحی دارد و روش درست استفاده از prototype این نیست.
 
مطالب
کامپوننت‌ها در AngularJS 1.5
در نسخه‌های  AngularJS 1.x عموماً با کمک کنترلرها و دایرکتیوها، می‌توانیم ویژگی‌های جدیدی را به اپلیکیشن‌هایمان اضافه کنیم؛ از دایرکتیوها برای ایجاد عناصر سفارشی HTML می‌توانستیم (می‌توانیم) استفاده کنیم. مشکل دایرکتیوها این است که برای ایجاد یک عنصر سفارشی ساده باید تنظیمات زیادی را انجام دهیم. در نسخه‌ی AngularJS 1.5 یک API جدید با نام کامپوننت معرفی شده است و این قابلیت، مدل ساده‌ی برنامه‌نویسی در کنترلرها و همچنین قدرت دایرکتیوها را در اختیارمان قرار خواهد داد. سینتکس این API خیلی شبیه به استفاده از کامپوننت‌ها در Angular 2.0 است. این یک مزیت مهم محسوب می‌شود؛ زیرا امکان مهاجرت از نسخه‌ی 1.5 به نسخه‌ی 2 را خیلی ساده خواهد کرد.

نحوه‌ی تعریف یک کامپوننت در AngularJS 1.5
همانند کنترلر و دایرکتیو، برای تعریف یک کامپوننت نیز باید از module API استفاده کنیم:

بنابراین برای ایجاد یک کامپوننت می‌توانیم به اینصورت عمل کنیم:

var app = angular.module("dntModule", []);
app.component("pmApp", {
  template: `Hello this is a simple component`
});

همانطور که مشاهده می‌کنید تابع component دو پارامتر را از ورودی دریافت خواهد کرد؛ نام کامپوننت و یک شیء برای تعیین تنظیمات کامپوننت. نام کامپوننت در اینجا به صورت camel case تعریف شده است؛ که در واقع یک convention برای Angular است. در این‌حالت برای استفاده‌ی از کامپوننت باید به اینصورت عمل کنیم:

<pm-app></pm-app>

در قسمت تنظیمات کامپوننت، در ساده‌ترین حالت یک template تعیین شده‌است که بیانگر نحوه‌ی رندر شدن یک کامپوننت می‌باشد. در اینحالت وقتی انگیولار به تگ فوق برسد، یک کامپوننت با نام pmApp را بارگذاری خواهد کرد.


ایجاد یک کامپوننت ساده

در ادامه می‌خواهیم یک کامپوننت ساده را جهت نمایش یکسری URL درون صفحه طراحی کنیم. ساختار صفحه index.html به صورت زیر خواهد بود:

<html ng-app="DNT">
<head>
    <meta charset="UTF-8">
    <title>Using Angular Component</title>
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
    <link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css">
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-md-3">
                <dnt-widget></dnt-widget>
            </div>
        </div>
    </div>
    <script src="bower_components/angular/angular.js"></script>
    <script src="scripts/app.js"></script>
    <script src="scripts/components/dnt-widget.component.js"></script>
</body>
</html>

در اینجا ابتدا توسط دایرکتیو ng-app، به Angular، ماژول‌مان را معرفی کرده‌ایم. سپس مداخل بوت‌استرپ و کتابخانه‌ی font-awesome را مشاهده می‌کنید. در ادامه، کتابخانه‌ی Angular و همچنین فایل app.js جهت معرفی ماژول برنامه معرفی شده‌است. در نهایت نیز یک فایل در مسیر ذکر شده برای قرار دادن کدهای کامپوننت در مسیر scripts/components اضافه شده‌است.

همانطور که ملاحظه می‌کنید، کامپوننت‌مان به صورت یک تگ سفارشی، درون صفحه قرار گرفته است:

<dnt-archive></dnt-archive>

در ادامه باید به Angular، نحوه‌ی تعریف این کامپوننت را اعلام کنیم. بنابراین یک فایل جاوا اسکریپتی را با نام dnt-widget.component، با محتویات زیر ایجاد کنید:

(function () {    
    "use strict";    
    var app = angular.module("DNT");    
    function DntArchiveController() {
      var model = this;      
      model.panel = {
          title: "Panel Title",
          items: [
              {
                  title: "Dotnettips", url: "https://www.dntips.ir"
              },
              {
                  title: "Google", url: "http://www.google.con"
              },
              {
                  title: "Yahoo", url: "http://www.yahoo.con"
              }
          ]
      };  
    };
    
    app.component("dntWidget", {
        templateUrl: '/scripts/components/dnt-widget.component.html',
        controllerAs: "model",
        controller: DntArchiveController
    });
} ());

توضیح کدهای فوق:

همانطور که مشاهده می‌کنید، برای پارمتر دوم کامپوننت، سه پراپرتی را تعیین کرده‌ایم:

templateUrl: به کمک این پراپرتی به Angular گفته‌ایم که محتوای قالب این کامپوننت، درون یک فایل HTML مجزا قرار دارد و به صورت linked template می‌باشد.

controllerAs: یکی از مزایای استفاده از کامپوننت‌ها، استفاده از controller as syntax می‌باشد. لازم به ذکر است اگر این پراپرتی را مقداردهی نکنیم، به صورت پیش‌فرض مقدار ctrl$ در نظر گرفته خواهد شد.

controller: مزیت دیگر کامپوننت‌ها، استفاده از کنترلرها است. با استفاده از این پراپرتی، یک کنترلر را برای کامپوننت‌مان رجیستر کرده‌ایم. در نتیجه زمانیکه‌ی Angular می‌خواهد کامپوننت‌مان را نمایش دهد، تابع تعریف شده برای این پراپرتی، جهت ایجاد یک controller instance فراخوانی خواهد شد. بنابراین هر پراپرتی یا تابعی که برای این controller instance تعریف کنیم، به راحتی درون ویوی آن جهت اعمال بایندینگ در دسترس خواهد بود (در نتیجه نیازی به scope$ نخواهد بود).

درون کنترلر نیز برای راحتی کار و همچنین به عنوان یک best practice، مقدار this را توسط یک متغیر با نام model، کپچر کرده‌ایم. در اینجا یک شیء را با نام panel نیز به مدل اضافه کرده‌ایم.


محتویات تمپلیت:

<div class="panel panel-default">
    <div class="panel-heading">
        <h3 class="panel-title">
            <span class="fa fa-archive"></span>
            {{ model.panel.title}}
        </h3>
    </div>
    <ul class="list-group">
        <li class="list-group-item" ng-repeat="item in model.panel.items">
            <span class="fa fa-industry"></span>
            <a href="{{ item.url }}">{{ item.title }}</a>
        </li>
    </ul>
</div>

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



همانطور که مشاهده می‌کنید استفاده از کامپوننت‌ها در Angular 1.5 در مقایسه با ایجاد دایرکتیوها و کنترلر‌ها خیلی ساده‌تر است. در واقع امکانات این API جدید تنها به مثال فوق ختم نمی‌شود؛ بلکه این API یک سیستم مسیریابی جدید را نیز معرفی کرده است که در قسمت‌های بعدی به آن نیز خواهیم پرداخت.


جهت تکمیل بحث نیز یک تقویم شمسی ساده را در اینجا قرار داده‌ام. می‌توانید جهت مرور بحث جاری به کدهای آن مراجعه کنید. البته هدف از تعریف این پروژه تنها یک مثال ساده برای معرفی کامپوننت‌ها بود و طبیعتاً باگ‌های زیادی دارد. اگر مایل بودید می‌توانید در توسعه‌ی آن مشارکت نمائید.


کدهای این قسمت را نیز از اینجا می‌توانید دریافت کنید.

مطالب
تبدیل یک View به رشته و بازگشت آن به همراه نتایج JSON حاصل از یک عملیات Ajax ایی در ASP.NET MVC

ممکن است بخواهیم در پاسخ یک تقاضای Ajax ایی، اگر عملیات در سمت سرور با موفقیت انجام شد، خروجی یک Controller action را به کاربر نهایی نشان دهیم. در چنین سناریویی لازم است که بتوانیم خروجی یک action را بصورت رشته برگردانیم. در این مقاله به این مسئله خواهیم پرداخت .
فرض کنید در یک سیستم وبلاگ ساده قصد داریم امکان کامنت گذاشتن بصورت
Ajax را پیاده سازی کنیم. یک ایده عملی و کارآ این است: بعد از اینکه کاربر متن کامنت را وارد کرد و دکمه‌ی ارسال کامنت را زد، تقاضا به سمت سرور ارسال شود و اگر سرور پیغام موفقیت را صادر کرد، متن نوشته شده توسط کاربر را به کمک کدهای JavaScript و در همان سمت کلاینت بصورت یک کادر کامنت جدید به محتوای صفحه اضافه کنیم. بنده در اینجا برای اینکه بتوانم اصل موضوع مورد بحث را توضیح دهم، از یک سناریوی جایگزین استفاده می‌کنم؛ کاربر موقعیکه دکمه ارسال را زد، تقاضا به سرور ارسال میشود. سرور بعد از انجام عملیات، تحت یک شی  JSON هم نتیجه‌ی انجام عملیات و هم محتوای HTML نمایش کامنت جدید در صفحه را به سمت کلاینت ارسال خواهد کرد و کلاینت در صورت موفقیت آمیز بودن عملیات، آن محتوا را به صفحه اضافه می‌کند.

با توجه به توضیحات داده شده، ابتدا یک شیء نیاز داریم تا بتوانیم توسط آن نتیجه‌ی عملیات Ajax ایی را بصورت  JSON به سمت کلاینت ارسال کنیم:

public class MyJsonResult
{
  public bool success { set; get; }
  public bool HasWarning { set; get; }
  public string WarningMessage { set; get; }
  public int errorcode { set; get; }
public string message {set; get; }   public object data { set; get; }  }

سپس به متدی نیاز داریم که کار تبدیل نتیجه‌ی action را به رشته، انجام دهد:

public static string RenderViewToString(ControllerContext context,
    string viewPath,
    object model = null,
    bool partial = false) 
{
    ViewEngineResult viewEngineResult = null;
    if (partial) viewEngineResult = ViewEngines.Engines.FindPartialView(context, viewPath);
    else viewEngineResult = ViewEngines.Engines.FindView(context, viewPath, null);
    if (viewEngineResult == null) throw new FileNotFoundException("View cannot be found.");
    var view = viewEngineResult.View;
    context.Controller.ViewData.Model = model;
    string result = null;
    using(var sw = new StringWriter()) {
        var ctx = new ViewContext(context, view, context.Controller.ViewData, context.Controller.TempData, sw);
        view.Render(ctx, sw);
        result = sw.ToString();
    }
    return result;
}
در اینجا موتور View را بر اساس اطلاعات یک View، مدل و سایر اطلاعات Context جاری کنترلر، وادار به تولید معادل رشته‌ای آن می‌کنیم.

فرض کنیم در سمت Controller هم از کدی شبیه به این استفاده میکنیم:
public JsonResult AddComment(CommentViewModel model) {
    MyJsonResult result = new MyJsonResult() {
        success = false;
    };
    if (!ModelState.IsValid) {
        result.success = false;
        result.message = "لطفاً اطلاعات فرم را کامل وارد کنید";
        return Json(result);
    }
    try {
        Comment theComment = model.toCommentModel();
        //EF service factory
        Factory.CommentService.Create(theComment);
        Factory.SaveChanges();
        result.data = Tools.RenderViewToString(this.ControllerContext, "/views/posts/_AComment", model, true);
        result.success = true;
    } catch (Exception ex) {
        result.success = false;
        result.message = "اشکال زمان اجرا";
    }
    return Json(result);
}

و در سمت کلاینت برای ارسال Form به صورت Ajax ایی خواهیم داشت:

@using (Ajax.BeginForm("AddComment", "posts", 
new AjaxOptions()
{
   HttpMethod = "Post", 
   OnSuccess = "AddCommentSuccess", 
   LoadingElementId = "AddCommentLoading"
}, new { id = "frmAddComment", @class = "form-horizontal" }))
{ 
    @Html.HiddenFor(m => m.PostId)
    <label for="fname">@Texts.ContactName</label> 
    <input type="text" id="fname" name="FullName" class="form-control" placeholder="@Texts.ContactName ">
    <label for="email">@Texts.Email</label> 
    <input type="email" id="InputEmail" name="email" class="form-control" placeholder="@Texts.Email">
    <br><textarea name="C_Content" cols="60" rows="10" class="form-control"></textarea><br>
    <input type="submit" value="@Texts.SubmitComments" name="" class="btn btn-primary">
    <div class="loading-mask" style="display:none">@Texts.LoadingMessage</div>
}
در اینجا در صورت موفقیت آمیز بودن عملیات، متد جاوا اسکریپتی AddCommentSuccess فراخوانی خواهد شد.
باید توجه شود Texts در اینجا یک Resource هست که به منظور نگهداری کلمات استفاده شده در سایت، برای زبانهای مختلف استفاده می‌شود (رجوع شود به مفهوم بومی سازی در Asp.net) .

و در قسمت script ‌ها داریم:

<script type="text/javascript">
  function AddCommentSuccess(jsData) {
   if (jsData && jsData.message)
    alert(jsData.message);
   if (jsData && jsData.success) {
    document.getElementById("frmAddComment").reset();
      //افزودن کامنت جدید ساخته شده توسط کاربر به لیست کامنتهای صفحه
    $("#divAllComments").html(jsData.data + $("#divAllComments").html());    
   }
  }
</script>
متد AddCommentSuccess اطلاعات شیء JSON بازگشتی از کنترلر را دریافت و سپس پیام آن‌را در صورت موفقیت آمیز بودن عملیات، به DIV ایی با id مساوی divAllComments اضافه می‌کند.

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

انشاالله در چندین ماه اینده بعد از کامل شدن سیستم و انتشار اون میتونید بررسیش کنید اگر مایل بودید. کیت‌های توسعه‌ی اون هم به صورت سورس باز منتشر خواهد شد و در همین سایت هم معرفی میشن. به نظر من ارزشش رو داره که روی جزئیات این سیستم کار کنم و امیدوارم بتونه جای تامل داشته باشه . در اخر من رو ببخشید برادر من هیچوقت قصد جسارت نداشتم به شما و دیگر دوستان . این سایت محیط مقدسی هست چون واقعا مطالب خوبی در اون قرار میگیره امیدوارم بتونم از اطلاعات شما و دیگر دوستان استفاده کنم . یا حق
مطالب
سفارشی‌سازی PasswordValidator در ASP.NET Identity
همانطور که می‌دانید Identity، فریمورک نسبتا جدیدی هست که مایکروسافت برای مدیریت کاربران و احراز هویت آن‌ها معرفی کرده و پیشرفت چشمگیری داشته است. در قسمت IdentityConfig (قسمتی که برای کانفیگ‌کردن Identity استفاده می‌شود) بخشی قابل تنظیم برای کانفیگ‌کردن سیاست‌های تعیین پسورد وجود دارد. به‌طور مثال : تعیین حداقل تعداد حروف برای کلمه‌ی عبور، ضرورت کوچک و بزرگ بودن حروف، الزام وجود کاراکتر ویژه. 
این نیاز وجود دارد که PasswordValidator موجود در Identity را برای پروژه‌های مختلف سفارشی‌سازی کرد و این امکان فراهم شود که بتوان سیاست‌های کاری شرکت و پروژه را در قالب PasswordValidator اعمال کرد. به عنوان مثال بررسی کنیم اگر پسورد کاربر عدد 12345 وارد شده است، خطا صادر کنیم و اجازه انتساب آن را برای کاربر ندهیم یا منطق‌های دیگری که نیاز داریم. پس در اینجا به وجود یک CustomPasswordValidator نیاز هست است.
public class CustomPasswordValidator : PasswordValidator
{
    public override async Task<IdentityResult> ValidateAsync(string pass)
    {
            IdentityResult result = await base.ValidateAsync(pass);
            if (pass.Contains("12345"))
           {
            var errors = result.Errors.ToList();
            errors.Add("Passwords cannot contain numeric sequences");
            result = new IdentityResult(errors);
           }
           return result;
    }
}
در قطعه کد بالا یک کلاس ایجاد شد با نام CustomPasswordValidator و از PasswordValidator موجود در فضای نام Microsoft.AspNet.Identity ارث‌بری شد تا ویژگی‌های اصلی این کلاس را به‌صورت ذاتی داشته باشیم و بتوانیم سفارشی‌سازی‌های خودمان را بر روی آن اعمال می‌کنیم. متد ValidateAsync موجود override شده و بر روی ورودی آن شرط‌های پروژه و سیاست‌ها بررسی شده‌اند و درصورت تخلف از قوانین خطا صادر شد.

نقطه‌ی پایان کار اینجاست که در داخل کلاس کانفیگ موجود برای Identity که درون فایل web.config مشخص شده است (در این مثال کلاس IdentityConfig) برای قسمت PasswordValidator ، حالا باید از کلاس CustomPasswordValidator، یک شیء جدید ساخته شود:

// Configure custom validation logic for passwords
manager.PasswordValidator = new CustomPasswordValidator
{
    RequiredLength = 6,
    RequireNonLetterOrDigit = false,
    RequireDigit = false,
    RequireLowercase = false,
    RequireUppercase = false,
};