مطالب
CSS پویا در ASP.NET MVC
سناریو هایی وجود دارد که نیاز است مشتری ، خود شیوه نامه هایی (CSS) را برای قسمت‌های مختلف سایت انتخاب کند. برای مثال تنظیماتی را برای منوی سایت در نظر گرفته ایم که مشتری بتواند رنگ و قلم و ... را متناسب با سلیقه‌ی خود تغییر دهد و یا یک قسمت کلی برای اعمال شیوه نامه‌ها به سایت ایجاد کرده ایم که در همه‌ی قسمت‌های سایت اعمال شود. بدین شکل در صورتی که مشتری، اطلاعات اندکی هم در مورد CSS داشته باشد میتواند ظاهر سایت خود را به آسانی تغییر دهد و تا حدودی بار را از روی دوش پشتیبان سایت بر میدارد.
و برای همه‌ی این‌ها نیاز است تا فیلدی در دیتابیس برای ذخیره‌ی شیوه نامه‌های مشتری ایجاد شود و یا در یک فایل متنی ذخیره شود که بسته به سیاست برنامه نویس دارد.
در این مطلب تصمیم داریم این سناریو را به صورت ساده در یک پروژه ASP.NET MVC پیاده سازی کنیم.
ایتدا یک پروژه از نوع ASP.NET MVC 4 ایجاد میکنیم. سپس قطعه کد زیر را به فایل  Layout.cshtml_ موجود در مسیر Views/Shared به صورت زیر اضافه میکنیم :
<head>
    @*سایر شیوه نامه‌ها و اسکریپت ها*@
    @RenderSection("styles", required: false)
</head>
RenderSection این امکان را میدهد که ما بتوانیم شیوه نامه‌ی دیگری را در صفحات دیگر به MasterPage خود تزریق کنیم.
سپس کنترلری به نام Home ایجاد میکنیم :
namespace DynamicCssExample.Controllers
{
    public class HomeController : Controller
    {

        public ActionResult Index()
        {
            return View();
        }

        public string GetStyle()
        {
            Response.ContentType = "text/css";
            //در این قسمت میتوانیم به دیتابیس متصل شویم و شیوه نامه‌ی مورد نظر را واکشی کنیم و بازگشت دهیم
            return "h2{color:yellow}";
        }

    }
}
در اینجا یک متد به نام GetStyle داریم که وظیفه‌ی بازگشت یک رشته را بر عهده دارد و نوع این مقدار بازگشتی از نوع text/css است و میتواند شیوه نامه هایی که در دیتابیس و یا یک فایل متنی ذخیره کرده ایم را واکشی و به استریم ارسال کند.
در انتها برای استفاده از این متد کافی است در View مربوطه بدین شکل عمل کنیم :
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

@section styles
{
    <link rel="stylesheet" href="@Url.Action("GetStyle", "Home")" type="text/css"/>
}

<h2>Index</h2>
در سکشن Style لینک شیوه نامه ای تعریف کرده ایم که ویژگی href آن به اکشن GetStyle در کنترلر Home اشاره میکند و این اکشن نیز محتوای شیوه نامه را برگشت میدهد.
مطالب
تخته وایت برد آنلاین توسط SignalR
همانطور که در دوره SignalR سایت نیز مطرح شده‌است : "یکی از کاربردهای جالب SignalR می‌تواند به روز رسانی مداوم صفحه نمایش کاربران، توسط اطلاعات ارسالی از طرف سرور باشد." در ادامه میخواهیم به طراحی یک "تخته وایت برد" آنلاین بپردازیم.
در این پروژه برای ترسم خطوط بر روی صفحه از Canvas در  HTML5 استفاده میشود.

پیشنیازها :
پیشنیازهای این مطلب با مطلب «مثال - نمایش درصد پیشرفت عملیات توسط SignalR» یکی است. برای مثال، نحوه دریافت وابستگی‌ها، تنظیمات فایل global.asax و افزودن اسکریپت‌ها، تفاوتی با مقاله‌ی ذکر شده ندارد و تنها تعدادی اسکریپت و CSS جهت زیبایی کار افزوده شده است :
<link href="Styles/bootstrap.min.css" rel="stylesheet" />

//برای نمایش و استفاده از جعبه‌ی رنگ در بوت استراپ
<link href="Styles/bootstrap-colorpalette.css" rel="stylesheet" />
<script src="Scripts/jquery-1.8.2.js"></script>
<script type="text/javascript" src='Scripts/jquery.signalR-1.1.3.js'></script>
<script src="Scripts/bootstrap.min.js"></script>
<script src="Scripts/bootstrap-colorpalette.js"></script>
<script type="text/javascript" src='<%: ResolveClientUrl("~/signalr/hubs") %>'></script>

//حاوی متدهایی برای رسم خط با استفاده از HTML5
<script src="Scripts/draw.js" type="text/javascript"></script>

//تعریف کلاینت‌های متصل به هاب
<script src="Scripts/Whiteboard.js"></script>

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

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using OnlineSignalRWhiteboard.Model;

namespace OnlineSignalRWhiteboard.Hubs
{
    [HubName("onWhiteboard")]
    public class Whiteboard : Hub
    {
        public void OnDrawPen(Point prev, Point current, string color, int width)
        {
            Clients.All.drawPen(prev, current, color, width);
        }

        public void ClearBoard()
        {
            Clients.All.clear();
        }

    }
}
در ابتدا توسط ویژگی HubName یک نام مشخص برای هاب خود انتخاب کرده ایم. سپس متدی به نام OnDrawPen تعریف شده است که پس از فراخوانی از سمت کلاینت، مقادیر دریافتی خود را با صدا زدن متدی در سمت کلاینت به نام drawPen  ، به تمام کلاینت‌ها ارسال و نقاط مربوطه در سمت کلاینت، بر روی صفحه رسم میشوند.
متد دیگری به نام ClearBoard نیز تعریف شده است که روال رخدادگردان clear در سمت کلاینت را فراخوانی میکند و باعث پاک شدن صفحه نمایش میشود.

کدهای کلاینت‌های متصل به هاب در فایل Whiteboard.js 
 :
$(function () {
    $.connection.hub.logging = true;
    var whiteboard = $.connection.onWhiteboard;

    $("#clear").click(function () {
        //پاک کردن صفحه نمایش
        whiteboard.server.clearBoard();
    });

    var color = function (colors) {
        //تغییر رنگ قلم
        draw.colour = colors;
    };

    $(".size").click(function () {
        //تغییر سایز قلم
        draw.lineWidth = $(this).height();
        $('#size').css('height', $(this).height());
    });

    $.connection.hub.start().done(function () {
    })
    .fail(function () {
        alert("Could not Connect!");
     });

    draw.onDraw = function (prev, current, color, width) {
        //ارسال پارامترها به سمت سرور تا سرور بتواند داده‌ها را به سمت سایر کلاینت‌ها نیز ارسال کند
        whiteboard.server.onDrawPen(prev, current, color, width);
    };

    whiteboard.client.drawPen = function (prev, current, color, width) {
        draw.drawPen(prev, current, color, width);
    };

    whiteboard.client.clear = function () {
        draw.clear();
    };

    $('#colorpalette3').colorPalette()
      .on('selectColor', function (e) {
          $('#color').css('background-color', e.color);
          color(e.color);
      });

    var canvas = document.getElementById('draw');
    //تغییر سایر کانواس متناسب با پنجره‌ی مرورگر
    window.addEventListener('resize', resizeCanvas, false);

    function resizeCanvas() {
        canvas.width = window.innerWidth - 20;
        canvas.height = window.innerHeight - 70;
    }
    resizeCanvas();

});
در این قطعه کد ابتدا ارجاعی به هاب مشخص شده است و سپس روال‌های رخداد گردانی مانند whiteboard.client.drawPen و whiteboard.client.clear تعریف شده اند که به ترتیب متصل هستند به فراخوانی های Clients.All.drawPen و  Clients.All.clear در سمت سرور.
همچنین یک متد به نام draw.onDraw تعریف شده است که وظیفه‌ی آن ارسال اطاعاتی نظیر موقعیت موس در صفحه، رنگ، اندازه و ...  به سمت متدی به نام onDrawPen در هاب است.

