نظرات مطالب
ASP.NET MVC #11
واقعا فوق العاده بود ... هر چی پیشرفته تر میشه جالب تر میشه :دی ... من از ViewModel برای جاهای دیگم استفاده میکنم ... مثه جستجوی پیشرفته(در کل فیلترینگ) که نیاز به چندین پارامتر مختلف داره ...
نظرات مطالب
پشتیبانی از حذف و به‌روز رسانی دسته‌ای رکوردها در EF 7.0

یک نکته‌ی تکمیلی: روش استفاده از خواص راهبری، در به‌روزسانی‌های دسته‌ای

در همین مثال مطلب جاری، فرض کنید موجودیت کاربر، به همراه خاصیت محاسباتی تعداد کتاب‌ها (NumberOfBooks) هم هست:

public class User
{
    // ...

    public virtual List<Book> Books { get; set; } = new();

    public int NumberOfBooks { get; set; }
}

اگر سعی کنیم این خاصیت را به صورت زیر به‌روز رسانی کنیم که در آن مقدار تعداد کتاب‌ها به کمک خاصیت راهبری Books محاسبه شود:

await context.Users.Where(x=> x.Id == 1).ExecuteUpdateAsync(s => s.SetProperty(user => user.NumberOfBooks,
                user => user.Books.Count()));

به خطای زیر می‌رسیم:

... could not be translated. Additional information: Translation of member 'Books' on entity type 'User' failed. This commonly occurs when the specified member is unmapped.

عنوان می‌کند که این خاصیت راهبری، نگاشت نشده‌است. برای رفع این مشکل باید به صورت زیر عمل کرد:

await context.Users
             .Where(user => user.Id == 1)
             .Select(user => new
             {
                user.NumberOfBooks,
                BooksCount = user.Books.Count()
             })
             .ExecuteUpdateAsync(s => s.SetProperty(arg => arg.NumberOfBooks, arg => arg.BooksCount));

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

مطالب
تهیه خروجی PDF و اکسل از حاصل جستجوی پویای jqGrid به کمک PDF Report
پیشنیازها
- صفحه بندی و مرتب سازی خودکار اطلاعات به کمک jqGrid در ASP.NET MVC
- فعال سازی و پردازش جستجوی پویای jqGrid در ASP.NET MVC
- سفارشی سازی عناصر صفحات پویای افزودن و ویرایش رکوردهای jqGrid در ASP.NET MVC
- آشنایی با کتابخانه‌ی PDF Report


اضافه کردن دکمه‌ی خروجی به jqGrid

برای تهیه خروجی از jqGrid نیاز است بدانیم، اکنون در چه صفحه‌ای از اطلاعات قرار داریم؟ بر روی چه ستونی، مرتب سازی صورت گرفته‌است؟ بر روی کدام فیلدها با چه مقادیری جستجو انجام شده‌است؟ تا ... بتوانیم بر این مبنا، منبع داده‌ی موجود را فیلتر کرده و لیست نهایی را تبدیل به گزارش کنیم. گزارشی که دقیقا با اطلاعاتی که کاربر در صفحه مشاهده می‌کند، تطابق داشته باشد.
خوشبختانه تمام این سؤالات توسط متد توکار excelExport در سمت سرور قابل دریافت است:
@section Scripts
{
    <script type="text/javascript">
    $(document).ready(function () {
        $('#list').jqGrid({
            caption: "آزمایش ششم",
                 // مانند قبل
            }).navGrid(
                 // مانند قبل
                    }).jqGrid('navButtonAdd', '#pager', {
                        caption: "", buttonicon: "ui-icon-print", title: "خروجی پی دی اف",
                        onClickButton: function () {
                            $("#list").jqGrid('excelExport', { url: '@Url.Action("GetProducts", "Home")' });
                        }
                    });
        });
    </script>
}

در اینجا توسط متد navButtonAdd یک دکمه‌ی جدید را اضافه کرده‌ایم که کلیک بر روی آن سبب فراخوانی متد excelExport و ارسال اطلاعات گزارش به url تنظیم شده‌است. باید دقت داشت که این اطلاعات از طریق Http Get به سرور ارسال می‌شوند و دقیقا اجزای آن همان اجزای جستجوی پویای jqGrid است:
public ActionResult GetProducts(string sidx, string sord, int page, int rows,
                                             bool _search, string searchField, string searchString,
                                             string searchOper, string filters, string oper)
با این تفاوت که یک oper نیز به مجموعه‌ی پارامترهای ارسالی به سرور اضافه شده‌است. این oper در اینجا با excel مقدار دهی می‌شود.
البته چون تعداد این پارامترها بیش از اندازه شده‌است، بهتر است آن‌ها را تبدیل به یک کلاس کرد:
namespace jqGrid06.Models
{
    public class JqGridRequest
    {
        public string sidx { set; get; }
        public string sord { set; get; }
        public int page { set; get; }
        public int rows { set; get; }
        public bool _search { set; get; }
        public string searchField { set; get; }
        public string searchString { set; get; }
        public string searchOper { set; get; }
        public string filters { set; get; }
        public string oper { set; get; }
    }
}
و متد جستجوی پویا را به نحو ذیل بازنویسی نمود:
        public ActionResult GetProducts(JqGridRequest request)
        {
            var list = ProductDataSource.LatestProducts;

            var pageIndex = request.page - 1;
            var pageSize = request.rows;
            var totalRecords = list.Count;
            var totalPages = (int)Math.Ceiling(totalRecords / (float)pageSize);

            var productsQuery = list.AsQueryable();

            productsQuery = new JqGridSearch().ApplyFilter(productsQuery, request, this.Request.Form);
            productsQuery = productsQuery.OrderBy(request.sidx + " " + request.sord);

            if (string.IsNullOrWhiteSpace(request.oper))
            {
                productsQuery = productsQuery
                                    .Skip(pageIndex * pageSize)
                                    .Take(pageSize);
            }
            else if (request.oper == "excel")
            {
                productsQuery = productsQuery
                                    .Skip(pageIndex * pageSize);
            }

            var productsList = productsQuery.ToList();

            if (!string.IsNullOrWhiteSpace(request.oper) && request.oper == "excel")
            {
                new ProductsPdfReport().CreatePdfReport(productsList);
            }

            var productsData = new JqGridData
            {
                Total = totalPages,
                Page = request.page,
                Records = totalRecords,
                Rows = (productsList.Select(product => new JqGridRowData
                {
                    Id = product.Id,
                    RowCells = new List<string>
                    {
                        product.Id.ToString(CultureInfo.InvariantCulture),
                        product.Name,
                        product.AddDate.ToPersianDate(),
                        product.Price.ToString(CultureInfo.InvariantCulture)
                    }
                })).ToArray()
            };

            return Json(productsData, JsonRequestBehavior.AllowGet);
        }

