نظرات اشتراک‌ها
بررسی وضعیت فعلی پروژه Roslyn
خیر. در دات نت دو فاز اجرایی کلی وجود دارد. تبدیل کدهای زبان‌های مختلف، توسط کامپایلر به کدهای IL. اجرای کدهای IL توسط JIT کامپایلر. بنابراین JIT هست که روی سرعت اجرایی تاثیر دارد و نه کامپایلر فاز اولیه تولید IL.
JIT فعلی دات نت هم در حال بازنویسی است. اطلاعات بیشتر
مطالب
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 و تغییر آن) بوجود نیامده است زیرا حذف این نام‌های قوی کار سختی نیست. بلکه هدف اصلی این تکنولوژی جلوگیری از تغییرات مخفی خرابکارانه و محرمانه اسمبلی توزیع شده و توزیع این نسخه‌های دستکاری شده به جای نسخه اصلی است. در زیر ابزارها و روش‌هایی که می‌توانند برای حذف کامل نام قوی یک اسمبلی به‌کار روند آورده شده است.
البته باید به این نکته اشاره کرد که در صورت حذف نام قوی یک اسمبلی (یا همان حذف امضای دیجیتال درون آن) تمامی اسمبلی‌هایی که قبل از حذف نام قوی به آن ریفرنس داشتند از کار خواهند افتاد. یعنی درواقع تمامی آن اسمبلی‌ها برای ریفرنس دادن به این اسمبلی با نام جدید (نامی که دیگر قوی نیست) باید آپدیت شوند. هم‌چنین درصورتی‌که اسمبلی‌هایی که قبل از حذف نام قوی به اسمبلی موردنظر ما ریفرنس داشتند، خود نام قوی داشته باشند با حذف نام قوی، آنها از کار خواهند افتاد. چون اسمبلی‌های دارای نام قوی تنها می‌توانند از اسمبلی‌های دارای نام قوی ریفرنس داشته باشند. بنابراین برای کارکردن برنامه موردنظر باید نام قوی تمامی اسمبلی‌های درگیر را حذف کرد!
منابع استفاده شده در تهیه این مطلب:
مطالب
ساخت بسته‌های نیوگت مخصوص NET Core.
فایل‌های nuspec مخصوص سایر نگارش‌های دات نت، در NET Core. ندید گرفته شده و پردازش نمی‌شوند. در اینجا نیز تمام تنظیمات تولید بسته‌های نیوگت، در فایل project.json درج می‌شوند که در ادامه آن‌ها را بررسی خواهیم کرد.


فعالسازی تولید خودکار بسته‌های نیوگت در پروژه‌های NET Core.

پس از تهیه‌ی یک کتابخانه‌ی مبتنی بر NET Core.، تنها کاری که در جهت تولید خودکار بسته‌های نیوگت باید انجام شود، افزودن مدخل postcompile ذیل به فایل project.json است:
    "scripts": {
        "postcompile": [
            "dotnet pack --no-build --configuration %compile:Configuration%"
        ]
    }
پس از آن هربار که پروژه کامپایل شود، به صورت خودکار فایل nupkg نهایی در پوشه‌ی bin\Release تشکیل می‌شود.
در این‌حالت اگر فایل nupkg تولیدی را توسط برنامه‌های zip باز کنید، مشاهده خواهید کرد که فایل nuspec خودکاری نیز در آن درج شده‌است؛ اما ... مشخصات ثبت شده‌ی در آن ناقص هستند و شامل مواردی مانند نام پروژه، نام نویسنده، مجوز استفاده‌ی از پروژه، آدرس پروژه و امثال آن‌ها نیستند. در نگارش‌های دیگر دات نت، این مشخصات از فایل nuspec تهیه شده‌ی توسط ما جمع آوری و درج می‌شود. اما در اینجا خیر.


تکمیل فایل project.json برای درج مشخصات پروژه و تکمیل اطلاعات فایل nuspec