کدهای کامل سمت کلاینت :
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <link href="Styles/bootstrap.min.css" rel="stylesheet" />
    <link href="Styles/bootstrap-colorpalette.css" rel="stylesheet" />
    <script src="Scripts/jquery-1.8.2.js"></script>
    <script type="text/javascript" src='Scripts/jquery.signalR-1.1.3.js'></script>
    <script src="Scripts/bootstrap.min.js"></script>
    <script src="Scripts/bootstrap-colorpalette.js"></script>
    <script type="text/javascript" src='<%: ResolveClientUrl("~/signalr/hubs") %>'></script>
    <script src="Scripts/draw.js" type="text/javascript"></script>
    <script src="Scripts/Whiteboard.js"></script>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <div style="position: static;">
              <div>
                <div>
                  <a data-toggle="collapse" data-target=".navbar-inverse-collapse">
                    <span></span>
                    <span></span>
                    <span></span>
                  </a>
                  <a href="#">تخته وایت برد آنلاین توسط SignalR و HTML5</a>
                  <div>
                    <ul>
                      <li>
                          <div>
                              <a id="selected-color2" data-toggle="dropdown">
                                  <div style="width: 20px; height: 20px; background: black" id="color">
                                  </div>
                              </a>
                              <ul style="width:293px;">
                                <li style="display:inline-block;">
                                  <div>رنگ پس زمینه</div>
                                  <div id="colorpalette3"></div>
                                </li>
                              </ul>
                          </div>
                      </li>
                      <li></li>
                      <li>
                          <div>
                              <a data-toggle="dropdown" style="width: 120px; height: 20px" href="#">
                                <hr style="width: 120px; height: 1px; background-color: black;margin: 0px; display: table-cell" id="size">
                              </a>
                              <ul style="width: 120px">
                                <li><hr style="width: 140px; height: 1px; background-color: black"></li>
                                  <li></li>
                                <li><hr style="width: 140px; height: 3px; background-color: black"></li>
                                  <li></li>
                                <li><hr style="width: 140px; height: 7px; background-color: black"></li>
                                  <li></li>
                                <li><hr style="width: 140px; height: 10px; background-color: black"></li>
                                  <li></li>
                                <li><hr style="width: 140px; height: 20px; background-color: black"></li>
                              </ul>
                            </div>
                      </li>
                      <li></li>
                      <li>
                          <div>
                            <a href="#" id="clear"><i></i>جدید</a>
                          </div>
                      </li>
                    </ul>
                  </div><!-- /.nav-collapse -->
                </div>
              </div><!-- /navbar-inner -->
            </div>
        </div>
        
        <div>
            <div>
                 <canvas id="draw" style="cursor: crosshair;border: 1px solid black;"></canvas>
            </div>
       </div>
    </form>
</body>
</html>
در ابتدا یک دکمه برای تغییر رنگ قلم و یک لیست بازشو برای تعیین اندازه‌ی قلم و همچنین یک دکمه برای پاک کردن صفحه نمایش قرار داده شده است. سپس یک Canvas به نام draw ایجاد کرده ایم که بتوانیم خطوط خود را بر روی آن رسم کنیم.

مشاهده نسخه نمایشی 
کدهای کامل این مثال را میتوانید از اینجا دانلود کنید : OnlineSignalRWhiteboard.zip  
و همچنین میتوانید نمونه‌ی بهینه شده‌ی آن را نیز از این قسمت دانلود کنید :  OnlineCustomSignalRWhiteboard.zip  
مطالب
پیاده سازی پروژه نقاشی (Paint) به صورت شی گرا 1#
قصد داریم در طی چند پست متوالی، یک پروژه Paint را به صورت شی گرا پیاده سازی کنیم. خوب، پروژه ای که می‌خواهیم پیاده سازی کنیم باید دارای این امکانات باشه که مرحله به مرحله پیش میریم و پروزه کامل در نهایت در قسمت پروژه‌ها ی همین سایت قرار خواهد گرفت.

  1. قابلیت ترسیم اشیا روی بوم گرافیکی دلخواه
  2. قابلیت جابجایی اشیا
  3. قابلیت تغییر رنگ اشیا
  4. ترسیم اشیا توپر و تو خالی
  5. تعیین پهنای خط شی ترسیم شده
  6. تعیین رنگ پس زمینه در صورت تو پر بودن شی
  7. قابلیت پیش نمایش رسم شکل در زمان ترسیم اشیا
  8. توانایی انتخاب اشیا
  9. تعیین عمق شی روی بوم گرافیکی مورد نظر
  10. ترسیم اشیایی مانند خط، دایره، بیضی، مربع، مستطیل، لوزی، مثلث 
  11. قابلیت تغییر اندازه اشیا ترسیم شده

خوب برای شروع ابتدا یک پروژه از نوع Windows Application ایجاد می‌کنیم (البته برای این قسمت می‌توانیم یک پروژه Class Library ایجاد کنیم)

سپس یک پوشه به نام Models به پروزه اضافه نمایید.

خوب در این پروژه یک کلاس پایه به نام Shape در نظر می‌گیریم.

همه اشیا ما دارای نقطه شروع، نقطه پایان، رنگ قلم، حالت انتخاب، رنگ پس زمینه، نوع شی، .... می‌باشند که بعضی از خصوصیات را توسط خصوصیات دیگر محاسبه می‌کنیم. مثلا خاصیت Width و Height و X و Y توسط خصوصیات نقطه شروع و پایان می‌توانند محاسبه شوند.

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

با توجه به تصویر بالا (البته این تجزیه تحلیل شخصی من بوده و دوستان به سلیقه خود ممکن است این ساختار را تغییر دهند)

نوع شمارشی ShapeType: در این نوع شمارشی انواع شی‌های موجود در پروژه معرفی شده است

محتوای این نوع به صورت زیر تعریف شده است:

namespace PWS.ObjectOrientedPaint.Models
{
    /// <summary>
    /// Shape Type in Paint
    /// </summary>
    public enum ShapeType
    {
        /// <summary>
        /// هیچ
        /// </summary>
        None = 0,
        /// <summary>
        /// خط
        /// </summary>
        Line = 1,
        /// <summary>
        /// مربع
        /// </summary>
        Square = 2,
        /// <summary>
        /// مستطیل
        /// </summary>
        Rectangle = 3,
        /// <summary>
        /// بیضی
        /// </summary>
        Ellipse = 4,
        /// <summary>
        /// دایره
        /// </summary>
        Circle = 5,
        /// <summary>
        /// لوزی
        /// </summary>
        Diamond = 6,
        /// <summary>
        /// مثلث
        /// </summary>
        Triangle = 7,
    }
}
انشاالله در پست‌های بعدی ما بقی کلاس‌ها به مرور پیاده سازی خواهند شد.

مطالب
آزمایش Web APIs توسط Postman - قسمت هفتم - استفاده از خروجی OpenAPI Swagger در Postman
در سری «OpenAPI Swagger» با نحوه‌ی مستندسازی یک Web API و همچنین آزمایش دستی اجزای آن به کمک Swagger-UI که رابط کاربری ایجاد شده‌ای بر اساس خروجی Open API است، آشنا شدیم. بنابراین اگر می‌توان رابط کاربری خودکاری را بر اساس OpenAPI Spec ایجاد کرد، به این معنا است که تمام اطلاعات لازم جهت انجام اینکار، هم اکنون در آن قرار دارد. در ادامه قصد داریم تعامل دستی با Swagger-UI را جهت آزمایش Web API، به Postman منتقل کرده تا اجرای مجموعه‌ای از آن‌ها را توسط Collection Runner، خودکار کنیم.


ساخت و ایجاد درخواست‌های Postman به کمک خروجی OpenAPI

در اینجا از همان برنامه‌ای که در سری «مستند سازی ASP.NET Core 2x API توسط OpenAPI Swagger» بررسی کردیم، استفاده خواهیم کرد. بنابراین، این برنامه از پیش تنظیم شده‌است و هم اکنون به همراه یک تولید کننده‌ی OpenAPI Specification نیز می‌باشد. آن‌را اجرا کنید تا بتوان به OpenAPI Specification تولیدی آن در آدرس زیر دسترسی یافت:
https://localhost:5001/swagger/LibraryOpenAPISpecification/swagger.json
سپس برنامه‌ی Postman را گشوده و از منوی File، گزینه‌ی Import آن‌را انتخاب کنید:


در برگه‌ی Import from link آن، همان URL فوق را که به خروجی OpenAPI Spec اشاره می‌کند، وارد کنید. اکنون با کلیک بر روی دکمه‌ی Import، یک مجموعه‌ی جدید، به نام Library API، به لیست مجموعه‌های Postman، اضافه می‌شود:


Postman تمام این اطلاعات را به صورت خودکار از OpenAPI Spec استخراج کرده‌است. تمام نام‌ها نیز بر اساس توضیحاتی که برای متدها نوشته‌ایم، انتخاب شده‌اند.


ارسال اولین درخواست به Web API

در اینجا برای نمونه اگر درخواست «Get list of authors» را انتخاب کنیم، یک چنین خروجی ظاهر می‌شود:


همانطور که مشاهده می‌کنید، متغیر {{baseUrl}} را جهت تنظیم آدرس پایه‌ی Web API انتخاب کرده‌است. این نکته در مطلب «قسمت پنجم - انواع متغیرهای قابل تعریف در Postman» بیشتر بحث شده‌است. هدف از تعریف متغیر {{baseUrl}} به این شکل در اینجا، امکان تعریف آن به صورت یک متغیر محیطی است تا بتوان آن‌را به سادگی بر اساس محیط‌های مختلفی که تعریف و انتخاب می‌کنیم، تغییر داد؛ بدون اینکه نیازی باشد اصل درخواست‌های تعریف شده، تغییری کنند. بنابراین در ادامه نیاز است یک محیط جدید را تعریف کنیم.
برای تعریف یک محیط جدید می‌توان بر روی دکمه‌‌ای با آیکن چشم، در بالای سمت راست صفحه و کلیک بر روی گزینه‌ی Add آن، یک محیط جدید را ایجاد کرد:


در صفحه‌ی باز شده ابتدا باید نامی را برای این محیط جدید انتخاب کرد و سپس می‌توان key/valueهایی را مخصوص این محیط، تعریف نمود:


ابتدا یک نام دلخواه وارد شده‌است و سپس متغیر محیطی baseUrl را با مقدار اولیه‌ی https://localhost:5001 تنظیم کرده‌ایم. پس از آن با کلیک بر روی Add پایین این صفحه، کار تعریف این محیط جدید به پایان می‌رسد.

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


پس از این انتخاب، اگر اشاره‌گر ماوس را به متغیر baseUrl نزدیک کنیم، می‌توان مقدار تنظیم شده‌ی آن‌را مشاهده کرد:


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


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