توضیحات:
اکثر قسمت‌های این متد با متدی که در مطلب «فعال سازی و پردازش جستجوی پویای jqGrid در ASP.NET MVC» مشاهده کردید یکی است؛ برای مثال order by آن با استفاده از کتابخانه‌ی Dynamic LINQ به صورت پویا عمل می‌کند و متد ApplyFilter، کار تهیه where پویا را انجام می‌دهد.
فقط در اینجا بررسی و پردازش پارامتر oper نیز اضافه شده‌است. اگر این پارامتر مقدار دهی شده باشد، یعنی نیاز است کل اطلاعات را واکشی کرد؛ زیرا می‌خواهیم گزارش گیری کنیم و نه اینکه صرفا اطلاعات یک صفحه را به کاربر بازگشت دهیم. همچنین در اینجا List نهایی فیلتر شده به یک گزارش Pdf Report ارسال می‌شود. این گزارش چون نهایتا اطلاعات را در مرورگر کاربر Flush می‌کند، کار به اجرای سایر قسمت‌ها نخواهد رسید و همینجا گزارش نهایی تهیه می‌شود.



کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید
jqGrid06.7z
 
مطالب دوره‌ها
کار با RavenDB از طریق REST API آن
در این قسمت قصد داریم برخلاف رویه معمول کار با RavenDB که از طریق کتابخانه‌های کلاینت آن انجام می‌شود، با استفاده از REST API آن، ساز و کار درونی آن‌را بیشتر بررسی کنیم.


REST چیست؟

برای درک ساختار پشت صحنه RavenDB نیاز است با مفهوم REST آشنا باشیم؛ زیرا سرور این بانک اطلاعاتی، خود را به صورت یک RESTful web service در اختیار مصرف کنندگان قرار می‌دهد.
REST مخفف representational state transfer است و این روزها هر زمانیکه صحبت از آن به میان می‌آید منظور یک RESTful web service است که با استفاده از تعدادی HTTP Verb استاندارد می‌توان با آن کار کرد؛ مانند GET، POST، PUT و DELETE. با استفاده از GET‌، یک منبع ذخیره شده بازگشت داده می‌شود. با استفاده از فعل PUT، اطلاعاتی به منابع موجود اضافه و یا جایگزین می‌شوند. POST نیز مانند PUT است با این تفاوت که نوع اطلاعات ارسالی آن اهمیتی نداشته و تفسیر آن به سرور واگذار می‌شود. از DELETE نیز برای حذف یک منبع استفاده می‌گردد.


چند مثال
فرض کنید REST API برنامه‌ای از طریق آدرس http://myapp.com/api/questions در اختیار شما قرار گرفته است. در این آدرس، به questions منابع یا Resource گفته می‌شود. اگر دستور GET پروتکل HTTP بر روی این آدرس اجرا شود، انتظار ما این است که لیست تمام سؤالات بازگشت داده شود و اگر از دستور POST استفاده شود، باید یک سؤال جدید به مجموعه منابع موجود اضافه گردد.
اکنون آدرس http://myapp.com/api/questions/1 را درنظر بگیرید. در اینجا عدد یک معادل Id اولین سؤال ثبت شده است. بر اساس این آدرس خاص، اینبار اگر دستور GET صادر شود، تنها اطلاعات سؤال یک بازگشت داده خواهد شد و یا اگر از دستور PUT استفاده شود، اطلاعات سؤال یک با مقدار جدید ارسالی جایگزین می‌شود و یا با فراخوانی دستور DELETE، سؤال شماره یک حذف خواهد گردید.


کار با دستور GET

در ادامه، به مثال قسمت قبل مراجعه کرده و تنها سرور RavenDB را اجرا نمائید (برنامه Raven.Server.exe)، تا در ادامه بتوانیم دستورات HTTP را بر روی آن امتحان کنیم. همچنین نیاز به برنامه معروف فیدلر نیز خواهیم داشت. از این برنامه برای ساخت دستورات HTTP استفاده خواهد شد.
پس از دریافت و نصب فیدلر، برگه Composer آن‌را گشوده و http://localhost:8080/docs/questions/1 را در حالت GET اجرا کنید:


در این حالت دستور بر روی بانک اطلاعاتی اجرا شده و خروجی را در برگه Inspectors آن می‌توان مشاهده کرد:



به علاوه در اینجا یک سری هدر اضافی (یا متادیتا) را هم می‌توان مشاهده کرد که RavenDB جهت سهولت کار کلاینت خود ارسال کرده است:


یک نکته: اگر آدرس http://localhost:8080/docs/questions را اجرا کنید، به معنای درخواست دریافت تمام سؤالات است. اما RavenDB به صورت پیش فرض طوری طراحی شده‌است که تمام اطلاعات را بازگشت ندهد و شعار آن Safe by default است. به این ترتیب مشکلات مصرف حافظه بیش از حد، پیش از بکارگیری یک سیستم در محیط کاری واقعی، توسط برنامه نویس یافت شده و مجبور خواهد شد تا برای نمایش تعداد زیادی رکورد، حتما صفحه بندی اطلاعات را پیاده سازی کرده و هربار تعداد معقولی از رکوردها را واکشی نماید.


کار با دستور PUT

اینبار نوع دستور را به PUT و آدرس را به http://localhost:8080/docs/questions/1 تنظیم می‌کنیم. همچنین در قسمت Request body، مقداری را که قرار است در سؤال یک درج شود، با فرمت JSON وارد می‌کنیم.
برای آزمایش صحت عملکرد آن، مرحله کار با دستور GET را یکبار دیگر تکرار نمائید:


همانطور که مشاهده می‌کنید، تغییر ما در عنوان سؤال یک، با موفقیت اعمال شده است.


کار با دستور POST

در حین کار با دستور PUT، نیاز است حتما Id سؤال مورد نظر برای به روز رسانی (و یا حتی ایجاد نمونه جدید، در صورت عدم وجود) ذکر شود. اگر نیاز است اطلاعاتی به سیستم اضافه شوند و Id آن توسط RavenDB انتساب داده شود، بجای دستور PUT از دستور POST استفاده خواهیم کرد.


