مطالب
EF Code First #11

استفاده از الگوی Repository اضافی در EF Code first؛‌ آری یا خیر؟!

اگر در ویژوال استودیو، اشاره‌گر ماوس را بر روی تعریف DbContext قرار دهیم، راهنمای زیر ظاهر می‌شود:

A DbContext instance represents a combination of the Unit Of Work and Repository patterns such that 
it can be used to query from a database and group together changes that will then be written back to
the store as a unit. DbContext is conceptually similar to ObjectContext.

در اینجا تیم EF صراحتا عنوان می‌کند که DbContext در EF Code first همان الگوی Unit Of Work را پیاده سازی کرده و در داخل کلاس‌ مشتق شده از آن، DbSet‌ها همان Repositories هستند (فقط نام‌ها تغییر کرده‌اند؛ اصول یکی است).
به عبارت دیگر با نام بردن صریح از این الگوها، مقصود زیر را دنبال می‌کنند:
لطفا بر روی این لایه Abstraction ایی که ما تهیه دیده‌ایم، یک لایه Abstraction دیگر را ایجاد نکنید!
«لایه Abstraction دیگر» یعنی پیاده سازی الگوهای Unit Of Work و Repository جدید، برفراز الگوهای Unit Of Work و Repository توکار موجود!
کار اضافه‌ای که در بسیاری از سایت‌ها مشاهده می‌شود و ... متاسفانه اکثر آن‌ها هم اشتباه هستند! در ذیل روش‌های تشخیص پیاده سازی‌های نادرست الگوی Repository را بر خواهیم شمرد:
1) قرار دادن متد Save تغییرات نهایی انجام شده، در داخل کلاس Repository
متد Save باید داخل کلاس Unit of work تعریف شود نه داخل کلاس Repository. دقیقا همان کاری که در EF Code first به درستی انجام شده. متد SaveChanges توسط DbContext ارائه می‌شود. علت هم این است که در زمان Save ممکن است با چندین Entity و چندین جدول مشغول به کار باشیم. حاصل یک تراکنش، باید نهایتا ذخیره شود نه اینکه هر کدام از این‌ها، تراکنش خاص خودشان را داشته باشند.
2) نداشتن درکی از الگوی Unit of work
به Unit of work به شکل یک تراکنش نگاه کنید. در داخل آن با انواع و اقسام موجودیت‌ها از کلاس‌ها و جداول مختلف کار شده و حاصل عملیات، به بانک اطلاعاتی اعمال می‌گردد. پیاده سازی‌های اشتباه الگوی Repository، تمام امکانات را در داخل همان کلاس Repository قرار می‌دهند؛ که اشتباه است. این نوع کلاس‌ها فقط برای کار با یک Entity بهینه شده‌اند؛ در حالیکه در دنیای واقعی، اطلاعات ممکن است از دو Entity مختلف دریافت و نتیجه محاسبات مفروضی به Entity سوم اعمال شود. تمام این عملیات یک تراکنش را تشکیل می‌دهد، نه اینکه هر کدام، تراکنش مجزای خود را داشته باشند.
3) وهله سازی از DbContext به صورت مستقیم داخل کلاس Repository
4) Dispose اشیاء DbContext داخل کلاس Repository
هر بار وهله سازی DbContext مساوی است با باز شدن یک اتصال به بانک اطلاعاتی و همچنین از آنجائیکه راهنمای ذکر شده فوق را در مورد DbContext مطالعه نکرده‌اند، زمانیکه در یک متد با سه وهله از سه Repository موجودیت‌های مختلف کار می‌کنید، سه تراکنش و سه اتصال مختلف به بانک اطلاعاتی گشوده شده است. این مورد ذاتا اشتباه است و سربار بالایی را نیز به همراه دارد.
ضمن اینکه بستن DbContext در یک Repository، امکان اعمال کوئری‌های بعدی LINQ را غیرممکن می‌کند. به ظاهر یک شیء IQueryable در اختیار داریم که می‌توان بر روی آن انواع و اقسام کوئری‌های LINQ را تعریف کرد اما ... در اینجا با LINQ to Objects که بر روی اطلاعات موجود در حافظه کار می‌کند سر و کار نداریم. اتصال به بانک اطلاعاتی با بستن DbContext قطع شده، بنابراین کوئری LINQ بعدی شما کار نخواهد کرد.
همچنین در EF نمی‌توان یک Entity را از یک Context به Context‌ دیگری ارسال کرد. در پیاده سازی صحیح الگوی Repository (دقیقا همان چیزی که در EF Code first به صورت توکار وجود دارد)، Context باید بین Repositories که در اینجا فقط نامش DbSet تعریف شده، به اشتراک گذاشته شود. علت هم این است که EF از Context برای ردیابی تغییرات انجام شده بر روی موجودیت‌ها استفاده می‌کند (همان سطح اول کش که در قسمت‌های قبل به آن اشاره شد). اگر به ازای هر Repository یکبار وهله سازی DbContext انجام شود، هر کدام کش جداگانه خاص خود را خواهند داشت.
5) عدم امکان استفاده از تنها یک DbConetext به ازای یک Http Request
هنگامیکه وهله سازی DbContext به داخل یک Repository منتقل می‌شود و الگوی واحد کار رعایت نمی‌گردد، امکان به اشتراک گذاری آن بین Repositoryهای تعریف شده وجود نخواهد داشت. این مساله در برنامه‌های وب سبب کاهش کارآیی می‌گردد (باز و بسته شدن بیش از حد اتصال به بانک اطلاعاتی در حالیکه می‌شد تمام این عملیات را با یک DbContext انجام داد).

نمونه‌ای از این پیاده سازی اشتباه را در اینجا می‌توانید پیدا کنید. متاسفانه شبیه به همین پیاده سازی، در پروژه MVC Scaffolding نیز بکارگرفته شده است.


چرا تعریف لایه دیگری بر روی لایه Abstraction موجود در EF Code first اشتباه است؟

یکی از دلایلی که حین تعریف الگوی Repository دوم بر روی لایه موجود عنوان می‌شود، این است:
«به این ترتیب به سادگی می‌توان ORM مورد استفاده را تغییر داد» چون پیاده سازی استفاده از ORM، در پشت این لایه مخفی شده و ما هر زمان که بخواهیم به ORM دیگری کوچ کنیم، فقط کافی است این لایه را تغییر دهیم و نه کل برنامه‌ را.
ولی سؤال این است که هرچند این مساله از هزار فرسنگ بالاتر درست است، اما واقعا تابحال دیده‌اید که پروژه‌ای را با یک ORM شروع کنند و بعد سوئیچ کنند به ORM دیگری؟!
ضمنا برای اینکه واقعا لایه اضافی پیاده سازی شده انتقال پذیر باشد، شما باید کاملا دست و پای ORM موجود را بریده و توانایی‌های در دسترس آن را به سطح نازلی کاهش دهید تا پیاده سازی شما قابل انتقال باشد. برای مثال یک سری از قابلیت‌های پیشرفته و بسیار جالب در NH هست که در EF نیست و برعکس. آیا واقعا می‌توان به همین سادگی ORM مورد استفاده را تغییر داد؟ فقط در یک حالت این امر میسر است: از قابلیت‌های پیشرفته ابزار موجود استفاده نکنیم و از آن در سطحی بسیار ساده و ابتدایی کمک بگیریم تا از قابلیت‌های مشترک بین ORMهای موجود استفاده شود.
ضمن اینکه مباحث نگاشت کلاس‌ها به جداول را چکار خواهید کرد؟ EF راه و روش خاص خودش را دارد، NH چندین و چند روش خاص خودش را دارد! این‌ها به این سادگی قابل انتقال نیستند که شخصی عنوان کند: «هر زمان که علاقمند بودیم، ORM مورد استفاده را می‌شود عوض کرد!»

دلیل دومی که برای تهیه لایه اضافه‌تری بر روی DbContext عنوان می‌کنند این است:
«با استفاده از الگوی Repository نوشتن آزمون‌های واحد ساده‌تر می‌شود». زمانیکه برنامه بر اساس Interfaceها کار می‌کند می‌توان آن‌ها را بجای اشاره به بانک اطلاعاتی، به نمونه‌ای موجود در حافظه، در زمان آزمون تغییر داد.
این مورد در حالت کلی درست است اما .... نه در مورد بانک‌های اطلاعاتی!
زمانیکه در یک آزمون واحد، پیاده سازی جدیدی از الگوی Interface مخزن ما تهیه می‌شود و اینبار بجای بانک اطلاعاتی با یک سری شیء قرارگرفته در حافظه سروکار داریم، آیا موارد زیر را هم می‌توان به سادگی آزمایش کرد؟
ارتباطات بین جداول‌را، cascade delete، فیلدهای identity، فیلدهای unique، کلیدهای ترکیبی، نوع‌های خاص تعریف شده در بانک اطلاعاتی و مسایلی از این دست.
پاسخ: خیر! تغییر انجام شده، سبب کار برنامه با اطلاعات موجود در حافظه خواهد شد، یعنی LINQ to Objects.
شما در حالت استفاده از LINQ to Objects آزادی عمل فوق العاده‌ای دارید. می‌توانید از انواع و اقسام متدها حین تهیه کوئری‌های LINQ استفاده کنید که هیچکدام معادلی در بانک اطلاعاتی نداشته و ... به ظاهر آزمون واحد شما پاس می‌شود؛ اما در عمل بر روی یک بانک اطلاعاتی واقعی کار نخواهد کرد.
البته شاید شخصی عنوان که بله می‌شود تمام این‌ها نیازمندی‌ها را در حالت کار با اشیاء درون حافظه هم پیاده سازی کرد ولی ... در نهایت پیاده سازی آن بسیار پیچیده و در حد پیاده سازی یک بانک اطلاعاتی واقعی خواهد شد که واقعا ضرورتی ندارد.