هرچند به ظاهر دیگر فایل nuspec دستی تهیه شده در اینجا پردازش نمی‌شود، اما تمام اطلاعات آن‌را در فایل project.json نیز می‌توان درج کرد:
{
    "version": "1.1.1.0",
    "authors": [ "Vahid Nasiri" ],
    "packOptions": {
        "owners": [ "Vahid Nasiri" ],
        "tags": [ "PdfReport", "Excel", "Export", "iTextSharp", "PDF", "Report", "Reporting", "Persian", ".NET Core" ],
        "licenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html",
        "projectUrl": "https://github.com/VahidN/iTextSharp.LGPLv2.Core"
    },
    "description": " iTextSharp.LGPLv2.Core  is an unofficial port of the last LGPL version of the iTextSharp (V4.1.6) to .NET Core.",

    "scripts": {
        "postcompile": [
            "dotnet pack --no-build --configuration %compile:Configuration%"
        ]
    }
}
در اینجا یک نمونه از مشخصات فایل project.json ایی را مشاهده می‌کنید که در آن مواردی مانند نویسندگان، برچسب‌هایی که در سایت نیوگت لیست خواهند شد، آدرس مجوز پروژه، آدرس مخزن کد پروژه و توضیحات آن، تکمیل شده‌اند. قسمت postcompile، دقیقا همین اطلاعات را جهت تولید فایل خودکار nuspec نهایی، استفاده می‌کند.


تکمیل تنظیمات Build پروژه

بهتر است کتابخانه‌های خود را در حالت release و همچنین بهینه سازی شده، توزیع کنید. به همین منظور نیاز است مدخل ذیل را نیز به فایل project.json اضافه کرد:
    "configurations": {
        "Release": {
            "buildOptions": {
                "optimize": true,
                "platform": "anycpu"
            }
        }
    },


افزودن مستندات XML ایی کتابخانه

به احتمال زیاد XML-Docهای هر متد (کامنت‌های مخصوص دات نتی هر متد یا خاصیت عمومی) را نیز به کدهای خود افزوده‌اید. برای اینکه فایل XML نهایی آن به صورت خودکار تولید شده و همچنین در بسته‌ی نیوگت نهایی درج شود، نیاز است مدخل xmlDoc را به buildOptions اضافه کنید:
    "buildOptions": {
        "xmlDoc": true
    },
در این حالت هر عنصری با سطح دسترسی public، باید دارای کامنت باشد. اگر می‌خواهید مجبور به انجام اینکار نشوید و کامپایلر اخطار صادر نکند، می‌توانید از اخطار شماره‌ی 1591 صرفنظر کنید:
    "buildOptions": {
        "xmlDoc": true,
        "nowarn": [ "1591" ] // 1591: missing xml comment for publicly visible type or member
    },


برای مطالعه‌ی بیشتر
project.json reference
نظرات مطالب
یکی کردن اسمبلی‌های یک پروژه‌ی WPF
یک نکته‌ی تکمیلی
اگر به دنبال یک راه حل پخته‌تر هستید که با انواع و اقسام اسمبلی‌ها بتواند کار کند (از mixed mode گرفته تا pcl و غیره)، افزونه‌ی  Fody / Costura توصیه می‌شود. کار با آن نیز بسیار ساده‌است. فقط کافی است دستور زیر را در کنسول پاور شل نیوگت VS.NET اجرا کنید:
PM> Install-Package Costura.Fody
بعد از نصب، تنها یکبار برنامه را مجددا build کنید.
اکنون اگر اسمبلی آن‌را بررسی کنید موارد ذیل را مشاهده خواهید کرد:

الف) اسمبلی‌های مدفون شده را zip کرده‌است.
ب) فایل pdb هم لحاظ شده.
ج) راه انداز خودکار و کدهای AssemblyResolver را تحت فضای نام  Costura به فایل EXE نهایی افزوده‌است.
 
Fody یکی از ابزارهای AOP سورس باز دات نت است.
نظرات مطالب
Blazor 5x - قسمت 34 - توزیع برنامه‌های Blazor بر روی IIS
یک نکته: کاهش زمان build پروژه‌های Blazor در حالت توسعه