مطابق تصویر، اطلاعات شیء مدنظر را با فرمت JSON به آدرس http://localhost:8080/docs/ ارسال خواهیم کرد. در این حالت اگر به برگه‌ی Inspectors مراجعه نمائیم، یک چنین خروجی JSON ایی دریافت می‌گردد:


Key در اینجا شماره منحصربفرد سند ایجاد شده است و برای دریافت آن تنها کافی است که دستور GET را بر روی آدرس زیر که نمایانگر Key دریافتی است، اجرا کنیم:
http://localhost:8080/docs/e0a92054-9003-4dda-84e2-93e83b359102


کار با دستور DELETE

برای حذف یک سند تنها کافی است آدرس آن‌را وارد کرده و نوع دستور را بر روی Delete قرار دهیم. برای مثال اگر دستور Delete را بر روی آدرس فوق که به همراه Id تولید شده توسط RavenDB است اجرا کنیم، بلافاصله سند از بانک اطلاعاتی حذف خواهد شد.


بازگشت چندین سند از بانک اطلاعاتی RavenDB

برای نمونه، در فراخوانی‌های Ajaxایی نیاز است چندین رکورد با هم بازگشت داده شوند. برای این منظور باید یک درخواست Post ویژه را مهیا کرد:



در اینجا آدرس ارسال اطلاعات، آدرس خاص http://localhost:8080/queries است. اطلاعات ارسالی به آن، آرایه‌ای از Idهای اسنادی است که به اطلاعات آن‌ها نیاز داریم.


بنابراین برای کار با RavenDB در برنامه‌های وب و خصوصا کدهای سمت کلاینت آن، نیازی به کلاینت یا کتابخانه ویژه‌ای نیست و تنها کافی است یک درخواست Ajax از نوع post را به آدرس کوئری‌های سرور RavenDB ارسال کنیم تا نتیجه نهایی را به شکل JSON دریافت نمائیم.
مطالب
طراحی افزونه پذیر با ASP.NET MVC 4.x/5.x - قسمت دوم
در مطلب «طراحی افزونه پذیر با ASP.NET MVC 4.x/5.x - قسمت اول» با ساختار کلی یک پروژه‌ی افزونه‌ی پذیر ASP.NET MVC آشنا شدیم. پس از راه اندازی آن و مدتی کار کردن با این نوع پروژه‌ها، این سؤال پیش خواهد آمد که ... خوب، اگر هر افزونه تصاویر یا فایل‌های CSS و JS اختصاصی خودش را بخواهد داشته باشد، چطور؟ موارد عمومی مانند بوت استرپ و جی‌کوئری را می‌توان در پروژه‌ی پایه قرار داد تا تمام افزونه‌ها به صورت یکسانی از آن‌ها استفاده کنند، اما هدف، ماژولار شدن برنامه است و جدا کردن فایل‌های ویژه‌ی هر پروژه، از پروژ‌ه‌ای دیگر و همچنین بالا بردن سهولت کار تیمی، با شکستن اجزای یک پروژه به صورت افزونه‌هایی مختلف، بین اعضای یک تیم. در این قسمت نحوه‌ی مدفون سازی انواع فایل‌های استاتیک افزونه‌ها را درون فایل‌های DLL آن‌ها بررسی خواهیم کرد. به این ترتیب دیگر نیازی به ارائه‌ی مجزای آن‌ها و یا کپی کردن آن‌ها در پوشه‌های پروژه‌ی اصلی نخواهد بود.


مدفون سازی فایل‌های CSS و JS هر افزونه درون فایل DLL آن

به solution جاری، یک class library جدید را به نام MvcPluginMasterApp.Common اضافه کنید. از آن جهت قرار دادن کلاس‌های عمومی و مشترک بین افزونه‌ها استفاده خواهیم کرد. برای مثال قصد نداریم کلاس‌های سفارشی و عمومی ذیل را هربار به صورت مستقیم در افزونه‌ای جدید کپی کنیم. کتابخانه‌ی Common، امکان استفاده‌ی مجدد از یک سری کدهای تکراری را در بین افزونه‌ها میسر می‌کند.
این پروژه برای کامپایل شدن نیاز به بسته‌ی نیوگت ذیل دارد:
 PM> install-package Microsoft.AspNet.Web.Optimization
همچنین باید به صورت دستی، در قسمت ارجاعات پروژه، ارجاعی را به اسمبلی استاندارد System.Web نیز به آن اضافه نمائید.
پس از این مقدمات، کلاس ذیل را به این پروژه‌ی class library جدید اضافه کنید:
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Web.Optimization;
 
namespace MvcPluginMasterApp.Common.WebToolkit
{
    public class EmbeddedResourceTransform : IBundleTransform
    {
        private readonly IList<string> _resourceFiles;
        private readonly string _contentType;
        private readonly Assembly _assembly;
 
        public EmbeddedResourceTransform(IList<string> resourceFiles, string contentType, Assembly assembly)
        {
            _resourceFiles = resourceFiles;
            _contentType = contentType;
            _assembly = assembly;
        }
 