و پاسخ صحیح در اینجا و این مساله خاص این است:
لطفا در حین کار با بانک‌های اطلاعاتی مباحث mocking را فراموش کنید. بجای SQL Server، رشته اتصالی و تنظیمات برنامه را به SQL Server CE تغییر داده و آزمایشات خود را انجام دهید. پس از پایان کار هم بانک اطلاعاتی را delete کنید. به این نوع آزمون‌ها اصطلاحا integration tests گفته می‌شود. لازم است برنامه با یک بانک اطلاعاتی واقعی تست شود و نه یک سری شیء ساده قرار گرفته در حافظه که هیچ قیدی همانند شرایط کار با یک بانک اطلاعاتی واقعی، بر روی آ‌ن‌ها اعمال نمی‌شود.
ضمنا باید درنظر داشت بانک‌های اطلاعاتی که تنها در حافظه کار کنند نیز وجود دارند. برای مثال SQLite حالت کار کردن صرفا در حافظه را پشتیبانی می‌کند. زمانیکه آزمون واحد شروع می‌شود، یک بانک اطلاعاتی واقعی را در حافظه تشکیل داده و پس از پایان کار هم ... اثری از این بانک اطلاعاتی باقی نخواهد ماند و برای این نوع کارها بسیار سریع است.


نتیجه گیری:
حین استفاده از EF code first، الگوی واحد کار، همان DbContext است و الگوی مخزن، همان DbSetها. ضرورتی به ایجاد یک لایه محافظ اضافی بر روی این‌ها وجود ندارد.
در اینجا بهتر است یک لایه اضافی را به نام مثلا Service ایجاد کرد و تمام اعمال کار با EF را به آن منتقل نمود. سپس در قسمت‌های مختلف برنامه می‌توان از متدهای این لایه استفاده کرد. به عبارتی در فایل‌های Code behind برنامه شما نباید کدهای EF مشاهده شوند. یا در کنترلرهای MVC نیز به همین ترتیب. این‌ها مصرف کننده نهایی لایه سرویس ایجاد شده خواهند بود.
همچنین بجای نوشتن آزمون‌های واحد، به Integration tests سوئیچ کنید تا بتوان برنامه را در شرایط کار با یک بانک اطلاعاتی واقعی تست کرد.


برای مطالعه بیشتر:
مطالب دوره‌ها
تغییر ترتیب آیتم‌های یک لیست به کمک افزونه jquery.sortable در ASP.NET MVC
در این مطلب قصد داریم ترتیب عناصر نمایش داده شده توسط یک لیست را به کمک افزونه بسیار سبک وزن jquery.sortable تغییر داده و نتایج را در سمت سرور مدیریت کنیم. این افزونه بر اساس امکانات کشیدن و رها ساختن HTML5 کار می‌کند و با مرورگرهای IE8 به بعد سازگار است.

مدل‌های برنامه

using System.Collections.Generic;

namespace jQueryMvcSample05.Models
{
    public class Survey
    {
        public int Id { set; get; }
        public string Title { set; get; }

        public virtual ICollection<SurveyItem> SurveyItems { set; get; }
    }
}

namespace jQueryMvcSample05.Models
{
    public class SurveyItem
    {
        public int Id { set; get; }
        public string Title { set; get; }
        public int Order { set; get; }

        //[ForeignKey("SurveyId")]
        public virtual Survey Survey { set; get; }
        public int SurveyId { set; get; }
    }
}
به کمک این ساختار قصد داریم اطلاعات یک سیستم نظر سنجی را نمایش دهیم.
تعدادی نظر سنجی به همراه گزینه‌های آن‌ها تعریف خواهند شد (یک رابطه one-to-many است). سپس توسط افزونه sortable می‌خواهیم ترتیب قرارگیری گزینه‌های آن‌را مشخص کنیم یا تغییر دهیم.


منبع داده فرضی برنامه


using System.Collections.Generic;
using jQueryMvcSample05.Models;

namespace jQueryMvcSample05.DataSource
{
    /// <summary>
    /// یک منبع داده فرضی جهت دموی ساده‌تر برنامه
    /// </summary>
    public static class SurveysDataSource
    {
        private static IList<Survey> _surveysCache;
        static SurveysDataSource()
        {
            _surveysCache = createSurveys();
        }

        public static IList<Survey> SystemSurveys
        {
            get { return _surveysCache; }
        }

        private static IList<Survey> createSurveys()
        {
            var results = new List<Survey>();
            for (int i = 1; i < 6; i++)
            {
                results.Add(new Survey
                {
                    Id = i,
                    Title = "نظر سنجی " + i,
                    SurveyItems = new List<SurveyItem>
                    {
                       new SurveyItem{ Id = 1, SurveyId = i, Title = "گزینه 1", Order = 1 },
                       new SurveyItem{ Id = 2, SurveyId = i, Title = "گزینه 2", Order = 2 },
                       new SurveyItem{ Id = 3, SurveyId = i, Title = "گزینه 3", Order = 3 },
                       new SurveyItem{ Id = 4, SurveyId = i, Title = "گزینه 4", Order = 4 }
                    }
                });
            }
            return results;
        }
    }
}
در اینجا نیز از یک منبع داده فرضی تشکیل شده در حافظه جهت سهولت دموی برنامه استفاده خواهد شد.


کدهای کنترلر برنامه

using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.UI;
using jQueryMvcSample05.DataSource;
using jQueryMvcSample05.Security;

namespace jQueryMvcSample05.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            var surveysList = SurveysDataSource.SystemSurveys;
            return View(surveysList);
        }

        [HttpPost]
        [AjaxOnly]
        [OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
        public ActionResult SortItems(int? surveyId, string[] items)
        {
            if (items == null || items.Length == 0 || surveyId == null)
                return Content("nok");

            updateSurvey(surveyId, items);

            return Content("ok");
        }

        /// <summary>
        /// این متد جهت آشنایی با پروسه به روز رسانی ترتیب گزینه‌ها در اینجا قرار گرفته است
        /// بدیهی است محل قرارگیری آن باید در لایه سرویس برنامه اصلی باشد
        /// </summary>
        private static void updateSurvey(int? surveyId, string[] items)
        {
            var itemIds = new List<int>();
            foreach (var item in items)
            {
                itemIds.Add(int.Parse(item.Replace("item-row-", string.Empty)));
            }

            var survey = SurveysDataSource.SystemSurveys.FirstOrDefault(x => x.Id == surveyId.Value);
            if (survey == null)
                return;

            int order = 0;
            foreach (var itemId in itemIds)
            {
                order++;
                var surveyItem = survey.SurveyItems.FirstOrDefault(x => x.Id == itemId);
                if (surveyItem == null) continue;
                surveyItem.Order = order;
            }

            //todo: call save changes ....
        }
    }
}

و کدهای View متناظر

@model IList<jQueryMvcSample05.Models.Survey>
@{
    ViewBag.Title = "Index";
    var sortUrl = Url.Action(actionName: "SortItems", controllerName: "Home");
}
<h2>
    نظر سنجی‌ها</h2>
@foreach (var survey in Model)
{
    <fieldset>
        <legend>@survey.Title</legend>
        <div id="sortable-@survey.Id">
            @foreach (var surveyItem in survey.SurveyItems.OrderBy(x => x.Order))
            {            
                <div id="item-row-@surveyItem.Id">
                    <span class="handles">::</span>
                    @surveyItem.Title
                </div>
            }
        </div>
    </fieldset>
}
<div>
    لطفا برای تغییر ترتیب آیتم‌های تعریف شده، از امکان کشیدن و رها کردن تعریف شده بر
    روی آیکون‌های :: در کنار هر آیتم استفاده نمائید.
</div>
@section JavaScript
{
    <script type="text/javascript">
        $(document).ready(function () {
            $('div[id^="sortable"]').sortable({ handle: 'span' }).bind('sortupdate', function (e, ui) {
                var sortableItemId = $(ui.item).parent().attr('id');
                var surveyId = sortableItemId.replace('sortable-', '');
                var items = [];
                $('#' + sortableItemId + ' div').each(function () {
                    items.push($(this).attr('id'));
                });
                //alert(items.join('&'));                
                $.ajax({
                    type: "POST",
                    url: "@sortUrl",
                    data: JSON.stringify({ items: items, surveyId: surveyId }),
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    complete: function (xhr, status) {
                        var data = xhr.responseText;
                        if (xhr.status == 403) {
                            window.location = "/login";
                        } else if (status === 'error' || !data || data == "nok") {
                            alert('خطایی رخ داده است');
                        }
                        else {
                            alert('انجام شد');
                        }
                    }
                });
            });
        });
    </script>
}
توضیحات

در اینجا نیاز بود تا ابتدا کدهای کنترلر و View ارائه شوند، تا بتوان در مورد ارتباطات بین آن‌ها بهتر بحث کرد.
در ابتدای نمایش صفحه Home، رکوردهای نظرسنجی‌ها از منبع داده دریافت شده و به View ارسال می‌شوند. در View برنامه یک حلقه تشکیل گردیده و این موارد رندر خواهند شد.
هر نظر سنجی با یک div بیرونی که با id مساوی sortable شروع می‌شود، آغاز گردیده و گزینه‌های آن نظر سنجی نیز توسط divهایی با id مساوی item-row شروع خواهند گردید. هر کدام از این idها حاوی id رکوردهای متناظر هستند. از این id‌ها در کدهای برنامه جهت یافتن یک نظر سنجی یا یک ردیف مشخص برای به روز رسانی ترتیب آن‌ها استفاده خواهیم کرد.
ادامه کار، به تنظیمات و اعمال افزونه sortable مرتبط می‌شود. توسط تنظیم ذیل به jQuery اعلام خواهیم کرد، هرجایی یک div با id شروع شده با sortable یافتی، افزونه sortable را به آن متصل کن:
 $('div[id^="sortable"]').sortable
در ادامه در ناحیه و  div ایی که عمل کشیدن و رها شدن رخ داده، id این div را بدست آورده و سپس کلیه row-itemهای آن را در آرایه‌ای به نام items قرار می‌دهیم:
 var sortableItemId = $(ui.item).parent().attr('id');
var surveyId = sortableItemId.replace('sortable-', '');
var items = [];
$('#' + sortableItemId + ' div').each(function () {
  items.push($(this).attr('id'));
});
اکنون که به id یک نظر سنجی و همچنین idهای ردیف‌های مرتب شده حاصل دسترسی داریم، آن‌ها را توسط jQuery Ajax به کنترلر برنامه ارسال می‌کنیم:
 data: JSON.stringify({ items: items, surveyId: surveyId })
امضای اکشن متد SortItems نیز دقیقا بر همین مبنا تنظیم شده است:
 public ActionResult SortItems(int? surveyId, string[] items)

اطلاعاتی که در اینجا دریافت می‌شوند در متد updateSurvey مورد استفاده قرار خواهند گرفت. بر اساس surveyId دریافتی، نظرسنجی مرتبط را یافته و سپس به گزینه‌های آن دست خواهیم یافت. اکنون نوبت به پردازش آرایه items دریافت شده است. این آرایه بر اساس انتخاب کاربر مرتب شده است.