همانطور که در مطلب «قسمت ششم - یک مثال تکمیلی: تبدیل رابط کاربری مثال JWT به یک مجموعه‌ی Postman» نیز مشاهده کردیم، برای تعریف هدرهای Authorization یا می‌توان به برگه‌ی هدرهای درخواست جاری مراجعه کرد و این هدرها را دستی تولید کرد و یا می‌توان با استفاده از برگه‌ی Authorization آن، کار تعریف این هدرها را ساده نمود. برای مثال در اینجا Postman بر اساس خروجی OpenAPI، دقیقا تشخیص داده‌است که این Web API از Basic authentication استفاده می‌کند. به همین جهت فیلدهای ورود نام کاربری و کلمه‌ی عبور را علاوه بر نوع اعتبارسنجی از پیش انتخاب شده، تدارک دیده‌است.
برای اینکه این مقادیر را نیز تبدیل به متغیرهای محیطی کنیم، برای ویرایش اطلاعات منتسب به محیط جاری، ابتدا باید آن‌را از dropdown محیط‌های بالای صفحه انتخاب کرد. اکنون با کلیک بر روی دکمه‌‌ای با آیکن چشم، در بالای سمت راست صفحه، لینک ویرایش این محیط انتخاب شده ظاهر می‌شود. با کلیک بر روی آن، می‌توان دو متغیر محیطی جدید را تعریف کرد:


پس از تعریف متغیرهای محیطی {{username}} و {{password}}، آن‌ها را در قسمت Authorization درخواست جاری استفاده می‌کنیم:


اینبار اگر مجددا بر روی دکمه‌ی Send کلیک کنیم، خروجی ذیل حاصل خواهد شد:


 
مطالب
Strong Name
نام قوی (Strong Name یا به‌صورت مخفف SN) تکنولوژی‌ای است که با ورود دانت نت معرفی شده و امکانات متنوعی را در زمینه حفاظت از هویت اسمبلی فراهم کرده است. اما بسیاری از برنامه‌نویسان به اشتباه آن را به‌عنوان ابزاری برای فعال‌سازی امنیت می‌پندارند، درصورتی‌که «نام قوی» درواقع یک تکنولوژی تعیین «هویتِ منحصربه‌فرد» اسمبلی‌ها است. یک نام قوی حاوی مجموعه‌ای از مشخصات یک اسمبلی (شامل نام ساده، نسخه و داده‌های کالچر (culture) آن در صورت وجود) به‌همراه یک کلید عمومی و یک امضای دیجیتال است. در زیر یک نمونه از یک اسمبلی دارای نام قوی را مشاهده می‌کنید:
System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35
این نام با استفاده از داده‌های موجود در فایل اصلی یک اسمبلی و نیز یک کلید خصوصی تولید می‌شود. (فایل اصلی اسمبلی فایلی است که حاوی مانیفست اسمبلی است که این مانیفست خود شامل عنوان و هش‌کدهای تمام فایل‌هایی است که اسمبلی را می‌سازند. دات نت از MultiFile Assembly پشتیبانی می‌کند. برای مدیریت این نوع از اسمبلی‌ها می‌توان از (Assembly Linker (al.exe استفاده کرد. البته درحال حاضر امکان توسعه این نوع از اسمبلی‌ها در ویژوال استودیو موجود نیست.) در sdkهای مایکروسافت ابزارهایی برای تولید نام‌های قوی برای اسمبلی‌ها وجود دارد که در ادامه در مورد نحوه استفاده از یک مورد از آن‌ها توضیح داده خواهد شد.
اسمبلی‌هایی که نام‌های قوی یکسانی دارند همانند و یکسان هستند. با اختصاص دادن یک نام قوی به یک اسمبلی می‌توان اطمینان حاصل کرد که نام آن منحصربه‌فرد خواهد شد. به‌طور کلی نام‌های قوی نیازمندی‌های زیر را برطرف می‌کنند:
- نام‌های قوی منحصربه‌فرد بودن نام یک اسمبلی را براساس جفت‌کلیدهای یکتا فراهم می‌کنند. هیچ‌کس دیگری امکان تولید همان اسمبلی‌ای را که شما تولید کرده‌اید ندارد، زیرا اسمبلی‌ای که با یک کلید خصوصی تولید شده است نسبت به اسمبلی دیگری که با یک کلید خصوصی دیگر تولید شده است نام متفاوتی خواهد داشت چون کلید عمومی متناظر با این کلید خصوصی بخشی از نام قوی نهایی تولید شده خواهد بود.
- نام‌های قوی از خط تولید نسخه‌های یک اسمبلی محافظت می‌کنند. یک نام قوی اطمینان می‌دهد تا شخص دیگری نتواند نسخه دیگری از اسمبلی شما را تولید کند. مصرف‌کنندگان می‌توانند مطمئن باشند که نسخه‌ای از اسمبلی را که بارگذاری می‌کنند از همان توزیع‌کننده اسمبلی می‌آید که این نسخه از اسمبلی را تولید کرده است.
- نام‌های قوی بررسی هویت مستحکمی را فراهم می‌کنند. عبور از دروازه امنیتی دات نت فریمورک نشان‌دهنده این است که محتوای اسمبلی پس از تولید آن تغییر نکرده است.
هنگامی‌که به یک اسمبلیِ دارای نام قوی در اسمبلی دیگری ریفرنس داده می‌شود، تا زمانی که به اسمبلی مقصد نیز یک نام قوی داده نشود نمی‌توان در نهایت از مزایای یک نام قوی بهره برد. درواقع در دنیای دات نت به اسمبلی‌های دارای نام قوی تنها می‌توان اسمبلی‌هایی ریفرنس داد که خود نیز دارای نام قوی هستند.
نام قوی یک تکنولوژی براساس اصول کریپتوگرافی و امضاهای دیجیتال است که ایده پایه‌ای آن را می‌توان در تصویر زیر دید:

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

1. تولید و مدیریت جفت‌کلیدهای قوی- نام‌گذاری‌شده (Strongly Named Key Pairs)

همان‌طور که در قسمت قبل اشاره شد برای نام‌گذاری قوی یک اسمبلی به یک کلید عمومی (public key) و یک کلید خصوصی (private key) که در مجموع به آن یک جفت کلید (key pair) می‌گویند، نیاز است.برای این‌کار می‌توان با استفاده از برنامه sn.exe (عنوان کامل آن Microsoft .Net Framework Strong Name Utility است) یک جفت کلید تولید کرده و آن را در یک فایل و یا در CSP (یا همان cryptographic service provider) ذخیره کرد. هم‌چنین این‌کار را می‌توان توسط ویژوال استودیو نیز انجام داد. امکان موردنظر در فرم پراپرتی یک پروژه و در تب Signing آن وجود دارد.

نکته: یک CSP عنصری از API کریپتوگرافی ویندوز (Win32 CryptoAPI) است که سرویس‌هایی چون اینکریپشن، دیکریپشن، و تولید امضای دیجیتال را فراهم می‌کند. این پرووایدرها هم‌چنین تسهیلاتی برای مخازن کلیدها فراهم می‌کنند که از اینکریپشن‌های قوی و ساختار امنیتی سیستم عامل (سیستم امنیتی و دسترسی کاربران ویندوز) برای محافظت از تمام کلیدهای کریپتوگرافی ذخیره شده در مخزن استفاده می‌کند. به‌طور خلاصه و مفید می‌شود اشاره کرد که می‌توان کلیدهای کریپتوگرافی را درون یک مخزن کلید CSP ذخیره کرد و تقریبا مطمئن بود که تا زمانی‌که هیچ‌کس کلمه عبور سیستم عامل را نداند، این کلیدها امن خواهند ماند. برای کسب اطلاعات بیشتر به داده‌های CryptoAPI در اسناد SDK سیستم عامل خود مراجعه کنید.

برنامه sn به همراه SDKهای ویندوز نصب می‌شود. البته با نصب ویژوال استودیو تمام SDKهای موردنیاز مطابق با نسخه‌های موجود، نصب خواهد شد. مسیر نسخه 4 و 32 بیتی این برنامه در سیستم عامل Windows 7 به‌صورت زیر است:

C:\Program Files\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\sn.exe

با استفاده از آرگومان k همانند دستور زیر یک جفت‌کلید جدید تولید شده و در فایل MyKeys.snk در ریشه درایو d: ذخیره می‌شود:

sn –k d:\MyKeys.snk

نکته: به بزرگی و کوچکی حروف سوییچ‌های دستورات برنامه sn دقت کنید!

این کار یک جفت کلید کریپتوگرافی 1024 بیتی به‌صورت تصادفی تولید می‌کند. این دستور را باید در خط فرمانی (Command Prompt) اجرا نمود که مسیر فایل sn.exe را بداند. برای راحتی کار می‌توان از خط فرمان ویژوال استودیو (Visual Studio Command Prompt) استفاده کرد.

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

نکته: هرچند که می‌توان از پسوندهای دیگری نیز برای نام فایل حاوی جفت کلید استفاده کرد، اما توصیه می‌شود از همین پسوند snk. استفاده شود.

فایل تولید شده حاوی هر دو کلید «عمومی» و «خصوصی» است. می‌توان با استفاده از دستور زیر کلید عمومی موجود در فایل mykeys.snk را استخراج کرده و در فایل mypublickey.snk ذخیره کرد:

sn –p d:\mykeys.snk d:\mypublickey.snk

 با استفاده از فایل حاوی کلید عمومی می‌توان با استفاده از دستور زیر کلید عمومی موجود در آن را بدست آورد:
sn -tp MyPublicKey.snk

 مقدار نمایش داده در انتهای تصویر فوق به‌عنوان «توکِن کلید عمومی» (Public key Token) درواقع 8 بایت پایانی کد هش‌شده کریپتوگرافیِ محاسبه‌شده از کلید عمومی است. چون خود کلید عمومی همان‌طور که مشاهده می‌شود بسیار طولانی است، دات‌نت‌فریمورک معمولا از این توکِن برای نمایش آن و ریفرنس دادن اسمبلی‌ها استفاده می‌کند. نیازی نیست تا راز این کلیدها توسط توسعه‌دهنده حفظ شود! پس از نام‌گذاری قوی اسمبلی (که در ادامه توضیح داده می‌شود) کامپایلر با استفاده از کلید خصوصی فراهم شده یک امضای دیجیتالی (یک کد اینکریپت شده) با استفاده از داده‌های «مانیفست اسمبلی» تولید می‌کند. در ادامه کامپایلر این «امضای دیجیتال» و «کلید عمومی» را درون اسمبلی قرار می‌دهد تا مصرف‌کننده‌های اسمبلی بتوانند این امضای دیجیتال را تایید کنند. حفظ کردن «کلید خصوصی» بسیار مهم است! اگر کسی به کلید خصوصی اسمبلی دست یابد می‌تواند با استفاده از آن نسخه‌ای تغییریافته از اسمبلی را امضا کرده و در اختیار مصرف‌کنندگان قرار دهد. مصرف‌کنندگان نیز بدون اینکه متوجه شوند می‌توانند از این نسخه تغییر یافته با همان توکِن کلید عمومی که در اختیار دارند استفاده کنند. درحال حاضر روشی برای فهمیدن این تغییر وجود ندارد. اگر کلید خصوصی لو رفت، باید یک جفت کلید دیگر تولید و با استفاده از کلید خصوصی جدید اسمبلی را دوباره امضا کرد و در اختیار مصرف‌کنندگان قرار داد. هم‌چنین باید مشتریانِ اسمبلی را از این تغییر آگاه ساخت و کلید عمومی مورد اطمینان را در اختیار آن‌ها قرار داد.
نکته: معمولا گروه کوچکی از افراد مورد اطمینان (که دسترسی امضای اسمبلی را دارند: signing authority) مسئولیت کلیدهای نامگذاری قوی یک شرکت را بر عهده دارند و برای امضای تمام اسمبلی‌ها قبل از ریلیز نهایی آن‌ها مسئول هستند.
قابلیت امضای تاخیری اسمبلی (که در ادامه بحث می‌شود) تسهیلاتی را برای بهره‌برداری راحت‌تر از این روش و جلوگیری از توزیع کلیدهای خصوصی میان تمام توسعه‌دهندگان را فراهم می‌کند.
یکی از روش‌هایی که sn برای افزایش امنیت کلیدها ارائه می‌دهد، استفاده از مخزن کلید CSP است. پس از تولید فایل حاوی جفت کلید، می‌توان با استفاده از دستور زیر این کلیدها را درون CSP با نام MyStrongNameKeys ذخیره کرد:
sn -i MyKeys.snk MyStrongNameKeys
سپس می‌توان فایل حاوی جفت کلید را حذف کرد.

نکته مهمی که درباره مخازن کلید CSP باید بدان اشاره کرد این است که این مخازن شامل مخازن تعریف‌شده توسط «کاربر» و نیز مخازن «سیستمی» است. سیستم امنیتی ویندوز به کابران اجازه دسترسی به مخازنی غیر از مخازن خودشان و مخازن سیستمی را نمی‌دهد. برنامه sn به‌صورت پیش‌فرض کلیدها را درون مخازن سیستمی ذخیره می‌کند. بنابراین هر کسی که بتواند به سیستم لاگین کند و نیز از نام مخزن مربوطه آگاه باشد، به‌راحتی می‌تواند اسمبلی شما را امضا کند! برای اینکه ابزار sn کلیدها را در مخازن کاربری ذخیره کند باید از دستور زیر استفاده کرد:
sn –m n
برای برگرداند تنظیم به ذخیره در مخازن سیستمی نیز باید از دستور زیر استفاده کرد:
sn –m y

 برای حذف کلیدها از مخزن می‌توان از دستور زیر استفاده کرد:
sn -d MyStrongNameKeys

2. نام‌گذاری قوی یک اسمبلی
نام‌گذاری قوی یک اسمبلی به دلایل زیادی انجام می‌شود:
- برای اینکه اسمبلی شناسه‌ای منحصربه‌فرد داشته باشد، تا کاربران بتوانند مجوزهای ویژه‌ای را در حین تنظیم سیاست‌های امنیتی دسترسی به کد اعمال کنند.
- تا اسمبلی را نتوان تغییر داده و سپس به عنوان اسمبلی اصلی توزیع نمود.
- تا اسمبلی بتواند نسخه‌گذاری (Versioning) و سیاست‌های نسخه‌گذاری را پشتیبانی کند.
- تا بتوان اسمبلی را در GAC (همان Global Assembly Cache که در مسیر %windir%\assembly قرار دارد) ذخیره کرده و آن را بین چند اپلیکیشن به اشتراک گذاشت.
برای نام‌گذاری قوی اسمبلی با استفاده از خط فرمان کامپایلر #C باید از سوییچهای keyfile/ و یا keycontainer/ استفاده کنید.
 

csc /keyfile:d:\mykeys.snk /out:"C:\Projects\ClassLibrary1\Class1.exe" "C:\Projects\ClassLibrary1\Class1.cs" 

نکته: برای استفاده از این ویژگی در ویژوال استودیو، باید در تب Signing در تنظیمات پروژه گزینه Sign the Assembly را انتخاب کرد. سپس می‌توان فایل حاوی جفت کلیدهای تولیدشده را انتخاب یا فایل جدیدی تولید کرد. البته ویژوال استودیو تا نسخه 2010 امکانی جهت استفاده از مخازن CSP را ندارد.

روش ساده دیگر استفاده از attributeهای سطح اسمبلی است:
[assembly:AssemblyKeyFileAttribute("MyKeys.snk")]
3. بررسی اینکه آیا یک اسمبلی قوی-نام‌گذاری‌شده تغییر یافته یا خیر
زمانی‌که CLR در زمان اجرا یک اسمبلی قوی-نام‌گذاری‌شده را بارگذاری می‌کند:
-ابتدا با استفاده از کلید عمومی (که در خود اسمبلی ذخیره شده است) هش‌کد اینکریپت‌شده که در زمان کامپایل محاسبه شده (یا همان امضای دیجیتال که این نیز درون خود اسمبلی ذخیره شده است) را دیکریپت می‌کند. (هش‌کد زمان کامپایل)
-پس از آن هش‌کد اسمبلی را با استفاده از داده‌های مانیفست اسمبلی محاسبه می‌کند. (هش‌کد زمان اجرا)
-سپس این دو مقدار بدست آمده (هش‌کد زمان کامپایل و هش‌کد زمان اجرا) را با یکدیگر مقایسه می‌کند. این عملیات مقایسه و تایید مشخص می‌کند که آیا اسمبلی پس از امضا دچار تغییر شده است یا خیر!
اگر یک اسمبلی نتواند عملیات تایید نام قوی را پشت سر بگذارد، CLR پیغام خطایی به نمایش خواهد گذاشت. این خطا یک اکسپشن از نوع System.IO.FileLoadException با پیغام Strong name validation failed خواهد بود. با استفاده از ابزار sn نیز می‌توان یک اسمبلی قوی-نام‌گذاری شده را تایید کرد. برای مثال برای تایید اسمبلی MyAsm.exe می‌توان از دستور زیر استفاده کرد:
sn –vf MyAsm.exe

سوییچ v موجب تایید نام قوی اسمبلی شده و سوییچ f برنامه را مجبور به بررسی صحت نام قوی اسمبلی می‌کند، حتی اگر این امکان قبلا برای اسمبلی غیرفعال شده باشد. (با استفاده از سویج Vr مثل دستور sn –Vr MyAsm.exe می‌توان عملیات تایید نام قوی یک اسمبلی خاص را غیرفعال کرد). اگر اسمبلی تغییر کرده باشد و نتواند آزمون فوق را پشت سر بگذارد خطایی به شکل زیر نمایش داده می‌شود:
Microsoft (R) .NET Framework Strong Name Utility Version 2.0.50727.42
Copyright (C) Microsoft Corporation. All rights reserved.
Failed to verify assembly --
Strong name validation failed for assembly MyAsm.exe'.
4. امضای تاخیری (Delay Sign) یک اسمبلی
درصورتی‌که بخواهیم یک اسمبلی را امضا کنیم اما نخواهیم تمام اعضای تیم توسعه به کلید خصوصی مربوطه دسترسی داشته باشند باید از تکنیک امضای با تاخیر اسمبلی استفاده کنیم. ابتدا باید کلید عمومی تولیدشده برای اسمبلی را استخراج کرده و آنرا توزیع کنیم. با توجه به توضیحات داده شده در بخش اول، به اسمبلی خود یک نام قوی اختصاص دهید. هم‌چنین اسمبلی خود را با استفاده از سویج delaysign/ باید کامپایل کنید. سپس با استفاده از سوییچ Vr برنامه sn عملیات تایید اسمبلی خود را غیرفعال کنید.
نکته: برای استفاده از این امکان در ویژوال استودیو باید گزینه Delay sign only را در تب Signing از پراپرتی پروژه انتخاب کرد.

 اسمبلی‌هایی که ریفرنسی به اسمبلی‌های نام‌گذاری قوی شده دارند، حاوی توکِن کلید عمومی آن اسمبلی‌ها نیز هستند. این بدین معنی است که این گونه اسمبلی‌ها بایستی قبل از ریفرنس داده شدن امضا شده باشند. در یک محیط توسعه که اسمبلی‌ها مرتبا کامپایل می‌شوند نیاز است تا تمام توسعه دهندگان و آزمایش‌کنندگان به جفت‌کلیدهای موجود دسترسی داشته باشند (یک ریسک امنیتی بزرگ). به جای توزیع کلید خصوصی، دات‌نت‌فریمورک مکانیزمی به نام امضای تاخیری (delay-signing) فراهم کرده است، که به شما اجازه می‌دهد تا یک اسمبلی را به‌صورت ناکامل (ناقص) امضا کنید. اسمبلی «ناقص-نام‌گذاریِ قوی شده»! حاوی کلید عمومی و توکِن کلید عمومی است که برای ریفرنس دادن اسمبلی نیاز است، اما تنها حاوی مکانِ خالیِ امضای دیجیتالی است که توسط کلید خصوصی تولید می‌شود. پس از کامل شدن توسعة برنامه، فرد مسئول امضای اسمبلی‌ها (signing authority - شخصی که مسئول امنیت و حفظ جفت‌کلیدهاست) اسمبلی‌‌های حاوی امضای تاخیری را دوباره امضا می‌کند، تا نام‌گذاریِ قوی آن اسمبلی کامل شود. برای امضای تاخیری یک اسمبلی تنها نیاز به کلید عمومی آن است، که هیچ ریسک امنیتی‌ای برای آن وجود ندارد. برای استخراج کلید عمومی یک جفت کلید همان‌طور که قبلا اشاره شده است، می‌توان از دستورات زیر استفاده کرد:
sn –p d:\MyKeys.snk d:\MyPublicKey.snk
sn –pc MyKeysContainer d:\MyPublicKey.snk
با داشتن فایل حاوی کلید عمومی، و با استفاده از از دستور کامپایل زیر می‌توان اسمبلی را امضای تاخیری کرد:
csc.exe /delaysign /keyfile:d:\MyPublicKey.snk /out:d:\MyAsm.exe d:\Class1.cs
نکته: برای امضای اسمبلی‌های چندفایلی (multifile assembly) باید از Assembly Linker (نام فایل اجرایی آن al.exe است) استفاده کرد. این ابزار نیز مانند ابزار sn.exe در sdkهای ویندوز یافت می‌شود. دستوری که باید برای امضای این نوع اسمبلی‌های به‌کار برد به‌صورت زیر است:
al /out:<assembly name> <module name> /keyfile:<file name>
از آنجاکه درهنگام بارگذاری اسمبلی، CLR اسمبلی را به عنوان یک اسمبلی قوی نام‌گذاری شده درنظر می‌گیرد، همان‌طور که قبلا اشاره شده، سعی می‌کند تا صحت آن را بررسی و تایید کند. اما چون اسمبلی با امضای تاخیری هنوز امضا نشده است، باید CLR را جوری تنظیم کنید تا تایید اعتبار این اسمبلی را در کامپیوتر جاری انجام ندهد. این کار را همان‌طور که در بالا توضیح داده شد، می‌توان با دستور زیر انجام داد:
sn –Vr d:\MyAsm.exe

از لحاظ فنی این دستور اسمبلی موردنظر را در لیست «صرف‌نظر از تایید اسمبلی» ثبت (register) می‌کند. دقت کنید که دستور فوق را باید در تمام سیستم‌هایی که قرار است به نحوی با این اسمبلی سروکار داشته باشند اجرا کنید!
نکته: تا زمانی‌که با استفاده از دستور فوق عملیات تایید اعتبار اسمبلی‌های امضای تاخیری شده را غیرفعال نکنید امکان اجرا یا بارگذاری آن اسمبلی‌ها و نیز دیباگ سورس‌کدهای آن را نخواهید داشت!
پس از تکمیل فاز توسعه باید اسمبلی را دوباره امضا کنید تا نام‌گذاری قوی کامل شود. برنامه sn به شما این امکان را می‌دهد تا بدون تغییر سورس‌کد اسمبلی خود یا کامپایل دوباره آن عملیات امضای دوباره آنرا انجام دهید. اما برای این‌کار شما باید به کلید خصوصی آن (در واقع به فایل حاوی جفت‌کلید مربوطه) دسترسی داشته باشید. برای امضای دوباره می‌توان از دستورات زیر استفاده کرد:
sn –R d:\MyAsm.exe MyKeys.snk
sn –R d:\MyAsm.exe MyKeysContainer

با استفاده از این دستور برنامه sn شروع به محاسبه هش‌کد زمان کامپایل می‌کند و درنهایت مقدار اینکریپت‌شده را درون اسمبلی ذخیره می‌کند.
نکته: هنگام استفاده از اسمبلی‌های با امضای تاخیری، امکان مقایسه بیلدهای مختلف یک اسمبلی خاص برای اطمینان از اینکه تنها در امضای دیجیتال با هم فرق دارند، معمولا مفید است. این مقایسه تنها وقتی امکان‌پذیر است که اسمبلی موردنظر با استفاده از سوییچ R دوباره امضا شود. برای مقایسه دو اسمبلی می‌توان از سوییچ D استفاده کرد:
sn –D assembly1 assembly2
پس از امضای دوباره اسمبلی می‌توان عملیات تایید آنرا که قبلا غیرفعال شده است، با استفاده از دستور زیر دوباره فعال کرد:
sn –Vu d:\MyAsm.exe

دستور فوق اسمبلی موردنظر را از لیست «صرفنظر از تایید اسمبلی» حذف (Unregister) می‌کند.
نکته: درصورتی‌که بخواهید یک اسمبلی را قبل از امضای دوباره (و یا در حالت کلی، قبل از اینکه اسمبلی دارای یک نام قوی کامل شده باشد) اجرا یا از آن به عنوان یک ریفرنس استفاده کنید، بدون اینکه آن را به لیست «صرفنظر از تایید اسمبلی» اضافی کنید،  با خطای زیر مواجه خواهید شد: 

برای فعال‌سازی تایید اسمبلی برای تمامی اسمبلی‌هایی که این ویژگی برای آنان غیرفعال شده است، می‌توانید از دستور زیر استفاده کنید:
sn –Vx
برای لیست کردن اسمبلی‌هایی که تایید آنان غیرفعال شده است، می‌توانید از دستور زیر استفاده کنید:
sn –Vl
نکته: در دات‌نت 1.0 و 1.1 کامپایلر #C فاقد سوییچ delaysign/ است. برای استفاده از امکان امضای تاخیری اسمبلی می‌توان از attribute سطح اسمبلی System.Reflection.AssemblyDelaySignAttribute استفاده کرد. همچنین می‌شود از ابزار لینکر اسمبلی (al.exe) که از این سوییچ پشتیبانی می‌کند استفاده کرد.
نکته: ابزارهای obfuscating که برای پیچیده‌کردن کد IL اسمبلی تولیدی به‌منظور جلوگیری از عملیات تولید دوباره کد (مثل کاری که برنامه Reflector انجام می‌دهد) به‌کار می‌روند، به دلیل تغییراتی که در محتوای اسمبلی ایجاد می‌کنند، درصورتیکه برای اسمبلی‌های دارای نام قوی استفاده شوند موجب ازکار افتادن آن‌ها می‌شوند. بنابراین یا باید آن‌ها را در سیستم‌هایی استفاده کرد که آن اسمبلی موردنظر در لیست صرفنظر از تایید اسمبلی ثبت شده باشد یا اینکه اسمبلی مربوطه را دوباره با استفاده از روش‌های توضیح داده‌شده (مثلا با استفاده از دستور sn –R myAsm.dll MyKeys.snk) برای تخصیص نام قوی جدید امضا کرد. الگوی معمولی که برای استفاده از obfuscating برای اسمبلی‌های دارای نام قوی استفاده می‌شود به‌صورت زیر است:
- ساخت اسمبلی با امضای تاخیری
- افزودن اسمبلی به لیست صرفنظر از تایید اسمبلی (sn -Vr)
- دیباگ و تست اسمبلی
- obfuscate کردن اسمبلی
- دیباگ و تست اسمبلی obfuscate شده
- امضای دوباره اسمبلی (sn -R)
الگوی ساده‌تر دیگری نیز برای این منظور استفاده می‌شود که به‌صورت زیر است:
- تولید اسمبلی بدون استفاده از تنظیمات امضای تاخیری
- دیباگ و تست اسمبلی
- obfuscate اسمبلی
- امضای دوباره اسمبلی (sn -R)
- دیباگ و تست دوباره نسخه obfuscate شده

5. مدیریت کش عمومی اسمبلی‌ها (Global Assembly Cache)
با استفاده از توضیحات این بخش می‌توان اسمبلی‌ها را به GAC اضافه و یا از درون آن حذف کرد. این کار با استفاده از برنامه gacutil.exe انجام می‌شود. مسیر نسخه 4 و 32 بیتی این برنامه به‌صورت زیر است:
C:\Program Files\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\gacutil.exe
این برنامه به‌همراه SDK ویندوز و یا به‌همراه ویژوال استودیو در مسیری مشابه نشانی بالا نصب می‌شود. همانند توضیحات داده‌شده در مورد برنامه sn.exe، برای راحتی کار می‌توانید از خط فرمان ویژه‌ای که ویژوال استودیو در اختیار شما قرار می‌دهد استفاده کنید. البته قبل از اجرای هر دستوری مطمئن شوید که خط فرمان شما با استفاده از مجوز مدیریتی (Administrator) اجرا شده است! تنها اسمبلی‌های دارای نام قوی می‌توانند در GAC نصب شوند. بنابراین قبل افزودن یک اسمبلی به GAC باید طبق راهنمایی‌های موجود در قسمت‌های قبلی آن را به‌صورت قوی نام‌گذاری کرد. برای افزودن یک اسمبلی با نام MyAsm.dll می‌توان از دستور زیر استفاده کرد:
gacutil /i c:\MyAsm.dll

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

می‌توان نسخه‌های متفاوتی از یک اسمبلی (با نام یکسان) را با استفاده از این ابزار در GAC رجیستر کرد و آن‌ها را در کنار یکدیگر برای استفاده در نرم‌افزارهای گوناگون در اختیار داشت. برای حذف یک اسمبلی از GAC و یا به اصطلاح uninstall کردن آن می‌توان از دستور زیر استفاده کرد:
gacutil /u MyAsm
نکته: دقت کنید که در این دستور تنها از نام اسمبلی استفاده شده است و نه نام فایل حاوی آن!

دستور فوق تمام نسخه‌های اسمبلی MyAsm موجود در GAC را حذف خواهد کرد. برای حذف نسخه‌ای خاص باید از دستوری مشابه زیر استفاده کرد:
gacutil /u MyAsm,Version=1.3.0.5
برای مشاهده تمام اسمبلی‌های نصب شده در GAC می‌توان از دستور زیر استفاده کرد:
gacutil /l

همان‌طور که مشاهده می‌کنید دستور فوق فهرستی بسیار طولانی از تمام اسمبلی‌های نصب‌شده در GAC را به‌همراه لیست اسمبلی‌هایی که در کش ngen به فرم باینری پیش‌کامپایل (Precompiled) شده‌اند، نمایش می‌دهد. برای تعیین اینکه آیا اسمبلی موردنظر در GAC نصب شده است می‌توان از دستور زیر استفاده کرد:
gacutil /l MyAsm

نکته: دات‌نت از GAC تنها در زمان اجرا استفاده می‌کند. بنابراین کامپایلر #C به‌صورت خودکار درون GAC را برای یافتن ریفرنس‌های یک اسمبلی جستجو نخواهد کرد. در زمان توسعه، کامپایلر #C به یک نسخه لوکال از ریفرنس‌های مذکور نیاز خواهد داشت. برای حل این مشکل می‌توان یک نسخه از این ریفرنس‌ها را به مسیر اسمبلی کپی کرد (در ویژوال استودیو می‌توان از خاصیت Copy Local ریفرنس‌ها استفاده کرد) یا با استفاده از سوییچ lib/ کامپایلر، مسیری را که می‌تواند این ریفرنس‌ها را در آن بیابد معرفی کرد (کاری که ویژوال استودیو به‌صورت خودکار انجام می‌دهد).
نکته: نکته‌ای که در پایان باید اشاره کرد این است که تکنولوژی نام قوی برای بحث امنیت کد اسمبلی (مثلا برای جلوگیری از مهندسی معکوس IL و تغییر آن) بوجود نیامده است زیرا حذف این نام‌های قوی کار سختی نیست. بلکه هدف اصلی این تکنولوژی جلوگیری از تغییرات مخفی خرابکارانه و محرمانه اسمبلی توزیع شده و توزیع این نسخه‌های دستکاری شده به جای نسخه اصلی است. در زیر ابزارها و روش‌هایی که می‌توانند برای حذف کامل نام قوی یک اسمبلی به‌کار روند آورده شده است.
البته باید به این نکته اشاره کرد که در صورت حذف نام قوی یک اسمبلی (یا همان حذف امضای دیجیتال درون آن) تمامی اسمبلی‌هایی که قبل از حذف نام قوی به آن ریفرنس داشتند از کار خواهند افتاد. یعنی درواقع تمامی آن اسمبلی‌ها برای ریفرنس دادن به این اسمبلی با نام جدید (نامی که دیگر قوی نیست) باید آپدیت شوند. هم‌چنین درصورتی‌که اسمبلی‌هایی که قبل از حذف نام قوی به اسمبلی موردنظر ما ریفرنس داشتند، خود نام قوی داشته باشند با حذف نام قوی، آنها از کار خواهند افتاد. چون اسمبلی‌های دارای نام قوی تنها می‌توانند از اسمبلی‌های دارای نام قوی ریفرنس داشته باشند. بنابراین برای کارکردن برنامه موردنظر باید نام قوی تمامی اسمبلی‌های درگیر را حذف کرد!
منابع استفاده شده در تهیه این مطلب:
مطالب
آموزش MDX Query - قسمت پانزدهم – اعمال شرط بر روی خروجی عمل واکشی

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

Select
From [Adventure Works]

همان طور که مشاهده می‌کنید، خروجی یک عدد می‌باشد. بدون نام ستون یا ردیف؟!

به‌خاطر بیاورید که هر Cube در SSAS دارای یک Measure پیش فرض بود که در صورت عدم اعلام نام یک Measure در کوئری، SSAS به صورت پیش فرض مقدار این Measure را بر می‌گرداند. خوب؛ نام ستون و سطر چرا ذکر نشده است؟ به دلیل عدم اعلام صریح نام سطر و ستون در کوئری بالا، SSAS نام ستون و سطر خاصی را نمی‌تواند نمایش دهد.

با بررسی کوئری زیر به درک بیشتری از شاخص (Measure) پیش فرض Cube دست پیدا خواهید کرد. 

Select
From [Adventure Works]
Where
( [Measures].[Reseller Sales Amount] )

خروجی همچنان مانند بالا می‌باشد اما در این حالت اعلام شده است که از کدام شاخص باید واکشی انجام شود. دلیل خروجی مشابه، یکسان بودن شاخص پیش فرض و شرط اعلام شده می‌باشد. به بیان دیگر [Measures].[Reseller Sales Amount] در [Adventure Works] به عنوان شاخص پیش فرض معرفی شده است و با اجرای کوئری زیر عملا شرط واکشی برای یک شاخص متفاوت اعمال شده است.

Select
From [Adventure Works]
Where [Measures].[Internet Sales Amount]

کوئری زیر را اجرا کنید:

Select
[Measures].[Internet Sales Amount] on columns
From [Adventure Works]

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

کوئری زیر را اجرا کنید:

select
[Measures].[Internet Sales Amount] on columns
From [Adventure Works]
Where [Measures].[Internet Sales Amount]

این کوئری با خطا مواجه می شود زیرا در  آن کوئری در یک  Axis  و در شرط ، اعمال انتخاب شاخص شده است که این مورد فقط می‌ بایستی در یکی از  این دو قسمت رخ دهد

و همچنین در صورت انتخاب دو شاخص متفاوت نیز با خطا برخورد خواهیم کرد.

Select
[Measures].[Internet Sales Amount] on columns
From [Adventure Works]
Where [Measures].[Reseller Sales Amount]

به عبارت دیگر نمی‌توان در خواست فیلتر کردن کوئری را برروی شاخص 1 داد؛ در صورتیکه می‌خواهیم شاخص 2 را واکشی کنیم. اعمال شرط برای واکشی اطلاعات از  شاخص، پیش فرض نوشتن این شرط لازم نمی‌باشد؛ زیرا این شاخص به صورت پیش فرض انتخاب شده‌است.  

select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
 }on columns
From [Adventure Works]
Where [Measures].[Reseller Sales Amount]

بنابراین کوئری بالا و کوئری زیر یکسان عمل خواهند کرد:

select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
 }on columns