        public void Process(BundleContext context, BundleResponse response)
        {
            var result = new StringBuilder();
 
            foreach (var resource in _resourceFiles)
            {
                using (var stream = _assembly.GetManifestResourceStream(resource))
                {
                    if (stream == null)
                    {
                        throw new KeyNotFoundException(string.Format("Embedded resource key: '{0}' not found in the '{1}' assembly.", resource, _assembly.FullName));
                    }
 
                    using (var reader = new StreamReader(stream))
                    {
                        result.Append(reader.ReadToEnd());
                    }
                }
            }
 
            response.ContentType = _contentType;
            response.Content = result.ToString();
        }
    }
}
اگر با سیستم bundling & minification کار کرده باشید، با تعاریفی مانند ("new Bundle("~/Plugin1/Scripts آشنا هستید. سازنده‌ی کلاس Bundle، پارامتر دومی را نیز می‌پذیرد که از نوع IBundleTransform است. با پیاده سازی اینترفیس IBundleTransform می‌توان محل ارائه‌ی فایل‌های استاتیک CSS و JS را بجای فایل سیستم متداول و پیش فرض، به منابع مدفون شده‌ی در اسمبلی جاری هدایت و تنظیم کرد.
کلاس فوق در اسمبلی معرفی شده به آن، توسط متد GetManifestResourceStream به دنبال فایل‌ها و منابع مدفون شده گشته و سپس محتوای آن‌ها را بازگشت می‌دهد.
اکنون برای استفاده‌ی از آن، به پروژه‌ی MvcPluginMasterApp.Plugin1 مراجعه کرده و ارجاعی را به پروژه‌ی MvcPluginMasterApp.Common فوق اضافه نمائید. سپس در فایل Plugin1.cs، متد RegisterBundles آن‌را به نحو ذیل تکمیل کنید:
namespace MvcPluginMasterApp.Plugin1
{
    public class Plugin1 : IPlugin
    {
        public EfBootstrapper GetEfBootstrapper()
        {
            return null;
        }
 
        public MenuItem GetMenuItem(RequestContext requestContext)
        {
            return new MenuItem
            {
                Name = "Plugin 1",
                Url = new UrlHelper(requestContext).Action("Index", "Home", new { area = "NewsArea" })
            };
        }
 
        public void RegisterBundles(BundleCollection bundles)
        {
            var executingAssembly = Assembly.GetExecutingAssembly();
            // Mostly the default namespace and assembly name are the same
            var assemblyNameSpace = executingAssembly.GetName().Name;
            var scriptsBundle = new Bundle("~/Plugin1/Scripts",
                new EmbeddedResourceTransform(new List<string>
                {
                    assemblyNameSpace + ".Scripts.test1.js"
                }, "application/javascript", executingAssembly));
            if (!HttpContext.Current.IsDebuggingEnabled)
            {
                scriptsBundle.Transforms.Add(new JsMinify());
            }
            bundles.Add(scriptsBundle);
            var cssBundle = new Bundle("~/Plugin1/Content",
                new EmbeddedResourceTransform(new List<string>
                {
                    assemblyNameSpace + ".Content.test1.css"
                }, "text/css", executingAssembly));
            if (!HttpContext.Current.IsDebuggingEnabled)
            {
                cssBundle.Transforms.Add(new CssMinify());
            }
            bundles.Add(cssBundle);
            BundleTable.EnableOptimizations = true;
        }
 
        public void RegisterRoutes(RouteCollection routes)
        {
        }
 
        public void RegisterServices(IContainer container)
        {
        }
    }
}
در اینجا نحوه‌ی کار با کلاس سفارشی EmbeddedResourceTransform را مشاهده می‌کنید. ابتدا فایل‌های js و سپس فایل‌های css برنامه به سیستم Bundling برنامه اضافه شده‌اند.
این فایل‌ها به صورت ذیل در پروژه تعریف گردیده‌اند:


همانطور که مشاهده می‌کنید، باید به خواص هر کدام مراجعه کرد و سپس Build action آن‌ها را به embedded resource تغییر داد، تا در حین کامپایل، به صورت خودکار در قسمت منابع اسمبلی ذخیره شوند.

یک نکته‌ی مهم
اینبار برای مسیردهی منابع، باید بجای / فایل سیستم، از «نقطه» استفاده کرد. زیرا منابع با نام‌هایی مانند namespace.folder.name در قسمت resources یک اسمبلی ذخیره می‌شوند:



مدفون سازی تصاویر ثابت هر افزونه درون فایل DLL آن

مجددا به اسمبلی مشترک MvcPluginMasterApp.Common مراجعه کرده و اینبار کلاس جدید ذیل را به آن اضافه کنید:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Web;
using System.Web.Routing;
 
namespace MvcPluginMasterApp.Common.WebToolkit
{
    public class EmbeddedResourceRouteHandler : IRouteHandler
    {
        private readonly Assembly _assembly;
        private readonly string _resourcePath;
        private readonly TimeSpan _cacheDuration;
 
        public EmbeddedResourceRouteHandler(Assembly assembly, string resourcePath, TimeSpan cacheDuration)
        {
            _assembly = assembly;
            _resourcePath = resourcePath;
            _cacheDuration = cacheDuration;
        }
 
        IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
        {
            return new EmbeddedResourceHttpHandler(requestContext.RouteData, _assembly, _resourcePath, _cacheDuration);
        }
    }
 
    public class EmbeddedResourceHttpHandler : IHttpHandler
    {
        private readonly RouteData _routeData;
        private readonly Assembly _assembly;
        private readonly string _resourcePath;
        private readonly TimeSpan _cacheDuration;
 
        public EmbeddedResourceHttpHandler(
            RouteData routeData, Assembly assembly, string resourcePath, TimeSpan cacheDuration)
        {
            _routeData = routeData;
            _assembly = assembly;
            _resourcePath = resourcePath;
            _cacheDuration = cacheDuration;
        }
 
        public bool IsReusable
        {
            get { return false; }
        }
 
        public void ProcessRequest(HttpContext context)
        {
            var routeDataValues = _routeData.Values;
            var fileName = routeDataValues["file"].ToString();
            var fileExtension = routeDataValues["extension"].ToString();
 
            var manifestResourceName = string.Format("{0}.{1}.{2}", _resourcePath, fileName, fileExtension);
            var stream = _assembly.GetManifestResourceStream(manifestResourceName);
            if (stream == null)
            {
                throw new KeyNotFoundException(string.Format("Embedded resource key: '{0}' not found in the '{1}' assembly.", manifestResourceName, _assembly.FullName));
            }
 
            context.Response.Clear();
            context.Response.ContentType = "application/octet-stream";
            cacheIt(context.Response, _cacheDuration);
            stream.CopyTo(context.Response.OutputStream);
        }
 
        private static void cacheIt(HttpResponse response, TimeSpan duration)
        {
            var cache = response.Cache;
 
            var maxAgeField = cache.GetType().GetField("_maxAge", BindingFlags.Instance | BindingFlags.NonPublic);
            if (maxAgeField != null) maxAgeField.SetValue(cache, duration);
 
            cache.SetCacheability(HttpCacheability.Public);
            cache.SetExpires(DateTime.Now.Add(duration));
            cache.SetMaxAge(duration);
            cache.AppendCacheExtension("must-revalidate, proxy-revalidate");
        }
    }
}
تصاویر پروژه‌ی افزونه نیز به صورت embedded resource در اسمبلی آن قرار خواهند گرفت. به همین جهت باید سیستم مسیریابی را پس درخواست رسیده‌ی جهت نمایش تصاویر، به منابع ذخیره شده‌ی در اسمبلی آن هدایت نمود. اینکار را با پیاده سازی یک IRouteHandler سفارشی، می‌توان به نحو فوق مدیریت کرد.
این IRouteHandler، نام و پسوند فایل را دریافت کرده و سپس به قسمت منابع اسمبلی رجوع، فایل مرتبط را استخراج و سپس بازگشت می‌دهد. همچنین برای کاهش سربار سیستم، امکان کش شدن منابع استاتیک نیز در آن درنظر گرفته شده‌است و هدرهای خاص caching را به صورت خودکار اضافه می‌کند.
سیستم bundling نیز هدرهای کش کردن را به صورت خودکار و توکار اضافه می‌کند.

اکنون به تعاریف Plugin1 مراجعه کنید و سپس این IRouteHandler سفارشی را به نحو ذیل به آن معرفی نمائید:
namespace MvcPluginMasterApp.Plugin1
{
    public class Plugin1 : IPlugin
    { 
        public void RegisterRoutes(RouteCollection routes)
        {
            //todo: add custom routes.
 
            var assembly = Assembly.GetExecutingAssembly();
            // Mostly the default namespace and assembly name are the same
            var nameSpace = assembly.GetName().Name;
            var resourcePath = string.Format("{0}.Images", nameSpace);
 
            routes.Insert(0,
                new Route("NewsArea/Images/{file}.{extension}",
                    new RouteValueDictionary(new { }),
                    new RouteValueDictionary(new { extension = "png|jpg" }),
                    new EmbeddedResourceRouteHandler(assembly, resourcePath, cacheDuration: TimeSpan.FromDays(30))
                ));
        } 
    }
}
در مسیریابی تعریف شده، تمام درخواست‌های رسیده‌ی به مسیر NewsArea/Images به EmbeddedResourceRouteHandler هدایت می‌شوند.
مطابق تعریف آن، file و extension به صورت خودکار جدا شده و توسط routeData.Values در متد ProcessRequest کلاس EmbeddedResourceHttpHandler قابل دسترسی خواهند شد.
پسوندهایی که توسط آن بررسی می‌شوند از نوع png یا jpg تعریف شده‌اند. همچنین مدت زمان کش کردن هر منبع استاتیک تصویری به یک ماه تنظیم شده‌است.


استفاده‌ی نهایی از تنظیمات فوق در یک View افزونه

پس از اینکه تصاویر و فایل‌های css و js را به صورت embedded resource تعریف کردیم و همچنین تنظیمات مسیریابی و bundling خاص آن‌ها را نیز مشخص نمودیم، اکنون نوبت به استفاده‌ی از آن‌ها در یک View است:
@{
    ViewBag.Title = "From Plugin 1";
}
@Styles.Render("~/Plugin1/Content")
 
<h2>@ViewBag.Message</h2>
 
<div class="row">
    Embedded image:
    <img src="@Url.Content("~/NewsArea/Images/chart.png")" alt="clock" />
</div>
 
@section scripts
{
    @Scripts.Render("~/Plugin1/Scripts")
}
در اینجا نحوه‌ی تعریف فایل‌های CSS و JS ارائه شده‌ی توسط سیستم Bundling را مشاهده می‌کنید.
همچنین مسیر تصویر مشخص شده‌ی در آن، اینبار یک NewsArea اضافه‌تر دارد. فایل اصلی تصویر، در مسیر Images/chart.png قرار گرفته‌است اما می‌خواهیم این درخواست‌ها را به مسیریابی جدید {NewsArea/Images/{file}.{extension هدایت کنیم. بنابراین نیاز است به این نکته نیز دقت داشت.

اینبار اگر برنامه را اجرا کنیم، می‌توان به سه نکته در آن دقت داشت:


الف) alert اجرا شده از فایل js مدفون شده خوانده شده‌است.
ب) رنگ قرمز متن (تگ h2) از فایل css مدفون شده، گرفته شده‌است.
ج) تصویر نمایش داده شده، همان تصویر مدفون شده‌ی در فایل DLL برنامه است.
و هیچکدام از این فایل‌ها، به پوشه‌های پروژه‌ی اصلی برنامه، کپی نشده‌اند.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید:
 MvcPluginMasterApp-Part2.zip