دریافت کدها و پروژه کامل این قسمت
jQueryMvcSample05.zip
 
مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت سوم - غنی سازی کامپوننت‌ها
در قسمت قبل، مقدمه‌ای بر نحوه‌ی تعریف یک کامپوننت در AngularJS 2.0 عنوان شد و همچنین نحوه‌ی بوت استرپ و آغاز اینگونه برنامه‌ها بررسی گردید. در این قسمت می‌خواهیم امکانات پیشرفته‌تری از کامپوننت‌ها را بررسی کنیم.


روش‌های مختلف تعریف خاصیت template در یک کامپوننت

در قسمت قبل، روش تعریف inline یک template را مشاهده کردید:
template:`
          <div><h1>{{pageTitle}}</h1>
               <div>My First Component</div>
          </div>
 `
در اینجا رشته‌ی قالب نهایی این View، در همان تعاریف متادیتای Component قرار گرفته‌است (روش inline). اگر این رشته تک سطری باشد، از روش متداول ذکر "" برای تعریف رشته‌ها در جاوا اسکریپت استفاده می‌شود و اگر این رشته چند سطری باشد، از back tick مربوط به ES 6 مانند مثال فوق کمک گرفته خواهد شد. استفاده از back ticks و رشته‌های چند سطری، نحوه‌ی تعریف قالب‌های inline را خواناتر می‌کند.
هر چند این روش تعریف قالب‌ها، مزیت سادگی و امکان مشاهده‌ی View را به همراه کدهای مرتبط با آن، در یک فایل میسر می‌کند، اما به دلیل رشته‌ای بودن، مزیت کار کردن با ادیتورهای وب، مانند داشتن intellisense، فرمت خودکار کدها و بررسی syntax را از دست خواهیم داد و با بیشتر شدن حجم این رشته، این مشکلات بیشتر نمایان خواهند شد.
به همین جهت قابلیت دیگری به نام linked template نیز در اینجا درنظر گرفته شده‌است:
 templateUrl: 'product-list.component.html'
در این حالت، محتوای قالب، به یک فایل html مجزا منتقل شده و سپس لینک آن در خاصیت دیگری از متادیتای Component به نام templateUrl ذکر می‌شود.


ساخت کامپوننت نمایش لیست محصولات

در ادامه می‌خواهیم کامپوننتی را طراحی کنیم که آرایه‌ای از محصولات را نمایش می‌دهد. در اینجا مرسوم است هر ویژگی برنامه، در یک پوشه‌ی مجزا قرار گیرد. به همین جهت در ادامه‌ی مثال قسمت قبل که پوشه‌ی app را به ریشه‌ی پروژه اضافه کردیم و سپس main.ts راه انداز و کامپوننت ریشه‌ی سایت یا app.component.ts را در آن تعریف کردیم، در داخل همین پوشه‌ی app، پوشه‌ی جدیدی را به نام products اضافه می‌کنیم. سپس به این پوشه‌ی جدید محصولات، فایل جدیدی را به نام product-list.component.html اضافه کنید. از این فایل جهت تعریف قالب کامپوننت لیست محصولات استفاده خواهیم کرد. در اینجا نیز مرسوم است نام قالب یک Component را به صورت نام ویژگی ختم شده‌ی به کلمه‌ی Component، با پسوند html تعریف کنیم.


پس از اضافه شدن فایل product-list.component.html، محتوای آن‌را به نحو ذیل تغییر دهید:
<div class='panel panel-default'>
    <div class='panel-heading'>
        {{pageTitle}}
    </div>
    <div class='panel-body'>
        <div class='row'>
            <div class='col-md-2'>Filter by:</div>
            <div class='col-md-4'>
                <input type='text' />
            </div>
        </div>
        <div class='row'>
            <div class='col-md-6'>
                <h3>Filtered by: </h3>
            </div>
        </div>
        <div class='table-responsive'>
            <table class='table'>
                <thead>
                    <tr>
                        <th>
                            <button class='btn btn-primary'>
                                Show Image
                            </button>
                        </th>
                        <th>Product</th>
                        <th>Code</th>
                        <th>Available</th>
                        <th>Price</th>
                        <th>5 Star Rating</th>
                    </tr>
                </thead>
                <tbody>
 
                </tbody>
            </table>
        </div>
    </div>
</div>
در اینجا قصد داریم داخل پنل بوت استرپ 3، لیستی از محصولات را به صورت یک جدول نمایش دهیم. همچنین می‌خواهیم قابلیت جستجوی داخل این لیست را نیز فراهم کنیم. فعلا شکل کلی این قالب را به نحو فوق تهیه می‌کنیم. قسمت tbody جدول آن را که قرار است لیست محصولات را رندر کند، در ادامه‌ی بحث تکمیل خواهیم کرد.
تنها نکته‌ی AngularJS 2.0 قالب فوق، اتصال به pageTitle است که نمونه‌ای از آن‌را در قسمت قبل با معرفی اولین کامپوننت مشاهده کرده‌اید.

در ادامه نیاز است برای این قالب و view، یک کامپوننت را طراحی کنیم که متشکل است از یک کلاس TypeScript ایی مزین شده به Component. بنابراین فایل ts جدیدی را به نام product-list.component.ts به پوشه‌ی App\products اضافه کنید؛ با این محتوا:
import { Component } from 'angular2/core';
 
@Component({
    selector: 'pm-products',
    templateUrl: 'app/products/product-list.component.html'
})
export class ProductListComponent {
    pageTitle: string = 'Product List';
}


با جزئیات نحوه‌ی تعریف یک کامپوننت در قسمت قبل در حین معرفی کامپوننت‌ها آشنا شدیم. در اینجا کلاس ProductListComponent با واژه‌ی کلیدی export همراه است تا توسط module loader برنامه قابلیت بارگذاری را پیدا کند. همچنین خاصیت عمومی pageTitle نیز در آن تعریف شده‌است تا در قالب مرتبط مورد استفاده قرار گیرد.
سپس این کلاس، با decorator ویژه‌ای به نام Component مزین شده‌است تا AngularJS 2.0 بداند که هدف از تعریف آن، ایجاد یک کامپوننت جدید است. مقدار selector آن که تشکیل دهنده‌ی یک تگ HTML سفارشی متناظر با آن خواهد شد، به pm-products تنظیم شده‌است و اینبار بجای تعریف inline قالب آن به صورت یک رشته، از خاصیت templateUrl جهت معرفی مسیر فایل html قالبی که پیشتر آماده کردیم، کمک گرفته شده‌است.


نمایش کامپوننت لیست محصولات در صفحه‌ی اصلی سایت

خوب، تا اینجا یک کامپوننت جدید را به نام لیست محصولات، ایجاد کردیم؛ اما چگونه باید آن‌را نمایش دهیم؟
در قسمت قبل که کامپوننت ریشه‌ی برنامه یا AppComponent را تعریف کردیم، نام selector آن را pm-app درنظر گرفتیم و در نهایت این directive سفارشی را به نحو ذیل در body صفحه‌ی اصلی سایت نمایش دادیم:
    <div>
        @RenderBody()
        <pm-app>Loading App...</pm-app>
    </div>
اما این روش، تنها برای root component سایت مناسب است. برای سایر کامپوننت‌های غیر ریشه‌ای (یعنی تمام کامپوننت‌ها)، سه مرحله‌ی زیر باید طی شوند:
الف) تگ سفارشی این دایرکتیو جدید را به کامپوننت ریشه‌ی سایت یا همان AppComponent اضافه می‌کنیم. بنابراین فایل app.component.ts را گشوده و سپس selector کامپوننت لیست محصولات را به قالب آن اضافه کنید:
import { Component } from 'angular2/core';
 
@Component({
    selector: 'pm-app',
    template:`
    <div><h1>{{pageTitle}}</h1>
        <pm-products></pm-products>
    </div>
    `
})
export class AppComponent {
    pageTitle: string = "DNT AngularJS 2.0 APP";
}
همانطور که مشاهده می‌کنید، تگ جدید pm-products بر اساس نام selector کامپوننت لیست محصولات، به قالب کامپوننت ریشه‌ی سایت اضافه شده‌است.
ب) تا اینجا یک دایرکتیو جدید را به نام pm-products به یک کامپوننت دیگر اضافه کرده‌ایم. اما این کامپوننت نمی‌داند که اطلاعات آن‌را باید از کجا تامین کند. برای این منظور خاصیت جدیدی را به نام directives به لیست خاصیت‌های Component ریشه‌ی سایت اضافه می‌کنیم. این خاصیت، آرایه‌ای از دایرکتیوهای سفارشی را قبول می‌کند:
 directives: [ProductListComponent]
ج) بلافاصله که این تغییر را اعمال کنید، در ادیتور TypeScript ایی موجود، ذیل کلمه‌ی ProductListComponent خط قرمز کشیده خواهد شد. چون هنوز مشخص نکرده‌ایم که این شیء جدید باید از کدام ماژول تامین شود و ناشناخته‌است. بنابراین import مربوطه را به ابتدای فایل اضافه می‌کنیم:
import { Component } from 'angular2/core';
import { ProductListComponent } from './products/product-list.component';
 
@Component({
    selector: 'pm-app',
    template:`
    <div><h1>{{pageTitle}}</h1>
        <pm-products></pm-products>
    </div>
    `,
    directives: [ProductListComponent]
})
export class AppComponent {
    pageTitle: string = "DNT AngularJS 2.0 APP";
}
کدهای فوق، کد نهایی کامپوننت ریشه‌ی سایت هستند که به آن selector جدیدی به نام pm-products اضافه شده‌است. سپس directive متناظر آن به لیست دایرکتیوهای کامپوننت جاری اضافه شده و در نهایت این دایرکتیو، از ماژول مرتبط با آن import شده‌است.

این سه مرحله، مراحلی هستند که جهت افزودن هر دایرکتیو جدید به کامپوننتی مشخص، باید طی شوند.

خوب، اکنون اگر برنامه را اجرا کنیم، چنین خروجی را می‌توان مشاهده کرد:


یک نکته
اگر برنامه را اجرا کردید و خروجی را مشاهده نکردید، مطمئن شوید که فایل‌های ts شما کامپایل شده‌اند. فشردن دکمه‌ی ctrl+s مجدد در این فایل‌ها، سبب کامپایل مجدد آن‌ها می‌شوند و یا انتخاب گزینه‌ی Build و سپس ReBuild solution نیز همینکار را انجام می‌دهد.


غنی سازی کامپوننت‌های AngularJS 2.0 با data-binding

در AngularJS 2.0 عملیات binding، کار مدیریت ارتباطات بین یک کلاس کامپوننت و قالب آن‌را انجام می‌دهد. نمونه‌ای از آن‌را پیشتر با خاصیت pageTitle و سپس نمایش آن در قالب کامپوننت متناظر با آن کلاس، مشاهده کرده‌اید. همچنین در اینجا یک قالب می‌تواند متدهای داخل کلاس کامپوننت خود را توسط رخدادها نیز فراخوانی کند.
به نحوه‌ی نمایش {{pageTitle}} اصطلاحا interpolation می‌گویند. در اینجا خاصیت pageTitle اطلاعات خود را از کلاس کامپوننت دریافت می‌کند. به این نوع binding، انقیاد یک طرفه یا one-way binding نیز گفته می‌شوند؛ از خاصیت کلاس شروع شده و به قالب خاتمه می‌یابد.
ویژگی interpolation فراتر است از صرفا نمایش یک خاصیت و می‌تواند حاوی محاسبات نیز باشد:
{{'Title: ' + pageTitle}}
{{2*20+1}}
و یا حتی در آن می‌توان متدی از کلاس کامپوننت را نیز فراخوانی کرد. در مثال زیر فرض شده‌است که متد getTitle، در کلاس متناظر با کامپوننت این قالب، تعریف شده‌است:
{{'Title: ' + getTitle()}}
کار interpolation درج عبارت محاسبه شده‌ی نهایی بین المان‌های html است؛ مانند:
 <h1>{{pageTitle}}</h1>
و یا حتی می‌توان این مقدار نهایی را به خواص المان‌های html نیز نسبت داد:
 <h1 innerText={{pageTitle}}></h1>
در این مثال خاصیت innerText المان h1 توسط interpolation مقدار دهی شده‌است.

بنابراین به صورت خلاصه هر زمانیکه نیاز به نمایش اطلاعات فقط خواندنی (one-way binding) داریم، ابتدا خاصیتی را در کلاس کامپوننت تعریف کرده و سپس مقدار این خاصیت را توسط interpolation، در قالب کامپوننت درج می‌کنیم. حین استفاده از interpolation نیازی به ذکر "" نیست.
در مورد مباحث تکمیلی binding در قسمت‌های بعدی بیشتر بحث خواهیم کرد.


افزودن منطقی سفارشی به قالب یک کامپوننت

دایرکتیوها به صورت المان‌ها و یا ویژگی‌های سفارشی HTML، قابلیت توسعه‌ی امکانات پیش فرض آن‌را دارند. در اینجا می‌توان دایرکتیوهای سفارشی خود را تولید کرد (مانند pm-products فوق) و یا از دایرکتیوهای توکار AngularJS 2.0 استفاده کرد. برای مثال ngIf* و ngFor* جزو structural directives توکار AngularJS 2.0 هستند. ستاره‌ای که پیش از نام این دایرکتیوها قرار گرفته‌است، آن‌‌ها را در گروه structural directives قرار می‌دهد.
کار دایرکتیوهای ساختاری، تغییر ساختار یا همان view کامپوننت‌ها است؛ با افزودن، حذف و یا تغییر المان‌های HTML تعریف شده‌ی در صفحه.

بررسی ngIf*

فایل قالب product-list.component.html را گشوده و تعریف جدول آن‌را به نحو ذیل تغییر دهید:
 <table class='table' *ngIf='products && products.length'>
کار ngIf* نمایش یا عدم نمایش قسمتی از DOM یا document object model بر اساس برآورده شدن منطقی است که توسط آن بررسی می‌شود. اگر حاصل عبارتی که به ngIf* انتساب داده می‌شود به false تعبیر شود، آن المان و فرزندان آن از DOM حذف می‌شوند و اگر این عبارت به true تعبیر شود، آن المان و فرزندانش مجددا به DOM اضافه خواهند شد.
برای نمونه عبارت انتساب داده شده‌ی به ngIf* در مثال فوق به این معنا است که اگر خاصیت و آرایه‌ی products در کلاس کامپوننت این قالب تعریف شده بود و همچنین دارای اعضایی نیز بود، آنگاه این جدول را نمایش بده.
برای آزمایش آن، فایل product-list.component.ts را گشوده و خاصیت عمومی آرایه‌ی products را به نحو ذیل به آن اضافه کنید:
import { Component } from 'angular2/core';
 
@Component({
    selector: 'pm-products',
    templateUrl: 'app/products/product-list.component.html'
})
export class ProductListComponent {
    pageTitle: string = 'Product List';
    products: any[] = [
        {
            "productId": 2,
            "productName": "Garden Cart",
            "productCode": "GDN-0023",
            "releaseDate": "March 18, 2016",
            "description": "15 gallon capacity rolling garden cart",
            "price": 32.99,
            "starRating": 4.2,
            "imageUrl": "app/assets/images/garden_cart.png"
        },
        {
            "productId": 5,
            "productName": "Hammer",
            "productCode": "TBX-0048",
            "releaseDate": "May 21, 2016",
            "description": "Curved claw steel hammer",
            "price": 8.9,
            "starRating": 4.8,
            "imageUrl": "app/assets/images/rejon_Hammer.png"
        }
    ];
}
فعلا چون اینترفیسی را برای شیء محصول تعریف نکرده‌ایم، نوع این آرایه را any یا همان حالت پیش فرض جاوا اسکریپت تعریف می‌کنیم.
همچنین فعلا در اینجا اطلاعات را بجای دریافت از سرور، توسط آرایه‌ی مشخصی از اشیاء تعریف کرده‌ایم. این موارد را در قسمت‌های بعدی بهبود خواهیم بخشید.

اکنون که خاصیت عمومی products تعریف شده‌است، امکان استفاده‌ی از ngIf* ایی که پیشتر تعریف کردیم، میسر شده‌است. در این حالت اگر برنامه را اجرا کنید، قسمت table header تصویر قبلی نمایش سایت، هنوز نمایان است. یعنی ngIf* تعریف شده کار می‌کند؛ چون خاصیت products تعریف شده‌است و همچنین دارای اعضایی است.
برای آزمایش بیشتر، خاصیت products را کامنت کنید و یکبار نیز فایل ts آن‌را ذخیره کنید تا فایل js متناظر با آن کامپایل شود. سپس مجددا برنامه را اجرا کنید. در این حالت دیگر نباید هدر جدول نمایان باشد؛ چون products تعریف نشده‌است.


بررسی ngFor*

تا اینجا بر اساس داشتن لیستی از محصولات یا عدم آن، جدول متناظری را نمایش داده و یا مخفی کردیم. اما این جدول هنوز فاقد ردیف‌های نمایش اعضای آرایه‌ی products است.
برای این منظور مجددا فایل قالب product-list.component.html را گشوده و سپس بدنه‌ی جدول را به نحو ذیل تکمیل کنید:
<tbody>
    <tr *ngFor='#product of products'>
        <td></td>
        <td>{{ product.productName }}</td>
        <td>{{ product.productCode }}</td>
        <td>{{ product.releaseDate }}</td>
        <td>{{ product.price }}</td>
        <td>{{ product.starRating }}</td>
    </tr>
</tbody>
یکی دیگر از دایرکتیوهای ساختاری، ngFor* نام دارد. کار آن تکرار قسمتی از DOM، به ازای تک تک عناصر لیست انتساب داده شده‌ی به آن است.
بنابراین ابتدا قسمتی از عناصر HTML را طوری کنار هم قرار می‌دهیم که جمع آن‌ها یک تک آیتم را تشکیل دهند. سپس با استفاده از ngFor* به AngularJS 2.0 اعلام می‌کنیم که این قطعه را به ازای عناصر لیست دریافتی، تکرار و رندر کند.
برای نمونه در مثال فوق می‌خواهیم ردیف‌های جدول تکرار شوند. بنابراین هر ردیف را به عنوان یک قطعه‌ی تکرار شونده‌ی توسط ngFor* مشخص می‌کنیم. به این ترتیب این ردیف و عناصر فرزند آن، به ازای تک تک محصولات موجود در آرایه‌ی products، تکرار خواهند شد.
علامت # در اینجا (product#) یک متغیر محلی را تعریف می‌کند که تنها در قالب جاری قابل استفاده خواهد بود و همچنین فقط در فرزندان tr تعریف شده قابل دسترسی هستند.
به علاوه در اینجا بجای in از of استفاده شده‌است. این of از ES 6 گرفته شده‌است. زمانیکه از حلقه‌ی جدید for...of استفاده می‌شود، متغیر محلی product حاوی یک عنصر از لیست product خواهد بود؛ اما اگر از حلقه‌ی قدیمی for...in استفاده می‌شد، تنها ایندکس عددی این عناصر در دسترس قرار می‌گرفتند. به همین جهت است که در این حلقه، اکنون product.productName به نام محصول آن عنصر آرایه‌ی دریافتی اشاره می‌کند و قابل استفاده است.

تا اینجا اگر برنامه را اجرا کنید، چنین خروجی را مشاهده خواهید کرد:


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: MVC5Angular2.part3.zip


خلاصه‌ی بحث