در پروژه‌های Blazor، خاصیت ویژه‌ی BlazorLinkOnBuild به true تنظیم شده‌است که سبب می‌شود تا موارد استفاده نشده‌ی در برنامه، از خروجی نهایی حذف شوند. این مورد هرچند حجم نهایی را کاهش می‌دهد، اما در زمان توسعه نیز فعال است و سبب بالا رفتن زمان build برنامه می‌شود؛ گاهی از اوقات تا 10 برابر! برای غیرفعال کردن آن در زمان debug و توسعه، می‌توان فایل csproj را به صورت زیر ویرایش کرد. این تنظیم در حین publish در حالت release، غیرفعال می‌شود:
<PropertyGroup>
    <BlazorLinkOnBuild  Condition="'$(Configuration)'!='Release'">false</BlazorLinkOnBuild>
</PropertyGroup>
مطالب
ویژگی های کمتر استفاده شده در NET. - بخش هفتم

DebuggerStepThroughAttribute

ویژگی DebuggerStepThroughAttribute باعث می‌شود که در زمان دیباگ کردن کد، با کلید F11، متدهایی که این ویژگی را دارند، بدون رفتن به داخل متد (همانند دیباگ با کلید F10 عمل می‌کند، به جز زمانی که در داخل متد break point گذاشته باشید) ، تنها اجرا می‌شوند.
به مثال زیر توجه کنید:
class Program
{
    public static void Main(string[] args)
    {
        DebuggerStepThroughMethod1();
    }

    [DebuggerStepThrough]
    public static void DebuggerStepThroughMethod1()
    {
        Console.WriteLine( "Method 1" );
        DebuggerStepThroughMethod2();
    }

    [DebuggerStepThrough]
    public static void DebuggerStepThroughMethod2()
    {
        Console.WriteLine( "Method 2" );
    }
}
و نتیجه دیباگ با استفاده از F11 به صورت زیر می‌شود:

همانطور که مشاهده می‌کنید برنامه را با کلید F11 اجرا کردم. بعد از ورود به Method1، با زدن کلید F11 دستور بعدی، break point درون Method2 است.

ConditionalAttribute

شما با استفاده از Conditional می توانید اجرای یک متد را به شناساننده پیش پردازشی ( pre-processing identifier ) وابسته کنید. ConditionalAttribute می‌تواند بر روی یک کلاس یا یک متد بکار برده شود.
class Program
{
    public static void Main(string[] args)
    {
        DebugMode();
    }

    [Conditional("DEBUG")]
    public static void DebugMode()
    {
        Console.WriteLine( "Debug mode" );
    }
}
در صورتی که مثال بالا را در حالت Debug اجرا کنید، خروجی کنسول پیام Debug mode است و در صورتی که در حالت Release اجرا کنید، متد DebugMode اجرا نخواهد شد.
نکته: شما می‌توانید با استفاده از دستور define# (در بیرون از فضای نام) مقدار سفارشی خود را تعریف کنید.
#define ReleaseMode


Flags Enum Attribute

ویژگی Flags برای پوشش فیلدهای بیتی و انجام مقایسه بیتی استفاده می‌شود. از این ویژگی باید برای زمانیکه یک داده شمارشی می‌تواند چندین مقدار را به صورت همزمان داشته باشد، استفاده کرد.
[System.Flags]
public enum Permission
{
    View = 1,
    Insert = 2,
    Update = 4,
    Delete = 8
}
این نکته خیلی مهم است که Flags به صورت خودکار، مقادیر enum را به توان دو نمی‌رساند و شما باید به صورت دستی این مقادیر را تعیین کنید. در صورتیکه مقادیر عددی را تعیین نکنید، enum در عملیات بیتی به درستی کار نخواهد کرد، چرا که مقدار enum از 0 شروع می‌شود و افزایش پیدا می‌کند.  
public static void Main( string[] args )
{
    var permission = ( Permission.View | Permission.Insert ).ToString();
    Console.WriteLine( permission ); // Displays ‘View, Insert’

    var userPermission = Permission.View | Permission.Insert | Permission.Update | Permission.Delete;
    // To retrieve the value from property you can do this
    if ( ( userPermission & Permission.Delete ) == Permission.Delete )
    {
        Console.WriteLine( "کاربر دارای مجوز دسترسی به عملیات حذف می‌باشد" );
    }

    // In .NET 4 and later
    Console.WriteLine( userPermission.HasFlag( Permission.Delete )
                            ? "کاربر دارای مجوز دسترسی به عملیات حذف می‌باشد"
                            : "کاربر مجوز دسترسی به عملیات حذف را ندارد");
}