مطالب
آشنایی با CLR: قسمت هفتم
کدهای IL و تایید آن ها

ساختار استکی
IL از ساختار استک استفاده می‌کند. به این معنی که تمامی دستور العمل‌ها داخل آن push شده و نتیجه‌ی اجرای آن‌ها pop می‌شوند. از آنجا که IL به طور مستقیم ارتباطی با ثبات‌ها ندارد، ایجاد زبانهای برنامه نویسی جدید بر اساس CLR بسیار راحت‌تر هست و عمل کامپایل، تبدیل کردن به کدهای IL می‌باشد.

بدون نوع بودن(Typeless)
از دیگر مزیت‌های آن این است که کدهای IL بدون نوع هستند. به این معنی که موقع افزودن دستورالعملی به داخل استک، دو عملگر وارد می‌شوند و هیچ جداسازی در رابطه با سیستم‌های 32 یا 64 بیت صورت نمی‌گیرد و موقع اجرای برنامه است که تصمیم می‌گیرد از چه عملگرهایی باید استفاده شود.

Virtual Address Space
بزرگترین مزیت این سیستم‌ها امنیت و مقاومت آن هاست. موقعی که تبدیل کد IL به سمت کد بومی صورت می‌گیرد، CLR فرآیندی را با نام verification یا تاییدیه، اجرا می‌کند. این فرآیند تمامی کدهای IL را بررسی می‌کند تا از امنیت کدها اطمینان کسب کند. برای مثال بررسی می‌کند که هر متدی صدا زده می‌شود با تعدادی پارامترهای صحیح صدا زده شود و به هر پارامتر آن نوع صحیحش پاس شود و مقدار بازگشتی هر متد به درستی استفاده شود. متادیتا شامل اطلاعات تمامی پیاده سازی‌ها و متدها و نوع هاست که در انجام تاییدیه مورد استفاده قرار می‌گیرد.
در ویندوز هر پروسه، یک آدرس مجازی در حافظه دارد و این جدا سازی حافظه و ایجاد یک حافظه مجازی کاری لازم اجراست. شما نمی‌توانید به کد یک برنامه اعتماد داشته باشید که از حد خود تخطی نخواهد کرد و فرآیند برنامه‌ی دیگر را مختل نخواهد کرد. با خواندن و نوشتن در یک آدرس نامعتبر حافظه، ما این اطمینان را کسب می‌کنیم که هیچ گاه تخطی در حافظه صورت نمی‌گیرد.
قبلا به طور مفصل در این مورد ذخیره سازی در حافظه صحبت کرده ایم.