از inline templateها جهت معرفی قالب‌های کوتاه استفاده می‌شود. در اینجا از "" برای معرفی قالب یک سطری و یا از back tickهای ES 6، برای تعریف قالب‌های چندسطری استفاده خواهد شد. برای قالب‌های مفصل‌تر، بهتر است Linked templateها استفاده شود؛ با پشتیبانی کامل ادیتورهای موجود از لحاظ تکمیل و بررسی کدها.
برای استفاده از یک کامپوننت در کامپوننتی دیگر، نام selector آن‌را به صورت یک المان جدید HTML در قالب دیگری ذکر کرده و سپس با استفاده از خاصیت directives، نام کلاس متناظر با آن‌را نیز ذکر می‌کنیم. همچنین کار import ماژول آن نیز باید در ابتدای فایل صورت گیرد.
جهت غنی سازی قالب‌ها و کامپوننت‌ها و نمایش اطلاعات فقط خواندنی می‌توان از binding یک طرفه‌ی ویژه‌ای به نام interpolation استفاده کرد. کار آن اتصال یک خاصیت عمومی کلاس کامپوننت، به قالب آن است. interpolation توسط {{}} تعریف می‌شود و می‌تواند شامل محاسبات نیز باشد.
همچنین در ادامه‌ی بحث، نحوه‌ی کار با دو دایرکتیو توکار ساختاری AngularJS 2.0 را نیز بررسی کردیم. این دایرکتیوهای ساختاری نیاز است با ستاره شروع شوند و عبارت انتساب داده شده‌ی به آن‌ها باید داخل "" قرار گیرد (برخلاف interpolation که نیازی به اینکار ندارد). از ngIf* برای حذف یا افزودن یک المان و فرزندان آن از/به DOM استفاده می‌شود. اگر عبارت منتسب به آن به true ارزیابی شود، این المان از صفحه حذف خواهد شد. از ngFor* برای تکرار المانی مشخص به همراه فرزندان آن به تعداد اعضای لیستی که برای آن تعیین می‌گردد، استفاده می‌شود. متغیر محلی این پیمایشگر با # مشخص شده و حلقه‌ی آن با of بجای in تعریف می‌شود.
بازخوردهای پروژه‌ها
نظر سنجی در مورد نحوه تکمیل پروژه

به نظر دوستان بهتر است است کدام یک از فریمورک‌های زیر را مورد استفاده قرار دهم تا در آینده هم به توان از این قالب به عنوان یک فریمورک برای کار‌های روزمره استفاده کنیم؟ واقعا نسبت به این موضوع وسواس شدیدی دارم .

  1. Kendo UI
  2. AngularJs
  3. AngularJs X-editable
  4. یا روش معمول و استفاده از HTML خالی
برای دسترسی داینامیک سورس  SmartStore  را هم بررسی کردم و به نظرم  ورش جالبی بود. ولی بنده با آن شیوه عمل نمیکنم درواقع با Attribute‌ها کار را به صورت داینامیک‌تر جلو میبرم .
لطفا سورس کد را در مخزن آن  بررسی کنید و پیشنهادات و نظرات خود را در مورد بهبود و تکمیل این قالب  و حتی  در مورد روشی که برای مدیریت داینامیک دسترسی‌ها در نظر گرفته ایم، بفرمایید. 

بعد از نظرات دوستان 2 روز فرصت لازم دارم پروژه را کامل کنم .
با تشکر
مطالب
انجام پی در پی اعمال Async به کمک Iterators - قسمت اول

تقریبا تمام اعمال کار با شبکه در Silverlight از مدل asynchronous programming پیروی می‌کنند؛ از فراخوانی یک متد وب سرویس تا دریافت اطلاعات از وب و غیره. اگر در سایر فناوری‌های موجود در دات نت فریم ورک برای مثال جهت کار با یک وب سرویس هر دو متد همزمان و غیرهمزمان در اختیار برنامه نویس هستند اما اینجا خیر. اینجا فقط روش‌های غیرهمزمان مرسوم هستند و بس. خیلی هم خوب. یک چارچوب کاری خوب باید روش استفاده‌ی صحیح از کتابخانه‌های موجود را نیز ترویج کند و این مورد حداقل در Silverlight اتفاق افتاده است.
برای مثال فراخوانی‌های زیر را در نظر بگیرید:
private int n1, n2;

private void FirstCall()
{
Service.GetRandomNumber(10, SecondCall);
}

private void SecondCall(int number)
{
n1 = number;
Service.GetRandomNumber(n1, ThirdCall);
}

private void ThirdCall(int number)
{
n2 = number;
// etc
}
عموما در اعمال Async پس از پایان عملیات در تردی دیگر، یک متد فراخوانی می‌گردد که به آن callback delegate نیز گفته می‌شود. برای مثال توسط این سه متد قصد داریم اطلاعاتی را از یک وب سرویس دریافت و استفاده کنیم. ابتدا FirstCall فراخوانی می‌شود. پس از پایان کار آن به صورت خودکار متد SecondCall فراخوانی شده و این متد نیز یک عملیات Async دیگر را شروع کرده و الی آخر. در نهایت قصد داریم توسط مقادیر بازگشت داده شده منطق خاصی را پیاده سازی کنیم. همانطور که مشاهده می‌کنید این اعمال زیبا نیستند! چقدر خوب می‌شد مانند دوران synchronous programming (!) فراخوانی‌های این متدها به صورت ذیل انجام می‌شد:
private void FetchNumbers()
{
int n1 = Service.GetRandomNumber(10);
int n2 = Service.GetRandomNumber(n1);
}
در برنامه نویسی متداول همیشه عادت داریم که اعمال به صورت A –> B –> C انجام شوند. اما در Async programming ممکن است ابتدا C انجام شود، سپس A و بعد B یا هر حالت دیگری صرفنظر از تقدم و تاخر آن‌ها در حین معرفی متدهای مرتبط در یک قطعه کد. همچنین میزان خوانایی این نوع کدنویسی نیز مطلوب نیست. مانند مثال اول ذکر شده، یک عملیات به ظاهر ساده به چندین متد منقطع تقسیم شده است. البته به کمک lambda expressions مثال اول را به شکل زیر نیز می‌توان در طی یک متد ارائه داد اما اگر تعداد فراخوانی‌ها بیشتر بود چطور؟ همچنین آیا استفاده از عدد n2 بلافاصله پس از عبارت ذکر شده مجاز است؟ آیا عملیات واقعا به پایان رسیده و مقدار مطلوب به آن انتساب داده شده است؟
private void FetchNumbers()
{
int n1, n2;

Service.GetRandomNumber(10, result =>
{
n1 = result;
Service.GetRandomNumber(n1, secondResult =>
{
n2 = secondResult;
});
});
}

به عبارتی می‌خواهیم کل اعمال انجام شده در متد FetchNumbers هنوز Async باشند (ترد اصلی برنامه را قفل نکنند) اما پی در پی انجام شوند تا مدیریت آن‌ها ساده‌تر شوند (هر لحظه دقیقا بدانیم که کجا هستیم) و همچنین کدهای تولیدی نیز خواناتر باشند.
روش استانداری که توسط الگوهای برنامه نویسی برای حل این مساله پیشنهاد می‌شود، استفاده از الگوی coroutines است. توسط این الگو می‌توان چندین متد Async را در حالت معلق قرار داده و سپس در هر زمانی که نیاز به آن‌ها بود عملیات آن‌ها را از سر گرفت.
دات نت فریم ورک حالت ویژه‌ای از coroutines را توسط Iterators پشتیبانی می‌کند (از C# 2.0 به بعد) که در ابتدا نیاز است از دیدگاه این مساله مروری بر آن‌ها داشته باشیم. مثال بعد یک enumerator را به همراه yield return ارائه داده است:

using System;
using System.Collections.Generic;
using System.Threading;

namespace CoroutinesSample
{
class Program
{
static void printAll()
{
foreach (int x in integerList())
{
Console.WriteLine(x);
}
}

static IEnumerable<int> integerList()
{
yield return 1;
Thread.Sleep(1000);
yield return 2;
yield return 3;
}

static void Main()
{
printAll();
}
}
}

کامپایلر سی شارپ در عمل یک state machine را برای پیاده سازی این عملیات به صورت خودکار تولید خواهد کرد:

private bool MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>2__current = 1;
this.<>1__state = 1;
return true;

case 1:
this.<>1__state = -1;
Thread.Sleep(0x3e8);
this.<>2__current = 2;
this.<>1__state = 2;
return true;

case 2:
this.<>1__state = -1;
this.<>2__current = 3;
this.<>1__state = 3;
return true;

case 3:
this.<>1__state = -1;
break;
}
return false;
}

در حین استفاده از یک IEnumerator ابتدا در وضعیت شیء Current آن قرار خواهیم داشت و تا زمانیکه متد MoveNext آن فراخوانی نشود هیچ اتفاق دیگری رخ نخواهد داد. هر بار که متد MoveNext این enumerator فرخوانی گردد (برای مثال توسط یک حلقه‌ی foreach) اجرای متد integerList ادامه خواهد یافت تا به yield return بعدی برسیم (سایر اعمال تعریف شده در حالت تعلیق قرار دارند) و همینطور الی آخر.
از همین قابلیت جهت مدیریت اعمال Async پی در پی نیز می‌توان استفاده کرد. State machine فوق تا پایان اولین عملیات تعریف شده صبر می‌کند تا به yield return برسد. سپس با فراخوانی متد MoveNext به عملیات بعدی رهنمون خواهیم شد. به این صورت دیدگاهی پی در پی از یک سلسه عملیات غیرهمزمان حاصل می‌گردد.

خوب ما الان نیاز به یک کلاس داریم که بتواند enumerator ایی از این دست را به صورت خودکار مرحله به مرحله آن هم پس از پایان واقعی عملیات Async قبلی (یا مرحله‌ی قبلی)، اجرا کند. قبل از اختراع چرخ باید متذکر شد که دیگران اینکار را انجام داده‌اند و کتابخانه‌های رایگان و یا سورس بازی برای این منظور موجود است.


ادامه دارد ...

مطالب
آموزش Knockout.Js #1
اگر از برنامه نویس‌های پروژه‌های WPF درباره ویژگی‌های مهم الگوی MVVM بپرسید به احتمال زیاد اولین مطلبی که عنوان می‌شود این است که هنگام کار با الگوی MVVM در WPF باید از مباحث data-binding استفاده شود. به صورت خلاصه، data-binding مکانیزمی است که عناصر موجود در Xaml را به آبجکت‌های موجود در ViewModel یا سایر عناصر Xaml مقید می‌کند به طوری که با تغییر مقدار در آبجکت‌های ViewModel، عناصر View نیز خود را به روز می‌کنند یا با تغییر در مقادیر عناصر Xaml، آبجکت‌های متناظر در ViewModel نیز تغییر خواهند کرد(در صورت تنظیم Mode = TwoWay).
Knockout.Js  چیست؟
در یک جمله Knockout.Js یک فریم ورک جاوا اسکریپ است که امکان پیاده سازی الگوی MVVM و مکانیزم data-binding را در پروژه‌های تحت وب به راحتی میسر می‌کند. به عبارت دیگر عناصر DOM را به data-model و آبجکت‌های data-model را به عناصر DOM مقید می‌کند، به طوری که با هر تغییر در مقدار یا وضعیت این عناصر یا آبجکت ها، تغییرات به موارد مقید شده نیز اعمال می‌گردد. به تصاویر زیر دقت کنید!
به روز رسانی data-model بدون استفاده از KO