نکته: در صورتیکه مقداری را برای enum تعریف کرده باشید، نمی‌توانید آن را با مقدار 0 مشخص کنید (در زمانی که ویژگی flags را بر روی enum اضافه کرده باشید)، چرا که با استفاده از عملیات بیتی AND نمی‌توانید دارا بودن آن مقدار را تست کنید و همیشه نتیجه صفر خواهد بود.


Dynamically Compile and Execute C# Code

CodeDOM

با استفاده از CodeDOM می‌توانید یک سورس کد را به صورت یک فایل اسمبلی کامپایل و ذخیره کنید.
public static void Main( string[] args )
{
    var sourceCode = @"class DotNetTips
                        {
                            public void Print()
                            {
                                System.Console.WriteLine("".Net Tips"");
                            }
                        }";
    var compiledAssembly = CompileSourceCodeDom( sourceCode );
    ExecuteFromAssembly( compiledAssembly );
}

static Assembly CompileSourceCodeDom( string sourceCode )
{
    CodeDomProvider csharpCodeProvider = new CSharpCodeProvider();
    var cp = new CompilerParameters
                {
                    GenerateExecutable = false
                };
    cp.ReferencedAssemblies.Add( "System.dll" );
    var cr = csharpCodeProvider.CompileAssemblyFromSource( cp,
                                                            sourceCode );
    return cr.CompiledAssembly;
}
همانطور که در مثال بالا مشاهده می‌کنید، متغیر sourceCode حاوی کد مربوط به یک کلاس #C می‌باشد که یک متد Print در آن تعریف شده است.


Roslyn

سکوی کامپایلر دات نت " Roslyn "،  کامپایلرهای متن باز #C و  VB.NET را به همراه APIهای تجزیه و تحلیل کد ارائه کرده است که با استفاده از این APIها می‌توان ابزارهای آنالیز کد جهت استفاده در ویژوال استودیو را ایجاد کرد.

برای استفاده از Roslyn باید این کتابخانه را نصب کنید

Install-Package Microsoft.CodeAnalysis

حال مثال قبل را با استفاده از Roslyn بازنویسی می‌کنیم:

public static void Main(string[] args)
{
    var sourceCode = @"class DotNetTips
                        {
                            public void Print()
                            {
                                System.Console.WriteLine("".Net Tips"");
                            }
                        }";
    var compiledAssembly = CompileSourceRoslyn( sourceCode );
    ExecuteFromAssembly( compiledAssembly );
}

private static Assembly CompileSourceRoslyn(string sourceCode)
{
    using ( var memoryStream = new MemoryStream() )
    {
        var assemblyFileName = string.Concat( Guid.NewGuid().ToString(),
                                                ".dll" );
        var compilation = CSharpCompilation.Create( assemblyFileName,
                                                    new[]
                                                    {
                                                        CSharpSyntaxTree.ParseText( sourceCode )
                                                    },
                                                    new[]
                                                    {
                                                        MetadataReference.CreateFromFile( typeof( object ).Assembly.Location )
                                                    },
                                                    new CSharpCompilationOptions( OutputKind.DynamicallyLinkedLibrary ) );
        compilation.Emit( memoryStream );
        var assembly = Assembly.Load( memoryStream.GetBuffer() );
        return assembly;
    }
}

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