Hosting
از آنجا که پروسه‌های ویندوزی به مقدار زیادی از منابع سیستم عامل نیاز دارند که باعث کاهش منابع و محدودیت در آن می‌شوند و نهایت کارآیی سیستم را پایین می‌آورد، ولی با کاهش تعدادی برنامه‌های در حال اجرا به یک پروسه‌ی واحد می‌توان کارآیی سیستم را بهبود بخشید و منابع کمتری مورد استفاده قرار می‌گیرند که این یکی دیگر از مزایای کدهای managed نسبت به unmanaged است. CLR در حقیقت این قابلیت را به شما می‌دهد تا چند برنامه‌ی مدیریت شده را در قالب یک پروسه به اجرا درآورید. هر برنامه‌ی مدیریت شده به طور پیش فرض بر روی یک appDomain اجرا می‌گردد و هر فایل EXE روی حافظه‌ی مجازی مختص خودش اجرا می‌شود. هر چند پروسه‌هایی از قبیل IIS و SQL Server که پروسه‌های CLR را پشتیبانی یا هاست می‌کنند می‌توانند تصمیم بگیرند که آیا appDomain‌ها را در یک پروسه‌ی واحد اجرا کنند یا خیر که در مقاله‌های آتی آن را بررسی می‌کنیم.

کد ناامن یا غیر ایمن UnSafe Code
به طور پیش فرض سی شارپ کدهای ایمنی را تولید می‌کند، ولی این اجازه را می‌دهد که اگر برنامه نویس بخواهد  کدهای ناامن بزند، قادر به انجام آن باشد. این کدهای ناامن دسترسی مستقیم به خانه‌های حافظه و دستکاری بایت هاست. این مورد قابلیت قدرتمندی است که به توسعه دهنده اجازه می‌دهد که با کدهای مدیریت نشده ارتباط برقرار کند یا یک الگوریتم با اهمیت زمانی بالا را جهت بهبود کارآیی، اجرا کند.
 هر چند یک کد ناامن سبب ریسک بزرگی می‌شود و می‌تواند وضعیت بسیاری از ساختارهای ذخیره شده در حافظه را به هم بزند و امنیت برنامه را تا حد زیادی کاهش دهد.  به همین دلیل سی شارپ نیاز دارد تا تمامی متدهایی که شامل کد unsafe هستند را با کلمه کلیدی unsafe علامت گذاری کند. همچنین کمپایلر سی شارپ نیاز دارد تا شما این کدها را با سوئیچ unsafe/ کامپایل کنید.

موقعیکه جیت تلاش دارد تا یک کد ناامن را کامپایل کند، اسمبلی را بررسی می‌کند که آیا این متد اجازه و تاییدیه آن را دارد یا خیر. آیا  System.Security.Permissions.SecurityPermission با فلگ SkipVerification مقدار دهی شده است یا خیر. اگر پاسخ مثبت بود JIT آن‌ها را کامپایل کرده و اجازه‌ی اجرای آن‌ها را می‌دهد. CLR به این کد اعتماد می‌کند و امیدوار است که آدرس دهی مستقیم و دستکاری بایت‌های حافظه موجب آسیبی نگردد. ولی اگر پاسخ منفی بود، یک استثناء از نوع System.InvalidProgramException یا System.Security.VerificationException را ایجاد می‌کند تا از اجرای این متد جلوگیری به عمل آید. در واقع کل برنامه خاتمه میابد ولی آسیبی به حافظه نمی‌زند.

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

در این زمینه مایکروسافت ابزار سودمندی را با نام PEVerify .exe را معرفی کرده است که به بررسی تمامی متدهای یک اسمبلی پرداخته و در صورت وجود کد ناامن به شما اطلاع میدهد. بهتر است از این موضوع اطلاع داشته باشید که این ابزار نیاز دارد تا به متادیتاهای یک اسمبلی نیاز داشته باشید. باید این ابزار بتواند به تمامی ارجاعات آن دسترسی داشته باشد که در مورد عملیات بایندینگ در آینده بیشتر صحبت می‌کنیم.

IL و حقوق حق تالیف آن
بسیاری از توسعه دهندگان از اینکه IL هیچ شرایطی برای حفظ حق تالیف آن‌ها ایجاد نکرده است، ناراحت هستند. چرا که ابزارهای زیادی هستند که با انجام عملیات مهندسی معکوس می‌توانند به الگوریتم آنان دست پیدا کنند و میدانید که IL خیلی سطح پایین نیست و برگرداندن آن به شکل یک کد، کار راحت‌تری هست و بعضی ابزارها کدهای خوبی هم ارائه می‌کنند. از دست این ابزارها می‌توان به ILDisassembler و  JustDecompile اشاره کرد.
اگر علاقمند هستید این عیب را برطرف کنید، می‌توانید از ابزارهای ثالث که به ابزارهای obfuscator (یک نمونه سورس باز ) معروف هستند استفاده کنید تا با کمی پیچیدگی در متادیتاها، این مشکل را تا حدی برطرف کنند. ولی این ابزارها خیلی کامل نیستند، چرا که نباید به کامپایل کردن کار لطمه بزنند. پس اگر باز خیلی نگران این مورد هستید می‌توانید الگوریتم‌های حساس و اساسی خود را در قالب unmanaged code ارائه کنید که در بالا اشاراتی به آن کرده‌ایم.
برنامه‌های تحت وب به دلیل عدم دسترسی دیگران از امنیت کاملتری برخوردار هستند.
نظرات مطالب
Functional Programming یا برنامه نویسی تابعی - قسمت اول
آیا میتونید پروژه بزرگی نام ببرید با کاربردهای معمول، که توش صرفا از روش فانکشنال استفاده شده باشه؟ آیا در گیت هاب میشه نمونه هایی پیدا کرد؟ مثلا CMS مطرحی داریم که کاملا بر اساس برنامه نویسی فانکشنال باشه؟ یعنی میخوام بگم چقدر اینکه کسی دغدغه است این باشه که باید شی گرا یاد بگیره یا فانکشنال معنی داره؟ از نظر من دست کم نو آموز‌ها نباید چنین دغدغه ای داشته باشن!