به روز رسانی data-model با استفاده از KO

ویژگی‌های مهم KO
»ارائه یک راه حل بسیار ساده و واضح برای اتصال بخش‌های مختلف UI به data-model
»به روز رسانی خودکار عناصر و بخش‌های مختلف UI بر اساس تغییرات صورت گرفته در data-model
»به صورت کامل با کتابخانه و توابع javascript پیاده سازی شده است.
»حجم بسیار کم(سیزده کیلو بایت) بعد از فشرده سازی
»سازگار با تمام مروگرهای جدید(... ,IE 6+, Firefox 2+, Chrome, Safari )
»امکان استفاده راحت بدون اعمال تغییرات اساسی در معماری پروژه هایی که در فاز توسعه هستند و بخشی از مسیر توسعه را طی کرده اند
»و...
آیا KO برای تکمیل JQuery در نظر گرفته شده است یا جایگزین؟
در اینکه JQuery بسیار محبوب است و در اکثر پروژه‌های تحت وب مورد استفاده است شکی وجود ندارد ولی این بدان معنی نیست که با توجه به وجود JQuery و محبوبیت آن دیگر نیازی به KO احساس نمی‌شود. به عنوان یک مثال ساده : فرض کنید در یک قسمت از پروژه قصد داریم یک لیست از داده‌ها را نمایش دهیم. در پایین لیست تعداد آیتم‌های موجود در لیست مورد نظر نمایش داده میشود. یک دکمه Add داریم که امکان اضافه شدن آیتم جدید را در اختیار ما قرار می‌دهد. بعد از اضافه شدن یک مقدار، باید عددی که تعداد آیتم‌های لیست را نمایش می‌دهد به روز کنیم. خب اگر قصد داشته باشیم این کار را با JQuery انجام دهیم راه حل‌های زیر پیش رو است :
» به دست آوردن تعداد tr‌های جدول موجود؛
»به دست آوردن تعداد div‌های موجود با استفاده از یک کلاس مشخص css؛
» یا حتی به دست آوردن تعداد آیتم‌های نمایشی در  span  هایی مشخص.
و البته سایر راه حل ها...
حال فرض کنید دکمه‌های دیگر نظیر Delete نیز مد نظر باشد که مراحل بالا تکرار خواهند شد. اما با استفاده از KO به راحتی می‌توانیم تعداد آیتم‌های موجود در یک آرایه را به یک عنصر مشخص bind کنیم به طور با هر تغییر در این مقدار، عنصر مورد نظر نیز به روز می‌شود یا به بیانی دیگر همواره تغییرات observe خواهند شد. برای مثال:
 Number of items :<span data-bind="text: myList().count"></span>
در نتیجه برای کار با KO وابستگی مستقیم به استفاده از JQeury وجود ندارد ولی این امکان هست که بتوانیم هم از JQuery و هم از KO در کنار هم به راحتی استفاده کنیم و از قدرت‌های هر دو فریم ورک بهره ببریم و البته KO جایگزینی برای JQuery نخواهد بود.
در پست بعد، شروع به کار با KO آموزش داده خواهد شد.
ادامه دارد...

 
مطالب
تقویم شمسی کاملا Native برای Blazor
یکی از مزایای Blazor، استفاده از دانش C# / HTML / CSS (که خیلی از ما اینها را هم اکنون بلد هستیم) برای نوشتن برنامه‌های وب (SPA / PWA)، برنامه‌های Android / iOS / Windows و وب‌سایت‌هایی با قابلیت Pre Rendering و SEO Friendly است. با یک بار کدنویسی به کمک Blazor، ولی با Configuration‌های متفاوت می‌توان خروجی‌های مختلفی را برای پلتفرم‌های مختلف گرفت؛ برای مثال Blazor Hybrid خروجی Android / iOS / Windows و Blazor Web Assembly خروجی PWA / SPA و در نهایت Blazor Static خروجی وب سایت می‌دهد. به علاوه حالت Blazor Server نیز وجود دارد که امروزه بزرگ‌ترین مزیت آن، Development experience فوق‌العاده‌اش هست که در آن با استفاده از Hot Reload، می‌توان تغییرات در فایل‌های SCSS / C# / Razor را به صورت آنی، بدون نیاز به Build مجدد، رفرش کردن و از دست دادن State مشاهده نمود. امکان استفاده از Nuget Packageهای DotNet ای در Android / iOS / Windows / Web در کنار امکان استفاده از امکانات Native هر پلتفرم نیز از دیگر مزایای این روش است.

اما یکی از موانع استفاده‌ی جدی از Blazor در پروژه‌های داخلی، نبود تقویم شمسی است که سبک بوده و پیش نیاز خاصی جز خود Blazor نداشته باشد. یک راه حل جدید برای حل این مشکل، استفاده از Bit Components است که اخیرا به صورت Open Source ارائه شده است. شما می‌توانید Repository مربوطه را Fork نموده، Clone نمایید، به فولدر src بروید و با ویژال استودیو، Bit.Client.Web.BlazorUI.sln را باز کنید و سورس کامپوننت‌ها را به همراه تست‌های خودکار آن ببینید.
در سایت مربوطه نیز می‌توانید دمویی از بیش از ۲۷ کامپوننت را شامل File uploader، Drop Down، Date Picker، Color Picker، Tree list و... مشاهده کنید که هر کدام دارای Documentation کامل بوده و آماده به استفاده در پروژه‌های شما هستند.
برای استفاده از Bit Components در پروژه خود، ابتدا Package مربوطه را نصب نمایید و سپس فایل js و css مربوطه را نیز به index.html یا Host.cshtml یا Layout.cshtml اضافه کنید (بسته به تنظیمات پروژه‌تان).
در Bit Components جز معدود مواردی که چند خطی با JavaScript توسعه داده شده‌است، کمپوننت‌ها با C# / Razor / CSS توسعه داده شده‌اند. این روش نسبت به روش‌هایی که بر روی کمپوننت‌های کاملا JavaScript ای، اصطلاحا Wrapper ایجاد می‌کنند، دارای دو مزیت سرعت بالاتر و تضمین کار کردن آن در حالت‌های مختلف مانند Pre Rendering است.
<link href="_content/Bit.Client.Web.BlazorUI/styles/bit.blazorui.min.css" rel="stylesheet" />
<script src="_content/Bit.Client.Web.BlazorUI/scripts/bit.blazorui.min.js"></script>  
همچنین در فایل Imports.razor نیز using زیر را اضافه کنید
@using Bit.Client.Web.BlazorUI
به همین سادگی! حال برای تست، از Bit Button به صورت زیر استفاده کنید و اگر درست بود، می‌توانید سراغ کامپوننت‌های پیچیده‌تر همچون Date Picker بروید.
<BitButton>Hello!</BitButton>
برای Bit Date Picker نیز در razor خود یک Property یا Field برای نگه‌داری Date انتخاب شده داشته باشید (برای مثال به اسم BirthDate) که لازم است از جنس DateTimeOffset باشد (دقت کنید، نمایش و گرفتن تاریخ به شمسی یا میلادی می‌تواند باشد که این بر اساس Culture جاری سیستم است (توضیحات اضافه‌تر در قسمت پایانی مقاله)، ولی در نهایت شما DateTimeOffset میلادی انتخاب شده را خواهید داشت)
<BitDatePicker SelectedDate="@BirthDate"></BitDatePicker>
این کامپوننت دارای تنظیمات بسیاری است که می‌توانید در این صفحه آنها را مطالعه و در پروژه خود تست نمایید. اما بد نیست در مورد قسمت Culture Info که کمی پیچیده‌تر است، توضیحاتی داشته باشیم.
در C# .NET، کلاس CultureInfo، وظیفه نگهداری مواردی چون چند زبانگی، تقویم‌های مختلف (اعم از شمسی و...)، موارد مربوط به ارز (برای مثال علامت $ یا ریال و...) را به عهده دارد. از جمله مزایای BitDatePicker، سازگاری با CultureInfo است، به نحوی که CultureInfo.CurrentUICulture هر چه که باشد، بر اساس آن عمل می‌کند. بنابراین می‌توانید در Program.cs پروژه Blazor خود بنویسید:
CultureInfo.CurrentUICulture = new CultureInfo("fa-IR");
و وقتی BitDatePicker در یکی از صفحات باشد، چون fa-IR از Persian Calendar استفاده می‌کند، پس تقویم به صورت شمسی نمایش داده می‌شود.

سوال اول: اگر بخواهیم در کل سیستم، تقویم شمسی باشد، ولی در یکی از صفحات میلادی چه؟ خب می‌توانیم در آن صفحه، به شکل زیر از BitDatePicker استفاده کنیم:
<BitDatePicker Culture="@(new System.Globalization.CultureInfo("en-US"))" />

سوال دوم: تقویم شمسی نمایش داده شده، اسامی ماه‌ها را به صورت فینگلیش نمایش می‌دهد و یا اسامی خلاصه شده روزها صحیح نیست!
این به خود BitDatePicker ربطی ندارد، بلکه به CultureInfo فارسی خود dotnet مرتبط است، اما شما چگونه می‌توانید این مورد را بهبود بدید؟
شما می‌توانید ابتدا با
var cultureInfo = CultureInfo.CreateSpecificCulture("fa-IR")
یک CultureInfo فارسی قابل ویرایش بسازید، برای مثال بنویسید
cultureInfo.DateTimeFormat.MonthNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", "" };  
یک نمونه پیاده‌سازی کامل در اینجا
در ادامه لازم هست چه Culture Info ای را که خودتان سفارشی سازی کرده‌اید، یا Culture Info‌های سیستمی را در CultureInfo.CurrentUICulture قرار بدهید تا BitDatePicker از آن پیروی کند.
در صورت بروز هر گونه مشکلی یا درخواست اضافه شدن امکانی، در repo مربوطه روی GitHub می‌توانید یک issue را ثبت کنید.
نظرات مطالب
اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity
سلام.
پروژه اعتبارسنجی jwt را مطابق همین پست انجام دادم و روی لوکال روی پورت 4983 اجرا شده است.  پروژه فرانت انگولار روی لوکال پورت 4200 در حال اجراست. و برای لاگین مستقیما به 4983 ریکوئست میفرستد و توکن دریافت میکند. سورس اصلی بکند روی پروژه دیگری روی لوکال پورت 7744 در حال اجراست. فرانت بعد از لاگین دیگر ارتباطی با 4983 ندارد و از این به بعد همه ریکوئست‌ها را به پورت 7744 ارسال میکند. در واقع 4983 فقط پروژه jwt به تنهایی است. فرانت وقتی ریکوئستی را به 7744 میفرستد نیاز به ولید شدن توکن دارد و باید توکن را برای بررسی به 4983 ارسال کند. و در startup 7744 این کد نوشته شده است:
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
            {
                options.Authority = "http://localhost:4983/";
                options.Audience = "Any";
                options.RequireHttpsMetadata = false;
                options.Events = new JwtBearerEvents();
                options.Configuration = new OpenIdConnectConfiguration();
                options.Events.OnTokenValidated = async context =>
                {
                    IEnumerable<Claim> userClaims = await ReadClaimsFromCacheAsync(context);

                    if (ThereIsNoCache(userClaims))
                    {
                        userClaims = await GetUserInfoAsync(context, authority);
                        await StoreInCacheAsync(userClaims, context);
                    }

                    context.Principal = new ClaimsPrincipal(new ClaimsIdentity(userClaims, "jwt", JwtClaimTypes.Subject, "roles"));
                };
                options.Events.OnAuthenticationFailed = async context =>
                {
                    await TestInvalid(context);
                };
            });