static void ExecuteFromAssembly( Assembly assembly )
{
    var helloKittyPrinterType = assembly.GetType( "DotNetTips" );
    var printMethod = helloKittyPrinterType.GetMethod( "Print" );
    var kitty = assembly.CreateInstance( "DotNetTips" );
    printMethod.Invoke( kitty,
                        BindingFlags.InvokeMethod,
                        null,
                        null,
                        CultureInfo.CurrentCulture );
}


مطالب
Ef 6 و Ngen : شروعی سریعتر برای برنامه های مبتنی بر Entity Framework
تولید کد Native زمانی اتفاق می‌افتد که کامپایلر JIT، کد اسمبلی‌های MSIL را به کدهای Native در ماشین محلی کامپایل می‌کند و این عمل بلافاصله قبل از اجرای متد برای اولین بار اتفاق می‌افتد. این کد به صورت موقتی بوده و در حافظه‌ای که برای پردازش در نظر گرفته شده ذخیره می‌شود و در پایان هر پردازش توسط سیستم عامل ویرایش می‌شود. کد Native به ازای هر بار شروع یک پردازش تولید می‌شود. ابزار Native Image Generator یا همان Ngen اقدام به تولید کد Native با استفاده از کامپایلر JIT نموده و آن را در هارد دیسک ذخیره می‌نماید. زمانیکه برنامه نیازمند یک اسمبلی CLR است، به جای بارگذاری خود اسمبلی، ایمیج کد Native آن بارگذاری می‌شود. به این نکته نیز توجه داشته باشید که CLR اطلاعاتی در مورد اینکه کدام اسمبلی، ایمیج کد Native است و این ایمیج در کجا و در چه زمانی تهیه شده است، دارد. کد Native باعث بهبود استفاده از حافظه می‌شود، زمانیکه یک اسمبلی بین پروسس‌ها به اشتراک گذاشته شده‌است. تا قبل از EF6 کتابخانه‌های هسته‌ای EF در زمان اجرا جزئی از دات نت فریمورک بودند و تولید کد Native آنها به صورت اتوماتیک انجام می‌شد. اما از نسخه 6، تمامی این کتابخانه‌ها در داخل پکیج Nuget آن ترکیب شده‌اند . پس برای تولید کد Native مربوط به فایل EntityFramework.dll نیازمند ابزار Ngen هستیم.
1- ابتدا یک برنامه‌ی ساده کنسول ویندوز ساخته و از Package Manager Console دستور Install-package entityframework را اجرا نموده تا پکیج Ef به برنامه اضافه گردد.
using System;
using System.Data.Entity;

namespace UsingNgen
{

    public class NgenDbContex : DbContext
    { }

    class Program
    {
        static void Main()
        {
            var nGenCtx = new NgenDbContex();
            Console.WriteLine("Press a key to exit...");
            Console.ReadKey();
        }
    }


}
حال کد ساده بالا را به برنامه اضافه می‌کنیم و برنامه را Build میکنیم.
2- برای ثبت جزئیات اجرای برنامه از ابزار Windows Performance Recorder که جزئی از ویندوز می‌باشد، استفاده می‌کنیم. کافیست عبارت WPR را در نوار جستجوی ویندوز تایپ کنید تا این ابزار در دسترس قرار گیرد. 



برای ضبط جزئیات، روی دکمه‌ی Start کلیک کنید و به محل ذخیره‌ی فایل اجرایی حاصل از Build ویژوال استودیو رفته و آن را اجرا کنید. بعد از اتمام اجرا، جزئیات را ذخیره نمایید.

بعد از ذخیره فایل، در پنجره بالا دکمه‌ای به نام Open in WPA ظاهر می‌شود. WPA مخفف Windows Performance Analyzer می‌باشد. آن را کلیک کنید تا محیط آنالایزر باز شود.