From [Adventure Works]

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

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where [Customer].[Customer Geography].[Country].[Canada]

با اعمال شرط کشور کانادا، عملا خروجی فروش نمایندگان فروش در کانادا بر اساس دسته بندی محصولات واکشی می‌گردد. کمی به خروجی دقت نمایید. مبلغ سرجمع برابر مبلغ کل فروش اینترنتی می‌باشد که در کوئری‌های قبلی بدست آوردیم؟!

خروجی این کوئری مشکوک به نظر می رسد . زیرا سرجمع مبالغ فروش نمایندگان فروش برای کانادایی ها برابر کل فروش نمایندگان فروش می باشد .آیا کانادایی ها تمام خرید را انجام داده اند؟ خیر .

دلیل این اشکال در این است که هیچ گونه ارتباطی بین بعد مشتری و شاخص پیش فرض در سیستم وجود ندارد .

مشکل کوئری بالا در این کوئری با تغییر بعد در قسمت اعمال شرط برطرف شده؛ اکنون خروجی حقیقی مشاهده می شود .

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
}on columns
From [Adventure Works]
Where [Sales Territory].[Sales Territory].[Country].[Canada]

حال اگر بخواهیم دو شرط را به صورت همزمان داشته باشیم به صورت زیر عمل خواهیم کرد :

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where (
[Customer].[Customer Geography].[Country].[Canada],
[Measures].[Internet Sales Amount]
)