که خطای زیر را دریافت میکنم:
IDX10500: Signature validation failed. No security keys were provided to validate the signature.
ممنون میشم راهنمایی بفرمایید. (نکته: یک کنترلر تستی روی 4983 نوشته ام که بعد از لاگین مستقیم از فرانت کال کردم و بدون مشکل توکن ولید شد و به کنترلر و اکشن موردنظر دسترسی پیدا کرد. ولی وقتی اعتبارسنجی از طریق 7744 صورت میگیرد خطا میدهد.)
نظرات مطالب
EF Code First #2
لطف کنید سؤالی رو که مطرح می‌کنید در حیطه مطلب جاری عنوان شده باشد و خارج از آن نباشد.
سؤال شما هم بحث کلاینت سروری است و نه بحث کلاینت تنها که EF روی آن مشغول به کار است.
- می‌شود در متد Seed ایی که در بالا توضیح دادم در SQL Server تریگر درست کرد. (که مثلا اگر کاربر دیگری به شرط اینکه این کاربر جزو کاربران تعریف شده در خود SQL Server باشد نه در برنامه شما، اتفاق خاصی رخ دهد. برنامه شما هم بدیهی است باید سرور را مدام چک کند تا از این مساله مطلع شود)- SQL Server مبحثی دارد به نام Service Broker : (^). توسط آن می‌توان از طریق سرور به کلاینت اطلاع رسانی کرد. بازهم خارج است از بحث یک ORM. یا تمام ORMهای موجود. - EF مبحثی دارد به نام Concurrency check که اگر شخصی در شبکه بر روی رکوردی که همین الان شما مشغول به کار هستید، تغییری را ایجاد کرد، به شما اطلاع رسانی کند. (در قسمت‌های بعدی بحث خواهد شد). البته این هم خودکار نیست. لازم است یک رفت و برگشت به سرور انجام شود.- entity framework auditing هم میسر است. خودکار نیست. در همان کلاس Context فوق که از DbContext مشتق می‌شود می‌توان متد تحریف شده public override int SaveChanges را تعریف کرد. در اینجا می‌توان به تمام تغییراتی که قرار است اعمال شوند دسترسی داشت. مثلا آن‌ها را در یک جدول مجزا ثبت کرد. بدیهی است برنامه بعدا نیاز خواهد داشت از این جدول گزارشگیری کند.
مطالب دوره‌ها
چرا XML و چرا پشتیبانی توکار از آن در SQL Server
مقدمه

فیلدهای XML از سال 2005 به امکانات توکار SQL Server اضافه شده‌اند و بسیاری از مزایای دنیای NoSQL را درون SQL Server رابطه‌ای مهیا می‌سازند. برای مثال با تعریف یک فیلد به صورت XML، می‌توان از هر ردیف به ردیفی دیگر، اطلاعات متفاوتی را ذخیره کرد؛ به این ترتیب امکان کار با یک فیلد که می‌تواند اطلاعات یک شیء را قبول کند و در حقیقت امکان تعریف اسکیمای پویا و متغیر را در کنار امکانات یک بانک اطلاعاتی رابطه‌ای که از اسکیمای ثابت پشتیبانی می‌کند، میسر می‌شود.
همچنین SQL Server در این حالت قابلیتی را ارائه می‌دهد که در بسیاری از بانک‌های اطلاعاتی NoSQL میسر نیست. در اینجا در صورت نیاز و لزوم می‌توان اسکیمای کاملا مشخصی را به یک فیلد XML نیز انتساب داد؛ هر چند این مورد اختیاری است و می‌توان یک un typed XML را نیز بکار برد. به علاوه امکانات کوئری گرفتن توکار از این اطلاعات را به کمک XPath ترکیب شده با T-SQL، نیز فراموش نکنید.
بنابراین اگر یکی از اهداف اصلی گرایش شما به سمت دنیای NoSQL، استفاده از امکان تعریف اطلاعاتی با اسکیمای متغیر و پویا است، فیلدهای نوع XML اس کیوال سرور را مدنظر داشته باشید.
یک مثال عملی: فناوری Azure Dev Fabric's Table Storage (نسخه Developer ویندوز Azure که روی ویندوزهای معمولی اجرا می‌شود؛ یک شبیه ساز خانگی) به کمک SQL Server و فیلدهای XML آن طراحی شده است.



چرا XML و چرا پشتیبانی توکار از آن در SQL Server

یک سند XML معمولا بیشتر از یک قطعه داده را در خود نگهداری می‌کند و نوع داده‌ی پیچیده محسوب می‌شود؛ برخلاف داده‌هایی مانند int یا varchar که نوع‌هایی ساده بوده و تنها یک قطعه از اطلاعات خاصی را در خود نگهداری می‌کنند. بنابراین شاید این سؤال مطرح شود که چرا از این نوع داده پیچیده در SQL Server پشتیبانی شده‌است؟
- از سال‌های نسبتا دور، از XML برای انتقال داده‌ها بین سیستم‌ها و سکوهای کاری مختلف استفاده شده‌است.
- استفاده‌ی گسترده‌ای در برنامه‌های تجاری دارد.
- بسیاری از فناوری‌های موجود از آن پشتیبانی می‌کنند.
برای مثال اگر با فناوری‌های مایکروسافتی کار کرده باشید، به طور قطع حداقل در یک یا چند قسمت از آن‌ها، مستقیما از XML استفاده شده‌است.
بنابراین با توجه به اهمیت و گستردگی استفاده از آن، بهتر است پشتیبانی توکاری نیز از آن داخل موتور یک بانک اطلاعاتی، پیاده سازی شده باشد. این مساله سهولت تهیه پشتیبان‌های خودکار، بازیابی آن‌ها و امنیت یکپارچه با SQL Server را به همراه خواهد داشت؛ به همراه تمام زیرساخت‌های مهیای در SQL Server.



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

الف) ذخیره سازی متنی
این روش نیاز به نگارش خاصی از SQL Server یا بانک اطلاعاتی الزاما خاصی نداشته و با تمام بانک‌های اطلاعاتی رابطه‌ای سازگار است؛ مثلا از فیلدهای varchar برای ذخیره سازی آن استفاده شود. مشکلی که این روش به همراه خواهد داشت، از دست دادن ارزش یک سند XML و برخورد متنی با آن است. زیرا در این حالت برای تعیین اعتبار آن یا کوئری گرفتن از آن‌ها نیاز است اطلاعات را از بانک اطلاعاتی خارج کرده و در لایه‌ای دیگر از برنامه، کار جستجو پردازش آن‌ها را انجام داد.

ب) تجزیه XML به چندین جدول رابطه‌ای
برای مثال یک سند XML را درنظر بگیرید که دارای اطلاعات شخص و خرید‌های او است. می‌توان این سند را به چندین فیلد در چندین جدول مختلف رابطه‌ای تجزیه کرد و سپس با روش‌های متداول کار با بانک‌های اطلاعاتی رابطه‌ای از آن‌ها استفاده نمود.

ج) ذخیره سازی آن‌ها توسط فیلدهای خاص XML
در این حالت با استفاده از فیلدهای ویژه XML می‌توان از فناوری‌های مرتبط با XML تمام و کمال استفاده کرد. برای مثال تهیه کوئری‌های پیچیده داخل همان بانک اطلاعاتی بدون نیاز به تجزیه سند به چندین جدول و یا خارج کردن آن‌ها از بانک اطلاعاتی و جستجوی بر روی آن‌ها در لایه‌ای دیگر از برنامه.


موارد کاربرد XML در SQL Server

کاربردهای مناسب

- اطلاعات، سلسله مراتبی و تو در تو هستند. XQuery و XPath در این موارد بسیار خوب عمل می‌کند.
- ساختار قسمتی از اطلاعات ثابت است و قسمتی از آن خیر. برای نمونه، یک برنامه‌ی فرم ساز را درنظر بگیرید که هر فرم آن هر چند دارای یک سری خواص ثابت مانند نام، گروه و امثال آن است، اما هر کدام دارای فیلدهای تشکیل دهنده متفاوتی نیز می‌باشد. به این ترتیب با استفاده از یک فیلد XML، دیگری نیازی به نگران بودن در مورد نحوه مدیریت اسکیمای متغیر مورد نیاز، نخواهد بود.
نمونه‌ی دیگر آن ذخیره سازی خواص متغیر اشیاء است. هر شیء دارای یک سری خواص ثابت است اما خواص توصیف کننده‌ی آن‌ها از هر رکورد به رکوردی دیگر متفاوت است.

کاربردهای نامناسب

- کل اطلاعات را داخل فیلد XML قرار دادن. هدف از فیلدهای XML قرار دادن یک دیتابیس داخل یک سلول نیست.
- ساختار تعریف شده کاملا مشخص بوده و به این زودی‌ها هم قرار نیست تغییر کند. در این حالت استفاده از قابلیت‌های رابطه‌ای متداول SQL Server مناسب‌تر است.
- قرار دادن اطلاعات باینری بسیار حجیم در سلول‌های XML ایی.
 