حال در سمت چپ این پنجره انواع آنالایزرها را مشاهده می‌کنید. روی آنالایزر Computation کلیک کنید و از زیرمجموعه‌ی آن، CPU Usage را انتخاب کنید. آمار مربوط به برنامه خودمان را در تصویر بالا مشاهده می‌کنید. کل برنامه 164 میلی ثانیه زمان برده و فایل Clr.dll حدود 47 میلی ثانیه و یک فایل clrjit.dll نیز برای تولید کد JIT وجود دارد. حال برای تسریع در عمل شروع، از تکنیک Ngen به صورت زیر استفاده می‌کنیم.

3- دوباره به نوار جستجوی ویندوز رفته و ابزار Developer Command Prompt for VsXXXX را با امتیاز دسترسی از نوع Admin اجرا کنید. XXXX نسخه‌ی ویژوال استودیو می‌باشد.

حال به محل ذخیره فایل اجرایی برنامه رفته و دستور Ngen Install EntityFramework.dll را تایپ کنید تا یک ایمیج کد Native از entityframework.dll ساخته شود. دوباره ابزار Windows Performance Recorder را لود کرده و روی دکمه Start کلیک کنید و فایل اجرایی برنامه را اجرا نمایید. پس از اتمام عملیات ثبت جزئیات، آن را در Windows Performance Analyzer باز نمایید.

همانطور که مشاهده می‌کنید کل برنامه ما 89 میلی ثانیه زمان برده و Clr.dll 29 ثانیه و به جای clrjit.dll فایل EntityFramework به صورت native تولید شده است.

مطالب
آشنایی با CLR: قسمت بیست و یکم

آغاز فصل سوم:

در فصل گذشته در مورد بسته بندی و توزیع اسمبلی‌ها، بررسی‌هایی را انجام دادیم. در این نوع توزیع، فرض ما بر این بود که دسترسی به اسمبلی‌ها، از طریق دایرکتوری خود اپلیکیشن می‌باشد؛ ولی برای اسمبلی‌های عمومی، صحبتی به میان نیاوردیم. در این فصل، ما تمرکز خود را برای توزیع اسمبلی‌های عمومی می‌گذاریم. اسمبلی‌های عمومی این قابلیت را می‌دهند که از طریق چند اپلیکیشن قابل دسترسی باشند. ساده‌ترین و قابل دسترس‌ترین نمونه‌ی این اسمبلی‌ها، اسمبلی‌های خود دات نت فریم ورک هستند؛ یا نمونه‌ی دیگر، شرکت‌های ثالثی مثل تلریک، که برای استفاده‌ی دیگر برنامه نویسان اسمبلی می‌سازند.

مشکلی که در توزیع اسمبلی‌های عمومی وجود دارد این است که شما باید این اطمینان را کسب کنید که اسمبلی شما، همیشه همان اسمبلی خواهد بود و تغییری در آن رخ نخواهد داد. فرض کنید که شما از یک اسمبلی که توسط شرکت تلریک تهیه شده است استفاده کرده‌اید و برنامه‌ی شما به خوبی با آن کار می‌کند. ولی چه اتفاقی می‌افتد که اگر برنامه‌ی دیگری بعد از شما نصب شود و از همان اسمبلی، منتها از نسخه‌ی دیگر آن استفاده می‌کند؟ بله برنامه‌ی شما احتمال زیادی دارد که در این حالت به مشکل بر بخورد یا اینکه شخص دیگری یک اسمبلی دیگری همنام اسمبلی و هم نسخه‌ی اسمبلی شما تولید می‌کند. برای رفع این مشکلات مایکروسافت تمهیداتی را اندیشیده است که ما به آن می‌گوییم «اسمبلی با نام قوی Strong Name Assembly».

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

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

حال یک اسمبلی نام قوی، دارای چهار خصوصیت است: نام اسمبلی بدون پسوند، نگارش، فرهنگ (Culture) و کلید عمومی.

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

"MyTypes, Version=1.0.8123.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
"MyTypes, Version=1.0.8123.0, Culture="en­US", PublicKeyToken=b77a5c561934e089" 
"MyTypes, Version=2.0.1234.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
"MyTypes, Version=1.0.8123.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