آیا بسیاری از قوانین برنامه نویسی فانکشنال، مواردی نیست که بهتره در برنامه نویسی شی گرا هم تا حد امکان مد نظر داشته باشیم؟ مثلا شفافیت و صداقت توابع، مواردی نیست که به نظر من تناقضی با شی گرایی داشته باشه! پس تا حد امکان اگه در شی گرایی هم مد نظر باشه میتونه به کیفیت بالاتر منتج بشه. اینطور فکر نمیکنید؟

نظرات مطالب
4# آموزش سیستم مدیریت کد Git : نصب و پیکر‌‏بندی
اگر شما به سایت‌های مدیریت کدی نظیر github مراجعه کنید و تعداد کاربران و یا پروژه‌های قرار گرفته بر روی آن‌ها را در نظر بگیرید متوجه محبوبیت سیستم مدیریت کد git خواهید شد در مورد تفاوت‌های سیستم‌های CVS و DVCS در مقاله اول توضیحاتی داده شد و در مقاله بعد درباره نحوه ذخیره سازی اطلاعات که باعث افزایش سرعت چشمگیر در عملیات check-in و check-out می‌شود
در ضمن در git و در همه سیستم‌های مدیریت کد امکان دستیابی به کد‌های قبل وجود دارد و به طور کلی این یکی از اهداف سیستم‌های مدریت کد است.
خود من هم یک برنامه نویس دات نت هستم اما دلیلی ندارد که مجبور باشیم هر آنچه که مایکروسافت ساخته را استفاده کنیم
من با هر دو سیستم TFS و Git کار کردم و به شخصه استفاده و راه اندازی آن را از TFS ساده‌تر می‌بینم چون تنها یکی از کاربردهای TFS مدیریت کد است بنابراین شما به طور نسبی با سیستم پیچیده‌تری سرو کار خواهید داشت.
اما در نهایت نیاز شما به  معماری مورد استفاده در مدیریت کد‌های خود تعیین کننده است
اگر یک سیستم مدیریت کد توزیع شده لازم دارید بهترین انتخاب git است
موفق باشید
نظرات مطالب
آشنایی با Refactoring - قسمت 4
Attached Properties در WPF در حقیقت یک نوع تزریق شیء به شیء مورد است. شما خواص شیءایی را به شیء دیگر تزریق می‌کنید. مثلا یک دکمه دارید، سپس Canvas.Top یا Grid.Row به آن متصل می‌کنید، علت هم این است که اگر قرار بود از روز اول برای یک دکمه تمام این خواص را تعریف کنند، باید بی‌نهایت خاصیت تعریف می‌کردند؛ چون WPF قابل توسعه است و می‌شود layout panel سفارشی هم طراحی کرد. البته این تازه یک مورد از کاربردهای این مبحث است.
به صورت خلاصه، Attached Properties ، کپسوله سازی شیء جاری را زیر سؤال نمی‌برد. (موضوع اصلی بحث جاری)
هر چند با استفاده از Attached Properties می‌توان به تمام خواص و کلیه رویدادهای شیء تزریق شده به آن هم دسترسی پیدا کرد. اینجا هم باز هم برخلاف بحث جاری ما نیست؛ چون اساسا این شیء الحاقی یا ضمیمه شده، نهایتا با شیء جاری از دید سیستم یکپارچه به نظر می‌رسد. این تزریق هم به همین دلیل صورت گرفته. بنابراین در اینجا هم دسترسی یا تغییر خواص شیء ضمیمه شده، خلاف مقررات شیء‌گرایی و کپسوله سازی نیست. چون ما در اساس داریم راجع به مثلا یک دکمه صحبت می‌کنیم. اگر خاصیتی هم به آن تزریق شده باز هم نهایتا جزو خواص همان دکمه در نظر گرفته می‌شود.
مطالب
NoSQL و مایکروسافت
روشی را که مایکروسافت برای پرداختن به مقوله NoSQL تاکنون انتخاب کرده است، قرار دادن ویژگی‌هایی خاصی از دنیای NoSQL مانند امکان تعریف اسکیمای متغیر، داخل مهم‌ترین بانک اطلاعاتی رابطه‌ای آن، یعنی SQL Server است، که در ادامه به آن خواهیم پرداخت. همچنین در سمت محصولات پردازش ابری آن نیز امکان دسترسی به محصولات NoSQL کاملی وجود دارد.

1) Azure table storage
Azure table storage در حقیقت یک Key-value store ابری است و برای کار با آن از اینترفیس پروتکل استاندارد OData استفاده می‌شود. علت استفاده و طراحی یک سیستم Key-value store در اینجا، مناسب بودن اینگونه سیستم‌ها جهت مقاصد عمومی است و به این ترتیب می‌توان به بازه بیشتری از مصرف کنندگان، خدمات ارائه داد.
پیش از ارائه Azure table storage، مایکروسافت سرویس خاصی را به نام SQL Server Data Services که به آن SQL Azure نیز گفته می‌شود، معرفی کرد. این سرویس نیز یک Key-Value store است؛ هرچند از SQL Server به عنوان مخزن نگهداری اطلاعات آن استفاده می‌کند.


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


3) SQL Azure Federations
در اینجا منظور از Federations در حقیقت همان پیاده سازی قابلیت Sharding بانک‌های اطلاعاتی NoSQL توسط SQL Azure است که برای توزیع اطلاعات بر روی سرورهای مختلف طراحی شده است. به این ترتیب دو قابلیت Partitioning و همچنین Replication به صورت خودکار در دسترس خواهند بود. هر Partition در اینجا، یک SQL Azure کامل است. بنابراین چندین بانک اطلاعاتی فیزیکی، یک بانک اطلاعاتی کلی را تشکیل خواهند داد.
هرچند در اینجا Sharding  (که به آن Federation member گفته می‌شود) و در پی آن مفهوم «عاقبت یک دست شدن اطلاعات» وجود دارد، اما درون یک Shard یا یک Federation member، مفهوم ACID پیاده سازی شده است. از این جهت که هر Shard واقعا یک بانک اطلاعاتی رابطه‌ای است. اینجا است که مفهوم برنامه‌های  Multi-tenancy را برای درک آن باید درنظر داشت. برای نمونه یک برنامه وب را درنظر بگیرید که قسمت اصلی اطلاعات کاربران آن بر روی یک Shard قرار دارد و سایر اطلاعات بر روی سایر Shards پراکنده شده‌اند. در این حالت است که یک برنامه وب با وجود مفهوم ACID در یک Shard می‌تواند سریع پاسخ دهد که آیا کاربری پیشتر در سایت ثبت نام کرده است یا خیر و از ثبت نام‌های غیرمجاز جلوگیری به عمل آورد.
در اینجا تنها موردی که پشتیبانی نشده‌است، کوئری‌های Fan-out می‌باشد که پیشتر در مورد آن بحث شد. از این جهت که با نحوه خاصی که Sharding آن طراحی شده است، نیازی به تهیه کوئری‌هایی که به صورت موازی بر روی کلیه Shards برای جمع آوری اطلاعات اجرا می‌شوند، نیست. هر چند از هر shard با استفاده از برنامه‌های دات نت، می‌توان به صورت جداگانه نیز کوئری گرفت.