تاریخچه‌ی پشتیبانی از XML در نگارش‌های مختلف SQL Server

الف) SQL Server 2000
در SQL Server 2000 روش (ب) توضیح داده شده در قسمت قبل، پشتیبانی می‌شود. در آن برای تجزیه یک سند XML به معادل رابطه‌ای آن، از تابعی به نام OpenXML استفاده می‌شود و برای تبدیل این اطلاعات به XML از روش Select … for XML می‌توان کمک گرفت. همچنین تاحدودی مباحث XPath Queries نیز در آن گنجانده شد‌ه‌است.

ب) SQL Server 2005
در نگارش 2005 آن، برای اولین بار نوع داده‌ای ویژه XML معرفی گشت به همراه امکان تعریف اسکیمای XML و اعتبارسنجی آن و پشتیبانی از XQuery برای جستجوی سریع بر روی داده‌های XML داخل همان بانک اطلاعاتی، بدون نیاز به استخراج اطلاعات XML و پردازش مجزای آن‌ها در لایه‌ای دیگر از برنامه.

ج) SQL Server 2008 به بعد
در اینجا فاز نگهداری این نوع داده خاص شروع شده و بیشتر شامل یک سری بهبودهای کوچک در کارآیی و نحوه‌ی استفاده از آن‌ها می‌شود.



استفاده از XML با کمک SQLCLR

از SQL Server 2005 به بعد، امکان استفاده از کلیه‌ی امکانات موجود در فضای نام System.Xml دات نت، در SQL Server نیز به کمک SQL CLR مهیا شده‌است. همچنین از SQL Server 2008 به بعد، امکانات فضای نام System.Xml.Linq و مباحث LINQ to XML نیز توسط SQL CLR پشتیبانی می‌شوند.
البته این امکانات در SQL Server 2005 نیز قابل استفاده هستند، اما اسمبلی شما unsafe تلقی می‌شود. پس از آزمایشات و بررسی کافی، فضای نام مرتبط با LINQ to XML و امکانات آن، به عنوان اسمبلی‌هایی امن و قابل استفاده در SQL Server 2008 به بعد، معرفی شده‌اند.




مزایای وجود فیلد ویژه XML در SQL Server

پس از اینکه فیلدهای XML به صورت یک نوع داده بومی بانک اطلاعاتی SQL Server معرفی شدند، مزایای ذیل بلافاصله در اختیار برنامه نویس‌ها قرار گرفت:
- امکان تعریف آن‌ها به صورت یک ستون جدولی خاصی
- استفاده از آن‌ها به عنوان یک پارامتر رویه‌های ذخیره شده
- امکان تعریف خروجی توابع scalar سفارشی تعریف شده به صورت XML
- امکان تعریف متغیرهای T-SQL از نوع XML

برای مثال در اینجا نحوه‌ی تعریف یک جدول جدید دارای فیلدی از نوع XML را مشاهده می‌کنید:
 CREATE TABLE xml_tab
(
  id INT,
  xml_col  XML
)
- پشتیبانی از فناوری‌های XML ایی مانند اعتبارسنجی اسکیما و نوشتن کوئری‌های پیشرفته با XQuery و XPath.
- امکان تعریف ایندکس‌های XML ایی اضافه شده‌است.



چه نوع XML ایی را می‌توان در فیلدهای XML ذخیره کرد؟

فیلدهای XML امکان ذخیره سازی داده‌های XML خوش فرم را مطابق استاندارد یک XML، دارند. حداکثر اندازه قابل ذخیره سازی در یک فیلد XML دو گیگابایت است.
البته امکانات مهیای در SQL Server در بسیاری از موارد فراتر از استاندارد یک XML هستند. به این معنا که در فیلدهای XML می‌توان Documents و یا Fragments را ذخیره سازی کرد. یک سند XML یا Document حاوی تنها یک ریشه اصلی است؛ اما یک Fragment می‌تواند بیش از یک ریشه اصلی را در خود ذخیره کند. یک مثال:
 DECLARE @xml_tab TABLE (xml_col XML)
-- document
INSERT @xml_tab VALUES ('<person/>')
-- fragment
INSERT @xml_tab VALUES ('<person/><person/>')
SELECT * FROM @xml_tab
مدل داده‌ای XML در SQL Server بر مبنای استانداردهای  XQuery و XPath طراحی شده‌است و این استانداردها Fragments را به عنوان یک قطعه داده XML معتبر، قابل پردازش می‌دانند؛ علاوه بر آن مقادیر null و خالی را نیز معتبر می‌دانند. برای مثال عبارات ذیل معتبر هستند:
 DECLARE @xml_tab TABLE (xml_col XML)
-- text only
INSERT @xml_tab VALUES ('data data data .....')
-- empty string
INSERT @xml_tab VALUES ('')
-- null value
INSERT @xml_tab VALUES (null)
SELECT * FROM @xml_tab
همچنین امکان ذخیره سازی یک متن خالی بدون فرمت نیز در اینجا مجاز است. بنابراین به کمک T-SQL می‌توان برای مثال نوع داده varchar و varchar max را به XML تبدیل کرد و برعکس. امکان تبدیل Text و NText (منسوخ شده) نیز به XML وجود دارد ولی در این حالت خاص، عکس آن، پشتیبانی نمی‌شود.
به علاوه باید دقت داشت که در SQL Server نوع داده‌ای XML برای ذخیره سازی داده‌ها بکار گرفته می‌شود. به این معنا که در اینجا پیشوندهای فضاهای نام XML بی‌معنا هستند.
 DECLARE @xml_tab TABLE (xml_col XML)
INSERT @xml_tab VALUES ('<doc/>')
INSERT @xml_tab VALUES ('<doc xmlns="http://www.doctors.com"/>')
-- این سه سطر در عمل یکی هستند
INSERT @xml_tab VALUES ('<doc xmlns="http://www.documents.com"/>')
INSERT @xml_tab VALUES ('<dd:doc xmlns:dd="http://www.documents.com"/>')
INSERT @xml_tab VALUES ('<rr:doc xmlns:rr="http://www.documents.com"/>')
SELECT * FROM @xml_tab
در این مثال، سه insert آخر در عمل یکی درنظر گرفته می‌شوند.



Encoding ذخیره سازی داده‌های XML

SQL Server امکان ذخیره سازی اطلاعات متنی را به فرمت UFT8، اسکی و غیره، دارد. اما جهت پردازش فیلدهای XML و ذخیره سازی آن‌ها از Collation پیش فرض بانک اطلاعاتی کمک خواهد گرفت. البته ذخیره سازی نهایی آن همیشه با فرمت UCS2 است (یونیکد دو بایتی).
 DECLARE @xml_tab TABLE  (id INT, xml_col XML)

INSERT INTO @xml_tab
VALUES
  (
5,
N'<?xml version="1.0" encoding="utf-8"?>
<doc1>
  <row name="vahid"></row>
</doc1>
')
برای نمونه به مثال فوق دقت کنید. اگر آن‌را اجرا کنید، برنامه با خطای ذیل متوقف خواهد شد:
 XML parsing: line 1, character 38, unable to switch the encoding
علت اینجا است که با قرار دادن N در ابتدای رشته XML ایی در حال ذخیره سازی، آن‌را به صورت یونیکد دوبایتی معرفی کرده‌ایم اما encoding سند در حال ذخیره سازی utf-8 تعریف شده‌است و این‌دو با هم سازگاری ندارند.
برای حل این مشکل باید N ابتدای رشته را حذف کرد. روش دوم، معرفی و استفاده از utf-16 است بجای utf-8 در ویژگی encoding.
همچنین در این حالت اگر encoding را utf-16 معرفی کنیم و ابتدای رشته در حال ذخیره سازی N قرار نگیرد، باز با خطای unable to switch the encoding مواجه خواهیم شد.



نحوه‌ی ذخیره سازی اطلاعات XML ایی در SQL Server

SQL Server فرمت اطلاعات XML وارد شده را حفظ نمی‌کند. برای مثال اگر قطعه کد زیر را اجرا کنید
 DECLARE @xml_tab TABLE  (id INT, xml_col XML)

INSERT INTO @xml_tab
VALUES
  (
5,
'<?xml version="1.0" encoding="utf-8"?><doc1><row name="vahid"></row></doc1>'
  )
   
SELECT * FROM @xml_tab
خروجی Select انجام شده به صورت زیر است:
 <doc1>
  <row name="vahid" />
</doc1>
اطلاعات و داده نهایی، بدون تغییری از آن قابل استخرج است. اما اصطلاحا lexical integrity آن حفظ نشده و نمی‌شود. بنابراین در اینجا ذکر سطر xml version ضروری نیست و یا برای مثال اگر ویژگی‌ها را توسط " و یا ' مقدار دهی کنید، همیشه توسط "  ذخیره خواهد شد.



ذخیره سازی داده‌هایی حاوی کاراکترهای غیرمجاز XML

اطلاعات دنیای واقعی همیشه به همراه اطلاعات تک کلمه‌ای ساده نیست. ممکن است نیاز شود انواع و اقسام حروف و تگ‌ها نیز در این بین به عنوان داده ذخیره شوند. روش حل استاندارد آن بدون نیاز به دستکاری اطلاعات ورودی، استفاده از CDATA است:
 DECLARE @xml_tab TABLE  (id INT, xml_col XML)

INSERT INTO @xml_tab
VALUES
  (
5,
'<person><![CDATA[ 3 > 2 ]]></person>'
  )
   
SELECT * FROM @xml_tab
در این حالت خروجی select اطلاعات ذخیره شده به صورت زیر خواهد بود:
 <person> 3 &gt; 2 </person>
به صورت خودکار قسمت CDATA پردازش شده و اصطلاحا حروف غیرمجاز XML ایی به صورت خودکار escape شده‌اند.



محدودیت‌های فیلدهای XML

- امکان مقایسه مستقیم را ندارند؛ بجز مقایسه با نال. البته می‌توان XML را تبدیل به مثلا varchar کرد و سپس این داده رشته‌ای را مقایسه نمود. برای مقایسه با null توابع isnull و coalesce نیز قابل بکارگیری هستند.
- order by و group by بر روی این فیلدها پشتیبانی نمی‌شود.
- به عنوان ستون کلید قابل تعریف نیست.
- به صورت منحصربفرد و unique نیز قابل علامتگذاری و تعریف نیست.
- فیلدهای XML نمی‌توانند دارای collate باشند.