استفاده از فضای نام

System.Reflection.AssemblyName

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

از آنجا که مطالب مربوطه به  امضاء کردن اسمبلی، در سایت جاری موجود می‌باشند، این مباحث را می‌توانید از طریق این مقاله "نام قوی" دنبال کنید تا در این باره گزافه گویی نکرده باشیم .

مکانیزم اعمال این امضاء بدین شکل است که بعد از ساخت فایل PE که شامل مانیفست است، کل محتویات آن ( به جز امضاهای شناسایی و هدر (Checksum) و اطلاعات مربوط به نام قوی) هش می‌شوند و سپس این مقدا هش شده توسط کلید خصوصی امضاء می‌شود و در همان PE، در بخش هش نشده‌ای به نام Reserved Section ذخیره می‌گردند و در نهایت CLR Header برای ارتباط با این بخش آپدیت می‌شود. 

تصویر زیر توضیح بند بالا را نشان می‌دهد:

 

مختصری در مورد GAC

اگر مقاله‌ی بالا را خوانده باشید، الان باید بدانید که GAC، وظیفه‌ی رجیستر و توزیع اسمبلی نام قوی را بر عهده دارد؛ ولی چرا باید اینکار را به GAC، واگذار کنیم؟ فرض کنید دو اسمبلی با نام‌های dotnettips.dll وجود دارند. اگر قرار باشد هر دو داخل یک دایرکتوری قرار بگیرند، مسلما اسمبلی دوم بر روی اسمبلی اولی overwrite خواهد شد. درنتیجه کاری که GAC انجام می‌دهد، جلوگیری از این اتفاق است و GAC مدیریت می‌کند که در داخل مسیر
%SystemRoot%\Microsoft.NET\Assembly
زیرشاخه هایی ایجاد شوند و اسمبلی‌ها در آن‌ها قرار بگیرند.

هر اسمبلی را که توزیع می‌کنید، حتما حداقل به یک اسمبلی نام قوی ارجاع خواهد داشت؛ دلیل این گفته هم وجود فضای نام system.object در اسمبلی mscorlib است. برای همین، موقعیکه شما با csc، کامپایل می‌کنید، از سوئیچ reference استفاده می‌شود تا ارجاعی را به نام اسمبلی مورد نظر داشته باشد. اگر نام اسمبلی به طور کامل به همراه مسیر ذکر شود که مستقیما از همانجا فراخوانی می‌شود؛ در غیر این صورت مسیرهای زیر مورد بررسی قرار می‌گیرند:
  1. مسیر پوشه‌ی کاری برنامه
  2. مسیر کامپایلر CSC
  3. هر مسیری که با سوئیچ lib به کامپایلر معرفی کرده باشید.
  4. هر مسیری که با متغیرهای Lib Environment مشخص شده باشند.
بنابراین اگر شما مثلا از یک اسمبلی مانند system.drawing استفاده کرده باشید، کمپایلر به طور خودکار دستور زیر را صادر می‌کند:
/reference:System.Drawing.dll
و همانطور که گفته شد، کامپایلر شروع به اسکن دایرکتوری‌ها می‌کند و در نهایت آن را در مسیر خود کامپایلر، یعنی مورد 2 پیدا می‌کند. برنامه کمپایل می‌شود، ولی این اسمبلی همان اسمبلی نیست که برنامه در زمان اجرا، مورد استفاده قرار می‌دهد؛ چون این اسمبلی یک اسمبلی نام قوی است و انتظار می‌رود تا در مسیر مورد تایید GAC یافت شود. به همین دلیل است که موقع نصب دات نت فریم ورک، از هر اسمبلی، دو نسخه بر روی سیستم کپی می‌شود. یکی در مسیر کامپایل و دیگری برای GAC.
در ضمن اسمبلی‌های موجود در مسیر کامپایلر به هیچ عنوان اهمیتی به نوع ماشین نمی‌دهند؛ چون متادیتاهای اسمبلی آن‌ها اهمیت دارند نه کد IL آن‌ها. کد IL فقط در زمان اجرا، برای ما اهمیت دارد و در زمان کامپایل، همان متادیتا کفایت می‌کند و در نهایت برنامه موقع اجرا، با توجه به نوع ماشین x86,x36,ARM، اسمبلی مورد نیاز خود را از طریق GAC فراهم می‌کند که هر یک از اسمبلی‌های مخصوص هر ماشین، توسط GAC، در داخل زیر شاخه‌های مخصوص خود قرار گرفته‌اند.
مطالب
یکی کردن اسمبلی‌ها با استفاده از Eazfuscator