در کوئری بالا سرجمع فروش اینترنتی توسط مشتریان کانادایی بدست آمده است.

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

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns,
[Measures].[Internet Sales Amount] On rows
From [Adventure Works]
Where (
[Customer].[Customer Geography].[Country].[Canada]
)

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

Select
{
[Product].[Product Categories].[Category],[Product].[Product Categories]
} on columns
From [Adventure Works]
Where
(
[Customer].[Customer Geography].[Country].[Canada],
[Customer].[Customer Geography].[Country].[Australia],
[Measures].[Internet Sales Amount]
)

در اینجا ما نیاز داریم میزان فروش اینترنتی کانادا و استرالیا را برای انواع محصولات بدست آوریم   ، اما نحوه استفاده از دو ساختار سلسله مراتبی مرتبط با یک دایمنشن را درست رعایت نکرده‌ایم .بنابر این کوئری زیر را اجرا خواهیم کرد :

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where
(
{
[Customer].[Customer Geography].[Country].[Canada],
[Customer].[Customer Geography].[Country].[Australia]
},
[Measures].[Internet Sales Amount]
)

که همان کوئری بالا می باشد با این تفاوت که از {}  استفاده شده است

درابتدا میزان فروش نمایندگان فروش در انگلستان را بدست می‌آوریم:

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where
[Sales Territory].[Sales Territory].[Country].[United Kingdom]