4) OData
اگر به CouchDB و امکان دسترسی به امکانات آن از طریق وب دقت کنید، در محصولات مایکروسافت نیز این دسترسی REST API پیاده سازی شده‌اند.
OData یک RESTful API است برای دسترسی به اطلاعاتی که به شکل XML یا JSON بازگشت داده می‌شوند. انواع و اقسام کلاینت‌هایی برای کار با آن از جاوا اسکریپت گرفته تا سیستم‌های موبایل، دات نت و جاوا، وجود دارند. از این API نه فقط برای خواندن اطلاعات، بلکه برای ثبت و به روز رسانی داده‌ها نیز استفاده می‌شود. در سیستم‌های جاری مایکروسافت، بسیاری از فناوری‌ها، اطلاعات خود را به صورت OData دراختیار مصرف کنندگان قرار می‌دهند مانند Azure table storage، کار با SQL Azure از طریق WCF Data Services (جایی که OData از آن نشات گرفته شده)، Azure Data Market (برای ارائه فیدهایی از اطلاعات خصوصا رایگان)، ابزارهای گزارشگیری مانند SQL Server reporting services، لیست‌های شیرپوینت و غیره.
به این ترتیب به بسیاری از قابلیت‌های دنیای NoSQL مانند کار با اطلاعات JSON بدون ترک دنیای رابطه‌ای می‌توان دسترسی داشت.


5) امکان اجرای MongoDB و امثال آن روی سکوی کاری Azure
امکان توزیع MongoDB بر روی یک Worker role سکوی کاری Azure وجود دارد. در این حالت بانک‌های اطلاعاتی این سیستم‌ها بر روی Azure Blob Storage قرار می‌گیرند که به آن‌ها Azure drive نیز گفته می‌شود. همین روش برای سایر بانک‌های اطلاعاتی NoSQL نیز قابل اجرا است.
به علاوه امکان اجرای Hadoop نیز بر روی Azure وجود دارد. مایکروسافت به کمک شرکتی به نام HortonWorks نسخه ویندوزی Hadoop را توسعه داده‌اند. HortonWorks را افرادی تشکیل داده‌اند که پیشتر در شرکت یاهو بر روی پروژه Hadoop کار می‌کرده‌اند.


6) قابلیت‌های فرا رابطه‌ای SQL Server
الف) فیلدهای XML (که در ابتدای این مطلب به آن پرداخته شد). به این ترتیب می‌توان به یک اسکیمای انعطاف پذیر، بدون از دست دادن ضمانت ACID رسید.
ب) فیلد HierarchyId برای ذخیره سازی اطلاعات چند سطحی. برای مثال در بانک‌های اطلاعاتی NoSQL سندگرا، یک سند می‌تواند سند دیگری را در خود ذخیره کند و الی آخر.
ج) Sparse columns؛ ستون‌های اسپارس تقریبا شبیه به Key-value stores عمل می‌کنند و یا حتی Wide column stores نیز با آن قابل مقایسه است. در اینجا هنوز اسکیما وجود دارد، اما برای نمونه علت استفاده از Wide column stores این نیست که واقعا نمی‌دانید ساختار داده‌های مورد استفاده چیست، بلکه در این حالت می‌دانیم که در هر ردیف تنها از تعداد معدودی از فیلدها استفاده خواهیم کرد. به همین جهت در هر ردیف تمام فیلدها قرار نمی‌گیرند، چون در اینصورت تعدادی از آن‌ها همواره خالی باقی می‌ماندند. مایکروسافت این مشکل را با ستون‌های اسپارس حل کرده است؛ در اینجا هر چند ساختار کلی مشخص است، اما مواردی که هر بار استفاده می‌شوند، تعداد محدودی می‌باشند. به این صورت SQL Server تنها برای ستون‌های دارای مقدار، فضایی را اختصاص می‌دهد. به این ترتیب از لحاظ فیزیکی و ذخیره سازی نهایی، به همان مزیت Wide column stores خواهیم رسید.
د) FileStreams در اس کیوال سرور بسیار شبیه به پیوست‌های سندهای بانک‌های اطلاعاتی NoSQL سندگرا هستند. در اینجا نیز اطلاعات در فایل سیستم ذخیره می‌شوند اما ارجاعی به آن‌ها در جداول مرتبط وجود خواهند داشت.


7) SQL Server Parallel Data Warehouse Edition
SQL PDW، نگارش خاصی از SQL Server است که در آن یک شبکه از SQL Serverها به صورت یک وهله منطقی SQL Server در اختیار برنامه نویس‌ها قرار می‌گیرد.
این نگارش، از فناوری خاصی به نام MPP یا massively parallel processing برای پردازش کوئری‌ها استفاده می‌کند. در اینجا همانند بانک‌های اطلاعاتی NoSQL، یک کوئری به نود اصلی ارسال شده و به صورت موازی بر روی تمام نودها پردازش گردیده (همان مفهوم Map Reduce که پیشتر در مورد آن بحث شد) و نتیجه در اختیار مصرف کننده قرار خواهد گرفت. نکته مهم آن نیز در عدم نیاز به نوشتن کدی جهت رخ دادن این عملیات از طرف برنامه نویس‌ها است و موتور پردازشی آن جزئی از سیستم اصلی است. تنها کافی است یک کوئری SQL صادر گردد تا نتیجه نهایی از تمام سرورها جمع آوری و بازگردانده شود.
این نگارش ویژه تنها به صورت یک Appliance به فروش می‌رسد (به صورت سخت افزار و نرم افزار باهم) که در آن CPU‌ها، فضاهای ذخیره سازی اطلاعات و جزئیات شبکه به دقت از پیش تنظیم شده‌اند.