نسخه جدید برنامه Eazfuscator به همراه دو قابلیت جالب یکی کردن و همچنین مدفون نمودن اسمبلی‌ها ارائه شده است:

یکی کردن چند اسمبلی با هم
Eazfuscator برای یکی کردن اسمبلی‌ها از برنامه معروف ILmerge استفاده می‌کند با این تفاوت که دیگر نیازی نیست تا پارامترهای آن‌را تنظیم کرد و بسیاری از مسایل را به صورت خودکار مدیریت می‌کند.
جهت فعال کردن این قابلیت، یکی از روش‌های کار به صورت زیر است:
فایلی به نام ObfuscationSettings.cs را به پروژه خود اضافه کرده، سپس محتویات آن‌را حذف نموده و با چند سطر زیر جایگزین و کامپایل کنید:
using System;
using System.Reflection;

[assembly: Obfuscation(Feature = "merge with file1.dll", Exclude = false)]
[assembly: Obfuscation(Feature = "merge with file2.dll", Exclude = false)]
[assembly: Obfuscation(Feature = "merge with file3.dll", Exclude = false)]

همانطور که ملاحظه می‌کنید این چند سطر حاوی نام اسمبلی‌هایی می‌باشند که قرار است با اسمبلی جاری یکی شوند.
سپس اسمبلی جاری را (می‌خواهد فایل exe باشد یا یک dll ، فرقی نمی‌کند) بر روی Eazfuscator کشیده و رها کنید. پس از چند لحظه اسمبلی نهایی تولید شده شامل تمام کلاس‌ها و منابع اسمبلی‌هایی خواهد بود که در فایل ObfuscationSettings.cs ذکر شده‌اند؛ به همراه Obfuscation خودکار آن‌ها.

مدفون کردن اسمبلی‌ها در یک اسمبلی
قابلیت دیگر این برنامه دفن (embedding) چند اسمبلی در اسمبلی نهایی است. برای فعال سازی آن روش کار همانند قبل است با این تفاوت که بجای merge with باید نوشت embed . برای مثال:
[assembly: Obfuscation(Feature = "embed Common.dll", Exclude = false)]

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

تفاوت مدفون کردن با یکی کردن چیست؟
در حالت یکی کردن اسمبلی‌ها، سربار اولیه بارگذاری برنامه همانند روش مدفون سازی وجود ندارد. اما این سربار آنقدر ناچیز است که کسی آن‌را احساس نخواهد کرد. مورد دیگر، عدم پشتیبانی از روش مدفون سازی در سایر سکوهای کاری مانند ویندوز فون، Compact Framework و غیره است. اما باید درنظر داشت که برای مثال ILMerge روی اسمبلی‌های دارای XAML کار نمی‌کند (مطابق مستندات رسمی آن). بنابراین همیشه نمی‌توان از روش یکی سازی استفاده کرد و محدودیت‌های خاص خودش را دارد.
در کل روش مدفون سازی به دلیل Obfuscation ، فشرده سازی و رمزنگاری همزمان، امنیت بیشتری را نسبت به حالت Obfuscation تنها ارائه می‌دهد (حداقل شخص "علاقمند" به مطالعه این نوع اسمبلی‌ها باید از چند لایه رد شود و تجربه برنامه LINQPad ثابت کرده که این روش در مقیاس کلان (در انظار عمومی هزاران علاقمند) بسیار موفق بوده است).