و برای بدست آوردن فروش اینترنتی تمام کشور ها به جز انگلستان بر اساس دسته بندی محصولات کوئری زیر را خواهیم نوشت :

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where
[Sales Territory].[Sales Territory].[Country]
-
[Sales Territory].[Sales Territory].[Country].[United Kingdom]

البته از تابع Except  هم می‌توان به صورت زیر استفاده کرد

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where
except(
[Sales Territory].[Sales Territory].[Country],
[Sales Territory].[Sales Territory].[Country].[United Kingdom]
)

عملگر منها مشابه except کار میکند.

مطالب
اصلاح daylight saving time ویندوز تا 90 سال بعد
چند سالی هست (از سال 2009) که آپدیت‌های daylight saving time ویندوز شامل حال تنظیمات رسمی ایران نمی‌شود. برای نمونه، همین یکی دو روز قبل بود که ساعت ویندوز به صورت خودکار تغییر کرد؛ درحالیکه باید در انتهای روز 30 شهریور اینکار صورت می‌گرفت.
اطلاعات daylight saving time یا بازه صرفه جویی زمانی ویندوز در دو مدخل رجیستری زیر ثبت می‌شوند:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\Iran Standard Time]



تصویر سوم مرتبط است به ویندوزهای ویستا به بعد که مفهوم dynamic daylight saving time در آن‌ها معرفی شده است.
در اینجا یک نمونه اطلاعات زمانی ثبت شده مرتبط با ایران را مشاهده می‌کنید:
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\Iran Standard Time]
"Display"="(GMT+03:30) Tehran"
"Dlt"="Iran Daylight Time"
"Std"="Iran Standard Time"
"MapID"="-1,72"
"Index"=dword:000000a0
"TZI"=hex:2e,ff,ff,ff,00,00,00,00,c4,ff,ff,ff,00,00,09,00,04,00,03,00,17,00,3b,\
  00,3b,00,00,00,00,00,03,00,02,00,03,00,17,00,3b,00,3b,00,00,00
TZI ایی که در اینجا وجود دارد، دارای یک چنین ساختاری است:
using System.Runtime.InteropServices;

namespace TimeZoneInfo.Core
{
    [StructLayout(LayoutKind.Sequential)]
    public struct TZI
    {
        public int Bias;
        public int StandardBias;
        public int DaylightBias;
        public SystemTime StandardDate;
        public SystemTime DaylightDate;
    }
}
و SystemTime آن نیز به نحو زیر تعریف شده است:
using System;
using System.Runtime.InteropServices;

namespace TimeZoneInfo.Core
{
    [StructLayoutAttribute(LayoutKind.Sequential)]
    public struct SystemTime
    {
        public short Year;
        public short Month;
        public short DayOfWeek;
        public short Day;
        public short Hour;
        public short Minute;
        public short Second;
        public short Milliseconds;
    }
}
برای مثال اگر اطلاعات درج شده در TZI به صورت زیر باشد:
2C 01 00 00 00 00 00 00
C4 FF FF FF 00 00 0A 00
00 00 05 00 02 00 00 00
00 00 00 00 00 00 04 00
00 00 01 00 02 00 00 00
00 00 00 00
نمونه رمزگشایی شده آن به نحو ذیل خواهد بود:
(little-endian)   => (big-endian)
  2C 01 00 00     => 00 00 01 2C = 300 Bias
  00 00 00 00     => 00 00 00 00 = 0 Std Bias
  C4 FF FF FF     => FF FF FF C4 = 4294967236 Dlt Bias
( SYSTEM TIME ) StandardDate
  00 00           => 00 00 = Year
  0A 00           => 00 0A = Month
  00 00           => 00 00 = Day of Week
  05 00           => 00 05 = Day
  02 00           => 00 02 = Hour
  00 00           => 00 00 = Minutes
  00 00           => 00 00 = Seconds
  00 00           => 00 00 = Milliseconds
( SYSTEM TIME ) DaylightDate
  00 00           => 00 00 = Year
  04 00           => 00 04 = Month
  00 00           => 00 00 = Day of Week
  01 00           => 00 01 = Day
  02 00           => 00 02 = Hour
  00 00           => 00 00 = Minutes
  00 00           => 00 00 = Seconds
  00 00           => 00 00 = Milliseconds
در ساختار SystemTime متناظر با TZI، فیلد Day دارای مقدار روز در یک ماه نیست. به معنای شماره هفته است. مثلا پنج شنبه (DayOfWeek) هفته سوم (Day) ماه 9 سال 2012.
همچنین Day از یک شروع می‌شود و DayOfWeek از صفر. Year اگر صفر وارد شود به معنای زمان نسبی است و برای سال بعد نیز می‌تواند کاربرد داشته باشد (و عموما صفر تعریف شده است).
بنابراین برای تبدیل DateTime به SystemTime سازگار با TZI به فرمول زیر خواهیم رسید:
        public static SystemTime ToSystemTime(DateTime time)
        {
            var result = new SystemTime
            {
                Year = 0, // سال نسبی وارد می‌شود نه مطلق
                Month = (short)time.Month,
                DayOfWeek = (short)time.DayOfWeek,
                Hour = (short)time.Hour,
                Minute = (short)time.Minute,
                Second = (short)time.Second,
                Milliseconds = (short)time.Millisecond
            };

            int weekdayOfMonth = 1; // شماره هفته است نه شماره روز
            for (int dd = time.Day; dd > 7; dd -= 7)
                weekdayOfMonth++;

            result.Day = (short)weekdayOfMonth;

            return result;
        }
در ادامه نیاز خواهیم داشت تا ساختار TZI سفارشی و بازسازی شده خودمان را بتوانیم به آرایه‌ای از بایت‌ها تبدیل کنیم تا بتوان در همان مدخل رجیستری نوشت. اینکار را توسط متد SerializeByteArray زیر می‌توان انجام داد:
using System;
using System.Runtime.InteropServices;

namespace TimeZoneInfo.Core
{
    public static class ByteUtils
    {
        public static Byte[] SerializeByteArray<T>(T msg) where T : struct
        {
            int objsize = Marshal.SizeOf(typeof(T));
            Byte[] ret = new Byte[objsize];

            IntPtr buff = Marshal.AllocHGlobal(objsize);
            Marshal.StructureToPtr(msg, buff, true);
            Marshal.Copy(buff, ret, 0, objsize);
            Marshal.FreeHGlobal(buff);

            return ret;
        }
    }
}
و اگر اینکار را تا بیش از 90 سال بعد بر اساس تاریخ ایران انجام داده و مداخل رجیستری ویندوز را تکمیل کنیم، خروجی آن فایل reg زیر خواهد بود که به سادگی با کلیک راست و انتخاب گزینه‌ی merge به رجیستری ویندوز اضافه شده و تا چندین سال بعد، مشکل تنظیمات DST را برطرف خواهد کرد:
پس از اعمال تغییرات فوق، نیاز است یکبار ویندوز را ری استارت کنید.
نظرات مطالب
Owin چیست ؟ قسمت اول
روش برنامه نویسی مایکروسافت بیش از دو سالی می‌شود که به این شکل شده است که هر امکان و قابلیت جدیدی بر روی آخرین نسخه NET Framework. ارائه می‌شود و البته سپس به نسخ قبلی نیز تعمیم می‌یابد، در همین جا است که می‌بینید اکثر امکانات Entity Framework 5 & 6 ابتدا بر روی NET Framework 4.5. ارائه شدند، و سپس بر روی 4
اگر ما بخواهیم به NET Framework. به عنوان یک پیش نیاز دردسر زا نگاه کنیم، در اولین قدم خودمان را به دردسر انداخته ایم، چون نه برای Helios، بلکه برای صدها امکان دیگر مانند Data Flow‌های جدید و ... نیز باید صبر کنیم، که عملا هزینه به فایده آن نمی‌صرفد. پس همیشه با فراغ بال از آخرین نسخه NET Framework. استقبال کنیم
نکته‌ی دیگر را که باید مد نظر داشته باشیم، این است که مطابق با سیاست هایی که مایکروسافت جدیدا اتخاذ کرده است، دیگر نباید خیلی نگران نسخه‌های جدید NET Framework. باشیم، چون دیگر از آن نسخه دهی‌های پشت سر هم و با حجم تغییرات بالا خبری نیست، بلکه اکثر فریم ورک‌های مهم جدا از NET. ارائه و به روز رسانی می‌شوند.
علاوه بر این، ارتقا به آخرین نسخه سیستم عامل ویندوز نیز به هیچ وجه مانند قبل ( IIS 6 به IIS 7 ) دردسر زا نیست، و خوشبختانه این ارتقا ( و یا تغییر هاست ) بدون دردسر است.
به نظر من این ارتقاء را انجام دهید، چون نه فقط Helios که خیلی چیزهای دیگری را دارید از دست می‌دهید، مانند سرعت بالاتر توسعه برنامه بر روی Visual Studio 2013 و Windows 8.1 برای توسعه برنامه‌های وبی، سرعت و کارآمدی بسیار بالاتر NET Framework 4.5.1. با IIS 8.5 برای مشتری‌های برنامه و ...
به نظر من آنقدر این ارتقاء ارزشمند است، که ارزش Helios این میان کمتر ارزشش به چشم می‌آید.
یکی از دلایلی که برنامه‌های سمت وب به سرعت بر برنامه‌های دسکتاپی قدیمی چیره شدند، همین است: امکان ارتقای سرورها در مدت زمان کم و به شکل مدیریت شده و با کمترین تاثیر روی مشتری‌های نهایی، بارها این تصمیم را که در ابتدایش کمی سخت به نظر می‌آید را گرفته ام و در نهایت از مشتری تا برنامه نویس همه را راضی دیده ام، چون هیچ کسی از امکانات جدید که بدون دردسر حاصل شود بدش نمی‌آید، و خوشبختانه کیفیت محصولات مایکروسافت واقعا بهبود یافته و دیگر آن زمانی که از NET 2. به 3.5 می‌رفتیم و گرفتار چندین مشکل می‌شدیم گذشته است.
از این نگذرید که بالاخره روزی باید این مهاجرت‌ها را انجام دهید، پس چه بهتر که از سود آن زودتر بهره مند شوید، البته بی مهابا عمل کردن توصیه نمی‌شود، بد نیست زمانی شروع به ارتقاء کنید که صفحه Release Notes و سوالات موجود در سایت Stack over flow در رابطه با اشکالات رخ داده در زمان ارتقاء و Breaking Changes را از بر باشید، به این صورت عمل کنید تماما برد کرده اید.
مطالب
متد جدید Chunk در دات نت 6
متد جدید ()Chunk در دات نت 6، به مجموعه‌ی LINQ اضافه شده‌است. این متد امکانی را فراهم میکند که بتوان مجموعه‌ای را به گروه‌های کوچکتر، تقسیم کنیم .


وضعیت فعلی  پیاده سازی این قابلیت 
در نسخه‌های قبلی دات نت، چنین قابلیتی برای تقسیم یک مجموعه، به مجموعه‌های کوچکتر بصورت توکار وجود ندارد.
مجموعه‌ی زیر را در نظر بگیرید:
int[] numbers = new int[] {6, 5, 1, 9, 18, 5, 3, 21};
این عملیات تقسیم به مجموعه‌های کوچکتر می‌تواند توسط متد‌های Take و Skip، انجام شود که نتیجه نهایی آنچنان چشم نواز نیست!
var coll1 = numbers.Take(2);
var coll2 = numbers.Skip(2).Take(2);
var coll3 = numbers.Skip(4).Take(2);
var coll4 = numbers.Skip(6).Take(2);

با کمی تامل شاید بتوان روش‌های بهتری نیز برای این نیاز ارائه کرد. در این پرسش و پاسخ که رای بالایی هم دارد، یک متد الحاقی برای تقسیم یک مجموعه، به زیر مجموعه‌های کوچکتر ارائه شده‌است:
static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
        int i = 0;
        var splits = from item in list
                     group item by i++ % parts into part
                     select part.AsEnumerable();
        return splits;
    }
}

پیاده سازی جدید
در دات نت 6، متد جدید Chunk می‌تواند یک مجموعه را به زیر مجموعه‌های کوچکتری تبدیل کند.
فرض کنید یک مجموعه‌ی بزرگ از اعداد تصادفی را داریم:
List<int> numbers = new();

int counter = 0;
Random rand = new(DateTime.Now.Millisecond);
while(counter < 100)
{
    numbers.Add(rand.Next(1, 1000));
    counter++;
}
با استفاده از متد Chunk میتوان این مجموعه‌ی 100 عضوی را به 10 مجموعه‌ی 10 عضوی، تبدیل کرد. این متد مقداری را بعنوان پارامتر دریافت میکند که سایز زیر مجموعه‌هایی است که قرار است تولید شوند.

شرایط خاص در این متد
اگر با تقسیم مجموعه‌ی بزرگتر، زیر مجموعه‌ها تعداد یکسانی عضو نداشتند، چه اتفاقی می‌افتد؟
فرض کنید مجموعه‌ی اصلی 100 عضو و زیر مجموعه‌ها 8 عضو داشته باشند:
IEnumerable<int[]> sublists = numbers.Chunk(8);
چند زیر مجموعه تولید خواهد شد؟ هر مجموعه چند عضو را خواهد داشت؟
خروجی تابع Chunk، سیزده زیر مجموعه دارد؛  12 زیر مجموعه‌ی اول آن، 8 عضوی است که خارج قسمت صحیح تقسیم عدد 100 بر عدد 8 می‌باشد و مجموعه‌ی آخر آن، 4 عضوی است که باقیمانده‌ی تقسیم صحیح 100 بر 8 است. در زمانیکه تعداد زیر مجموعه‌ها فرد است، به این رفتار دقت داشته باشید.
  محاسبه‌ی میانگین ششمین زیر مجموعه‌ی تولید شده در قسمت فوق :
var avg=sublists.ElementAt(6).Average();
مطالب
آشنایی با CLR: قسمت پانزدهم
در قسمت قبلی نحوه‌ی ساخت اسمبلی را یاد گرفتیم. ولی ممکن است که بخواهید اسمبلی را از طریق Assembly Linker یا AL.exe ایجاد کنید. این روش موقعی سودمند است که بخواهید یک اسمبلی از ماژولها از کامپایلرهای مختلف را ایجاد کنید یا اینکه کامپایلر شما مانند کامپایلر سی شارپ از دستور یا سوئیچی مشابه addmodule استفاده نمی‌کند. یا حتی اینکه در زمان کامپایل هنوز اطلاعاتی از نیازمندی‌های اسمبلی‌ها ندارید و به بعد موکول می‌کنید. از AL همچنین می‌توانید در زمینه‌ی ساخت اسمبلی‌های فقط ریسورس هم استفاده کنید که می‌تواند جهت انجام localization به کار رود. AL می‌تواند یک فایل dll یا exe تولید کند که شامل یک فایل manifest بوده که اشاره به ماژول‌های تشکیل دهنده‌اش دارد.

نحوه‌ی ساخت اسمبلی با استفاده از ابزار AL :
csc /t:module RUT.cs
csc /t:module FUT.cs
al /out: MultiFileLibrary.dll /t:library FUT.netmodule RUT.netmodule
تصویر زیر نتیجه‌ی دستور بالاست:


در این مثال ما دو ماژول جدا به نام‌های RUT.netmodule و FUT.netmodule را در یک اسمبلی ایجاد کرده‌ایم. داخل این اسمبلی‌ها جدول متادیتا یا بخش IL از ماژول‌ها به چشم نمی‌خورد. به این معنی که کد IL و جداول مربوطه به آن، هر کدام داخل ماژول یا فایل خودش بوده و در اسمبلی کدی وجود ندارد و تنها یک جدول مانیفست جهت شناسایی ماژول‌هایش دارد. شکل بالا گویای اطلاعات داخلی اسمبلی است که می‌توانید با تصویری که در قسمت قبلی درج شده مقایسه کنید.
تصویر قسمت قبلی جهت مقایسه:


در این حالت سه فایل تشکیل شده است که یکی از آن‌ها MultiFileLibrary.dll ، FUT.netmodule و RUT.netmodule است و در استفاده از این ابزار هیچ راهی برای داشتن یک تک فایل وجود ندارد.
این ابزار همچنین می‌تواند فایل‌های CUI ,GUI و ... را با سوئیچ‌های زیر هم تولید کند:
/t[arget]:exe, /t[arget]:winexe, or /t[arget]:appcontainerexe

 البته اینکار تا حدی غیر معمول است که یک فایل exe بخواهد کدهای IL ابتدایی را از ماژول‌های جداگانه بخواند. در صورتیکه چنین قصدی را دارید، باید یکی از ماژول‌ها را به عنوان مدخل ورودی Main تعریف کنید تا برنامه از آنجا آغاز به کار کند. نحوه‌ی ساخت یک فایل اجرایی و معرفی ماژول Main به شکل زیر است:
csc /t:module /r:MultiFileLibrary.dll Program.cs
al /out:Program.exe /t:exe /main:Program.Main Program.netmodule
در اولین خط مانند سابق فایل netmodule تهیه می‌گردد و در خط دوم، داخل اسمبلی قرار می‌گیرد. ولی به علت استفاده از سوئیچ main یک تابع عمومی global به نام EntryPoint__ هم تعریف می‌گردد که کد IL آن به شرح زیر است:
.method privatescope static void __EntryPoint$PST06000001() cil managed
{
.entrypoint
// Code size 8 (0x8)
.maxstack 8
IL_0000: tail.
IL_0002: call void [.module 'Program.netmodule']Program::Main()
IL_0007: ret
} // end of method 'Global Functions'::__EntryPoint

کد بالا یک کد ساده است که می‌گوید داخل فایل Program.netmodule در نوع Program متدی وجود دارد به نام Main که محل آغازین برنامه است. البته این روش ایجاد فایل‌های EXE، بدین شکل توصیه چندانی نمی‌شود و ذکر این مطلب فقط اطلاع از وجود چنین قابلیتی بود.