نظرات مطالب
بهینه سازی برنامه‌های وب ASP.NET برای موتورهای جستجو (SEO)
- ابتدا باید اسمبلی استاندارد System.ComponentModel.DataAnnotations به ارجاعات پروژه اضافه شود.
- سپس باید از فضای نام مرتبط استفاده کنید (و نه از فضاهای نام مشابه)
enum MyEnum {
    [System.ComponentModel.Description("Blah")]
    MyValue
}
نظرات مطالب
EF Code First #12
سلام آقای نصیری من در حال نوشتن یک پروژه ویندوزی با روشهای گفته شده در این سایت هستم، منظور استفاده از EF Code First و StructureMap و ... می‌باشد. Base برنامه نوشته شده و شامل یک پروژه مرکزی- پروژه حسابداری مالی – حقوق دستمزد- خرید وفروش و.... می‌باشد، کاربران فقط و فقط از طریق اجرای سیستم مرکزی قادر به ورود به سیستم‌های نرم افزاری حوزه خود می‌باشند، با توجه به مطالب فوق الذکر و پرسش و پاسخ‌های کلیه دوستان، متوجه شدم که فقط باید از یک Context استفاده کرد، درسته؟ در حالیکه از بین این چند نرم افزار شاید شرکتی تنها قصد خرید واستفاده سیستم مالی وشرکتی دیگر سیستم مالی وn تای دیگر از برنامه‌ها رو خرید و مورد استفاده قرار دهد، و چون فعلا کلیه عملیات مرتبط با بانک اطلاعاتی در یک Context ودر MainProject انجام میشه، شرکتی که از یک نرم افزار من استفاده خواهد کرد همان ساختار جداول و بانک اطلاعاتی را دارد که شرکتی دیگر از چند نرم افزار دیگر از همین پروژه استفاده میکند. درکل نکته مبهم برام اینست که با چه تکنیک و روشی می‌توان از uow استفاده و پیروی کرد ولی هر پروژه Context خودش رو داشته باشد که با ورود به آن زیرسیستم، عملیات ساخت یا ویرایش DataBase و جداول مرتبط فقط بر روی آن زیر سیستم انجام شود و هیچ تاثیری رو جداول سیستم‌های دیگر نداشته باشد؟ (درضمن کلیه سیستم‌های از یک DataBase استفاده خواهند کرد). با تشکر
مطالب دوره‌ها
استفاده از IL Code Weaving برای تولید ویژگی‌های تکراری مورد نیاز در WCF
با استفاده از IL Code Weaving علاوه بر مدیریت اعمال تکراری پراکنده در سراسر برنامه مانند ثبت وقایع، مدیریت استثناءها، کش کردن داده‌ها و غیره، می‌توان قابلیتی را به کدهای موجود نیز افزود. برای مثال یک برنامه معمول WCF را درنظر بگیرید.
using System.Runtime.Serialization;

namespace AOP03.DataContracts
{
    [DataContract]
    public class User
    {
        [DataMember]
        public int Id { set; get; }

        [DataMember]
        public string Name { set; get; }
    }
}
نیاز است کلاس‌ها و خواص آن توسط ویژگی‌های DataContract و DataMember مزین شوند. در این بین نیز اگر یکی فراموش گردد، کار دیباگ برنامه مشکل خواهد شد و در کل حجم بالایی از کدهای تکراری در اینجا باید در مورد تمام کلاس‌های مورد نیاز انجام شود. در ادامه قصد داریم تولید این ویژگی‌ها را توسط PostSharp انجام دهیم. به عبارتی یک پوشه خاص به نام DataContracts را ایجاد کرده و کلاس‌های خود را به نحوی متداول و بدون اعمال ویژگی خاصی تعریف کنیم. در ادامه پس از کامپایل آن، به صورت خودکار با ویرایش کدهای IL توسط PostSharp، ویژگی‌های لازم را به اسمبلی نهایی اضافه نمائیم.


تهیه DataContractAspect جهت اعمال خودکار ویژگی‌های DataContract و DataMember

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using PostSharp.Aspects;
using PostSharp.Extensibility;
using PostSharp.Reflection;

namespace AOP03
{
    [Serializable]
    //این ویژگی تنها نیاز است به کلاس‌ها اعمال شود
    [MulticastAttributeUsage(MulticastTargets.Class)]
    public class DataContractAspect : TypeLevelAspect, IAspectProvider
    {
        public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
        {
            var targetType = (Type)targetElement; //همان نوعی است که ویژگی جاری به آن اعمال خواهد شد

            //این سطر معادل است با درخواست تولید ویژگی دیتاکانترکت
            var introduceDataContractAspect = new CustomAttributeIntroductionAspect(
                new ObjectConstruction(typeof(DataContractAttribute).GetConstructor(Type.EmptyTypes)));

            //این سطر معادل است با درخواست تولید ویژگی دیتاممبر
            var introduceDataMemberAspect = new CustomAttributeIntroductionAspect(
                new ObjectConstruction(typeof(DataMemberAttribute).GetConstructor(Type.EmptyTypes)));

            //در اینجا کار اعمال ویژگی دیتاکانترکت به کلاسی که به عنوان پارامتر متد جاری
            //دریافت شده انجام خواهد شد
            yield return new AspectInstance(targetType, introduceDataContractAspect);

            //مرحله بعد کار اعمال ویژگی دیتاممبر به خواص کلاس است
            foreach (var property in targetType.GetProperties(BindingFlags.Public |
                                                          BindingFlags.DeclaredOnly |
                                                          BindingFlags.Instance))
            {
                if (property.CanWrite)
                    yield return new AspectInstance(property, introduceDataMemberAspect);
            }
        }
    }
}
توضیحات مرتبط با قسمت‌های مختلف این Aspect سفارشی، به صورت کامنت در کدهای فوق ارائه شده‌اند.
برای اعمال آن به سراسر برنامه تنها کافی است به فایل AssemblyInfo.cs پروژه مراجعه و سپس سطر زیر را به آن اضافه کنیم:
 [assembly: DataContractAspect(AttributeTargetTypes = "AOP03.DataContracts.*")]
به این ترتیب در زمان کامپایل پروژه، Aspect تعریف شده به تمام کلاس‌های موجود در فضای نام AOP03.DataContracts اعمال خواهند شد.

در این حالت اگر کلیه ویژگی‌های کلاس User فوق را حذف و برنامه را کامپایل کنیم، با مراجعه به برنامه ILSpy می‌توان صحت اعمال ویژگی‌ها را به کمک PostSharp بررسی کرد:
 

نظرات مطالب
غنی سازی کامپایلر C# 9.0 با افزونه‌ها
یک نکته‌ی تکمیلی: چگونه اخطارهای code styleها را هم در پروسه‌ی Build وارد کنیم؟

Roslyn analyzers به همراه پیشنهادهایی در مورد code styles هم هستند؛ برای مثال توصیه‌ی به استفاده‌ی از var و یا عکس آن. می‌توان این توصیه‌ها را تبدیل به اجبار هم کرد تا در یک تیم، code styles یکدستی ارائه شود و روش آن، وارد کردن این توصیه‌ها به پروسه‌ی build است. برای اینکار می‌توان فایل csproj را به نحو زیر ویرایش کرد:
<PropertyGroup>
   <TargetFramework>net6.0</TargetFramework>

   <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
اکنون با فعالسازی EnforceCodeStyleInBuild، یک مرحله‌ی دیگر نیز باقی است. باید به فایل editorconfig. مراجعه و تنظیم زیر را به آن افزود:
[*.cs]
# Default severity for analyzer diagnostics with category 'Style' (escalated to build warnings)
dotnet_analyzer_diagnostic.category-Style.severity = warning
پس از این تنظیمات، توصیه‌های مرتبط با code-styles، به صورت اخطارهای کامپایلر ظاهر می‌شوند و یا حتی اگر علاقمند بودید، می‌توان مقدار warning فوق را به error نیز تغییر داد تا دیگر در صورت وجود توصیه‌ی خاصی، برنامه کامپایل نشود.

بدیهی است در این صورت، روش لغو توصیه‌ی خاصی به صورت زیر است:
 dotnet_diagnostic.IDE0008.severity = none
IDE0008 برای نمونه شماره‌ی یکی از توصیه‌هایی است که با آن موافق نیستید. در این حالت فقط کافی است severity آن‌را به none تنظیم کنید تا دیگر در پروسه‌ی build ظاهر نشود.
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 17 - بررسی فریم ورک Logging
ASP.NET Core به همراه یک فریم ورک توکار ثبت وقایع (Logging) ارائه شده‌ی توسط تزریق وابستگی‌ها است که به صورت پیش فرض نیز فعال است.


این تصویر را پیشتر در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 6 - سرویس‌ها و تزریق وابستگی‌ها» مشاهده کرده‌اید. در اینجا لیست سرویس‌هایی را مشاهده می‌کنید که به صورت پیش فرض، ثبت شده‌اند و فعال هستند و ILogger و ILoggerFactory نیز جزئی از آن‌ها هستند. بنابراین نیازی به فعال سازی آن‌ها نیست؛ اما برای استفاده‌ی از آن‌ها نیاز به انجام یک سری تنظیمات است.


پیاده سازی ثبت وقایع در ASP.NET Core

اولین قدم کار با فریم ورک ثبت وقایع ASP.NET Core، معرفی ILoggerFactory به متد Configure کلاس آغازین برنامه است:
public void Configure(ILoggerFactory loggerFactory, IApplicationBuilder app, IHostingEnvironment env)
{
   loggerFactory.AddConsole(Configuration.GetSection("Logging"));
   loggerFactory.AddDebug();
متد Configure امضای مشخصی را ندارد و در اینجا به هر تعداد سرویسی که نیاز باشد، می‌توان اینترفیس‌های آن‌ها را جهت تزریق وابستگی‌های متناظر توسط IoC Containser توکار ASP.NET Core، معرفی کرد. در اینجا برای تنظیم ویژگی‌های سرویس ثبت وقایع، تزریق وابستگی ILoggerFactory  صورت گرفته‌است.
سطر اول متد، تنظیمات ثبت وقایع را از خاصیت Logging فایل appsettings.json برنامه می‌خواند (در مورد خاصیت Configuration، در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 7 - کار با فایل‌های config» بیشتر بحث شد) و لاگ کردن ویژه‌ی در کنسول NET Core. را فعال می‌کند:
{
    "Logging": {
        "IncludeScopes": false,
        "LogLevel": {
            "Default": "Debug",
            "System": "Information",
            "Microsoft": "Information"
        }
    }
}
در مورد Log Level و یا سطوح ثبت وقایع، در ادامه‌ی مطلب بحث خواهد شد.

و سطر دوم سبب نمایش اطلاعات لاگ شده در کنسول دیباگ ویژوال استودیو می‌شود.
متد AddDebug برای شناسایی، نیاز به افزودن وابستگی‌های ذیل در فایل project.json برنامه را دارد:
{
    "dependencies": {
        //same as before 
        "Microsoft.Extensions.Logging": "1.0.0",
        "Microsoft.Extensions.Logging.Console": "1.0.0",
        "Microsoft.Extensions.Logging.Debug": "1.0.0" 
    }
}
پس از این تنظیمات، برنامه را اجرا کنید.


در اینجا می‌توانید ریز وقایعی را که توسط ASP.NET Core لاگ شده‌است، مشاهده کنید. برای مثال چه درخواستی صورت گرفته‌است و چقدر اجرای آن زمان‌برده‌است.
این فعال سازی مرتبط است به متد AddDebug که اضافه شد. اگر می‌خواهید خروجی AddConsole را هم مشاهده کنید، از طریق خط فرمان، به پوشه‌ی اصلی پروژه وارد شده و سپس دستور dotnet run را اجرا کنید:


دستور dotnet run سبب راه اندازی وب سرور برنامه بر روی پورت 5000 شده‌است که در تصویر نیز مشخص است.
بنابراین اینبار برای دسترسی به برنامه باید مسیر http://localhost:5000 را در مرورگر خود طی کنید. در اینجا نیز می‌توان حالت‌های مختلف اطلاعات لاگ شده را مشاهده کرد و تمام این‌ها مرتبط هستند به ذکر متد AddConsole .


کار با سرویس ثبت وقایع ASP.NET Core از طریق تزریق وابستگی‌ها

برای کار با سرویس ثبت وقایع توکار ASP.NET Core در قسمت‌های مختلف برنامه، می‌توان از ترزیق وابستگی ILogger آن استفاده کرد:
[Route("[controller]")]
public class AboutController : Controller
{
    private readonly ILogger<AboutController> _logger;
 
    public AboutController(ILogger<AboutController> logger)
    {
        _logger = logger;
    }
 
    [Route("")]
    public ActionResult Hello()
    {
        _logger.LogInformation("Running Hello");
        return Content("Hello from DNT!");
    }
در این کنترلر، وابستگی اینترفیس ILogger با پارامتری از نوع کنترلر جاری به سازنده‌ی کلاس تزریق شده‌است. علت ذکر این پارامتر جنریک این است که ILoggerFactory بداند چگونه باید متد CreateLogger خود را در پشت صحنه وهله سازی کند.
سپس با توجه به اینکه این سرویس جزو سرویس‌های از پیش ثبت شده‌ی ASP.NET Core است، امکانات آن بدون نیاز به تنظیمات بیشتری در دسترس است. برای مثال از متد LogInformation آن در اکشن متد Hello استفاده شده‌است و خروجی عبارت لاگ شده‌ی آن‌را در اینجا می‌توانید مشاهده کنید:



سطوح مختلف ثبت وقایع

اینترفیس ILogger به همراه متدهای مختلفی است؛ مانند LogError، LogDebug و غیره. معانی آن‌ها به شرح زیر هستند:
Debug (1): ثبت واقعه‌ای است با بیشترین حد جزئیات ممکن که عموما شامل اطلاعات حساسی نیز می‌باشد. بنابراین نباید در حالت ارائه‌ی نهایی برنامه فعال شود.
(2) Verbose: ثبت وقایعی مفصل، جهت بررسی مشکلات در حین توسعه‌ی برنامه. تنها باید حاوی اطلاعاتی برای دیباگ برنامه باشند.
(3) Information: عموما برای ردیابی قسمت‌های مختلف برنامه مورد استفاده قرار می‌گیرند.
(4) Warning: جهت ثبت واقعه‌ای نامطلوب در سیستم بکار می‌رود و سبب قطع اجرای برنامه نمی‌شود.
(5) Errors: مشکلات برنامه را که سبب قطع سرویس دهی آن شده‌اند را ثبت می‌کند. هدف آن ثبت مشکلات واحد جاری است و نه کل برنامه.
Critical (6): هدف آن ثبت مشکلات بحرانی کل سیستم است که سبب از کار افتادن آن شده‌اند.

برای مثال در حین تنظیم متد AddDebug که سبب نمایش اطلاعات لاگ شده در کنسول دیباگ ویژوال استودیو می‌شود، می‌توان حداقل سطح ثبت وقایع را نیز ذکر کرد:
 loggerFactory.AddDebug(minLevel: LogLevel.Information);
این حداقل مرتبط است با اعدادی که در کنار سطوح فوق ملاحظه می‌کنید. برای مثال اگر حداقل سطح ثبت وقایع به Information تنظیم شود، چون سطح آن 3 است، دیگر سطوح پایین‌تر از آن لاگ نخواهند شد. اهمیت این مساله در اینجا است که اگر صرفا نیاز به اطلاعات Critical داشتیم، نیازی نیست تا با انبوهی از اطلاعات لاگ شده سر و کار داشته باشیم و به این ترتیب می‌توان حجم اطلاعات نمایش داده شده را کاهش داد.

البته ترتیب واقعی این سطوح را در enum مرتبط با آن‌ها بهتر می‌توان مشاهده کرد:
  public enum LogLevel
  {
    Trace,
    Debug,
    Information,
    Warning,
    Error,
    Critical,
    None,
  }

یک نکته: زمانیکه متد AddDebug را بدون پارامتر فراخوانی می‌کنید، حداقل سطح ثبت وقایع آن به Information تنظیم شده‌است. یعنی در این لاگ، خبری از اطلاعات Debug نخواهد بود (چون سطح دیباگ پایین‌تر است از Information).  بنابراین اگر می‌خواهید این اطلاعات را هم مشاهده کنید باید پارامتر minLevel آن‌را به LogLevel.Debug تنظیم نمائید.


امکان استفاده‌ی از پروایدرهای ثبت وقایع ثالث

تا اینجا، دو نمونه از پروایدرهای توکار ثبت وقایع ASP.NET Core را بررسی کردیم. اگر نیاز به ثبت این اطلاعات با فرمت‌های مختلف و یا در بانک اطلاعاتی وجود دارد، می‌توان به تامین کننده‌های ثالثی که قابلیت کار با ILoggerFactory را دارند نیز مراجعه کرد. برای مثال:
- elmah.io - provider for the elmah.io service
- Loggr - provider for the Loggr service
- NLog - provider for the NLog library
- Serilog - provider for the Serilog library
مطالب
تولید فایل Word بدون نصب MS Word بر روی سرور

یکی از مواردی که ممکن است در محیط کاری با آن برخورد داشت، تقاضای تولید فایل word یک گزارش با فرمتی مشخص از یک برنامه ASP.Net است. برای مثال یک قالب درست کرده‌اند که header‌ و footer و کلا یک فرمت رسمی دارد. الان برنامه شما باید این فایل word رسمی را با گزارشی که تولید می‌کند پر کند. حالا اینجاست که گرفتاری برنامه نویس شروع می‌شود! روی سرور باید word نصب باشد تا توسط اشیاء COM آن بتوان یک چنین کارهایی را آن‌هم با ASP.Net که به صورت پیش فرض کمترین سطح دسترسی را روی سیستم دارد انجام داد. یا اینکه باید به سراغ کامپوننت‌های تجاری رفت و حالا اینجا با این وضع تحریم و غیره چگونه بتوان آنها را خریداری کرد یا شاید احتمالا در سایت‌های وارز بتوان نسخه تکه پاره شده آنها را یافت. مشکلی هم که این نوع کامپوننت‌ها دارند این است که ممکن است سال دیگر اصلا ساپورت نشوند. محصولات مایکروسافت هم که مرتبا در حال به روز رسانی هستند. در این حالت برنامه متکی به این نوع کامپوننت‌های تجاری سورس بسته در همان نگارش قبلی خود مجبور است باقی بماند.
خوشبختانه با ارائه آفیس 2007 و فرمت OpenXML فایلهای آن، این مشکل تقریبا مرتفع شده است. مایکروسافت نیز برای سهولت تولید این نوع اسناد، OpenXML SDK را ارائه داده است که از آدرس زیر قابل دریافت است:
Open XML Format SDK 1.0

البته پیش نمایش نگارش دو SDK آن نیز موجود است که در مطلب جاری به آن پرداخته نخواهد شد.

فایل‌های office 2007 از یک فایلzip تشکیل شده از چند فایل xml داخل آن، ایجاد شده‌اند. برای مثال یک فایل docx را با winrar یا امثال آن باز کنید (تصویر زیر):



برای کار با اینگونه اسناد باید با اصطلاحات زیر آشنا شد:
Package : فایل zip شما (همان فایل برای مثال docx) اینجا یک بسته نام دارد.
Parts : اجزای این بسته که همان فایل‌های آن هستند، parts نامیده شده اند.
Relations : اگر به فایل‌های موجود در یک بسته دقت کنید، فایلهایی با پسوند rels را خواهید دید که بیانگر نحوه ارتباط Parts با یکدیگر هستند.
Relations Ids: هر ارتباط با یک ID منحصربفرد تعریف می‌گردد.

اگر علاقمند باشید که پوستری را در این رابطه مشاهده نمائید می‌توان به آدرس زیر مراجعه نمود.
Open XML Developer Map

نحوه استفاده از OpenXML SDK در دات نت:
ابتدا باید ارجاعی را به فایل DocumentFormat.OpenXml.dll که پس از نصب در مسیر OpenXMLSDK\1.0.1825\lib قرار گرفته است به پروژه افزود. سپس نیاز است تا ارجاعی به کتابخانه WindowsBase نیز به برنامه افزوده شود (تصویر زیر). افزودن ارجاعی به این کتابخانه جهت کامپایل برنامه ضروری است (شکل زیر).


تا اینجا ارجاعات برنامه به صورت زیر خواهند بود:



یک مثال ساده:
قصد داریم یک فایل docx ساده را با استفاده از OpenXML SDK ایجاد کنیم. در مثال زیر فرمت متغیر docXml را می‌توان با ایجاد یک فایل docx ساده در word و سپس باز کردن بسته فشرده شده آن و مشاهده محتوای فایل word\document.xml بدست آورد.
using System.IO;
using System.Text;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;

namespace OpenXMLTestApp
{
class CWord
{

public static void CreateDocument(string documentFileName, string text)
{
using (WordprocessingDocument wordDoc =
WordprocessingDocument.Create(documentFileName, WordprocessingDocumentType.Document))
{
MainDocumentPart mainPart = wordDoc.AddMainDocumentPart();

string docXml =
@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
<w:document xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
<w:body><w:p><w:r><w:t>#REPLACE#</w:t></w:r></w:p></w:body>
</w:document>";

docXml = docXml.Replace("#REPLACE#", text);

using (Stream stream = mainPart.GetStream())
{
byte[] buf = (new UTF8Encoding()).GetBytes(docXml);
stream.Write(buf, 0, buf.Length);
}
}
}
}
}

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

CWord.CreateDocument("test.docx", "سلام دنیا");

این کتابخانه کار ایجاد فایل‌های xml ، تولید روابط بین‌ آنها و همچنین بسته بندی و zip کردن نهایی را به صورت خودکار انجام می‌دهد.

برای مطالعه بیشتر می‌توان به منابع زیر مراجعه نمود:

یک ویدیوی آموزشی رایگان از مایکروسافت
دریافت

سؤالات متداول در MSDN
http://msdn.microsoft.com/en-us/library/bb491088.aspx
البته اگر پس از نصب SDK به پوشه doc آن مراجعه نمائید، این سؤال و جواب‌ها را در فایل راهنمای chm آن نیز می‌توان پیدا کرد.

مثال دیگری در مورد ایجاد یک گزارش از بانک اطلاعاتی و گرفتن خروجی docx از آن
http://openxmldeveloper.org/articles/GenerateWordTable.aspx
البته این مثال خیلی قدیمی است و قسمت‌های کار با پکیج را با SDK‌ ارائه شده می‌توان به صورت خودکار انجام داد. اما حداقل نحوه تولید جداول استاندارد OpenXML را می‌توان از آن ایده گرفت.

مثالی در مورد نحوه قرار دادن عکس در فایل docx تولیدی

همچنین مثال‌های بیشتری را در وبلاگ‌های مربوطه می‌توان یافت:
http://blogs.msdn.com/brian_jones/
http://blogs.msdn.com/ericwhite/default.aspx


نظرات مطالب
طراحی افزونه پذیر با ASP.NET MVC 4.x/5.x - قسمت سوم
قسمت post build برای کپی فایل‌ها مهم است. اگر این قسمت اجرا نمی‌شود، یعنی یا مسیرها را اشتباه وارد کرده‌اید و یا پروژه‌ی مدنظر build نمی‌شود.
- مسیرها را بررسی کنید.
- کل solution را build کنید.
- روی پروژه اصلی کلیک راست کنید و گزینه‌ی project dependencies را انتخاب کنید. در اینجا می‌شود مشخص کرد، زمانیکه این پروژه build می‌شود، چه پروژه‌هایی حتما باید build شوند (در قسمت depends on آن، تیک همه‌ی موارد را قرار دهید).
مطالب
آشنایی با ساختار IIS قسمت نهم
در قسمت قبلی  ما یک هندلر ایجاد کردیم و درخواست‌هایی را که برای فایل jpg و به صورت GET ارسال میشد، هندل می‌کردیم و تگی را در گوشه‌ی تصویر درج و آن را در خروجی نمایش میدادیم. در این مقاله قصد داریم که کمی هندلر مورد نظر را توسعه دهیم و برای آن یک UI یا یک رابط کاربری ایجاد نماییم. برای توسعه دادن ماژولها و هندلر‌ها ما یک dll نوشته و باید آن را در GAC که مخفف عبارت Global Assembly Cache ریجستر کنیم.


جهت اینکار یک پروژه از نوع class library ایجاد کنید. فایل class1.cs را که به طور پیش فرض ایجاد می‌شود، حذف کنید و رفرنس‌های Microsoft.Web.Management.dll و Microsoft.Web.Administration.dll را از مسیر زیر اضافه کنید:
\Windows\system32\inetsrv
اولین رفرنس شامل کلاس‌هایی است که جهت ساخت ماژول‌ها برای کنسول IIS مورد نیاز است و دومی هم برای خواندن پیکربندی‌های نوشته شده مورد استفاده قرار می‌گیرد.
برای طراحی UI  بر پایه winform باید رفرنس‌های System.Windows.Forms.dll و System.Web.dll را از سری اسمبلی‌های دات نت نیز اضافه کنیم و در مرحله‌ی بعدی جهت ایجاد امضاء یا strong name (^  و  ^  ) به خاطر ثبت در GAC پروژه را انتخاب و وارد Properties پروژه شوید. در تب signing گزینه sign the assembly را تیک زده و در لیست باز شده گزینه new را انتخاب نمایید و نام  imageCopyrightUI را به آن نسبت داده و گزینه تعیین کلمه عبور را غیرفعال کنید و تایید و تمام. الان باید یک فایل snk مخفف strong name key ایجاد شده باشد تا بعدا با استفاده از این کلید dll ایجاد شده را در GAC ریجستر کنیم.

در مرحله بعدی در تب Build Events کد زیر را در بخش Post-build event command line اضافه کنید. این کد باعث می‌شود بعد از هر بار کامپایل پروژه، به طور خودکار در GAC ثبت شود:

call "%VS80COMNTOOLS%\vsvars32.bat" > NULL 
gacutil.exe /if "$(TargetPath)"

نکته:در صورتی که از VS2005 استفاده می‌کنید در تب Debug در قسمت Start External Program مسیر زیر را قرار بدهید. اینکار برای تست و دیباگینگ پروژه به شما کمک خواهد کرد. این تنظیم شامل نسخه‌های اکسپرس نمی‌شود.
 \windows\system32\inetsrv\inetmgr.exe

بعد از پایان اینکار پروژه را Rebuild کنید. با اینکار dll در GAC ثبت می‌شود. استفاده از سوییچ‌های if به طور همزمان در درستور gacutil به معنی این هست که اگر اولین بار است نصب می‌شود، پس با سوییچ i نصب کن. ولی اگر قبلا نصب شده است نسخه جدید را به هر صورتی هست جایگزین قبلی کن یا همان reinstall کن.
 
ساخت یک Module Provider
رابط‌های کاربری IIS همانند هسته و کل سیستمش، ماژولار و قابل خصوصی سازی است. رابط کاربری، مجموعه‌ای از ماژول هایی است که میتوان آن‌ها را حذف یا جایگزین کرد. تگ ورودی یا معرفی برای هر UI یک module provider است. خیلی خودمانی، تگ ماژول پروایدر به معرفی یک UI در IIS می‌پردازد. لیستی از module provider‌ها را می‌توان در فایل زیر در تگ بخش <modules> پیدا کرد.
%windir%\system32\inetsrv\Administration.config

در اولین گام یک کلاس را به اسم imageCopyrightUIModuleProvider.cs ایجاد کرده و سپس آن‌را به کد زیر، تغییر می‌دهیم. کد زیر با استفاده از ModuleDefinition  یک نام به تگ Module Provider داده و کلاس imageCopyrightUI را که بعدا تعریف می‌کنیم، به عنوان مدخل entry رابط کاربری معرفی کرده:

using System;
using System.Security;
using Microsoft.Web.Management.Server;
    
namespace IIS7Demos           
{
    class imageCopyrightUIProvider : ModuleProvider
    {
        public override Type ServiceType              
        {
            get { return null; }
        }

        public override ModuleDefinition GetModuleDefinition(IManagementContext context)
        {
            return new ModuleDefinition(Name, typeof(imageCopyrightUI).AssemblyQualifiedName);
        }

        public override bool SupportsScope(ManagementScope scope)
        {
            return true;
        }
    }            
}

با ارث بری از کلاس module provider، سه متد بازنویسی می‌شوند که یکی از آن ها SupportsScope هست که میدان عمل پروایدر را مشخص می‌کند، مانند اینکه این پرواید در چه میدانی باید کار کند که می‌تواند سه گزینه‌ی server,site,application باشد. در کد زیر مثلا میدان عمل application انتخاب شده است ولی در کد بالا با برگشت مستقیم true، همه‌ی میدان را جهت پشتیبانی از این پروایدر اعلام کردیم.

 public override bool SupportsScope(ManagementScope scope)
        {
            return (scope == ManagementScope.Application) ;
        }

حالا که پروایدر (معرف رابط کاربری به IIS) تامین شده، نیاز است قلب کار یعنی ماژول معرفی گردد. اصلی‌ترین متدی که باید از اینترفیس ماژول پیاده سازی شود متد initialize است. این متد جایی است که تمام عملیات در آن رخ می‌دهد. در کلاس زیر imageCopyrightUI ما به معرفی مدخل entry رابط کاربری می‌پردازیم. در سازنده‌های این متد، پارامترهای نام، صفحه رابط کاربری وتوضیحی در مورد آن است. تصویر کوچک و بزرگ جهت آیکن سازی (در صورت عدم تعریف آیکن، چرخ دنده نمایش داده می‌شود) و توصیف‌های بلندتر را نیز شامل می‌شود.

 internal class imageCopyrightUI : Module
    {
        protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo)
        {            
            base.Initialize(serviceProvider, moduleInfo);
            IControlPanel controlPanel = (IControlPanel)GetService(typeof(IControlPanel));
            ModulePageInfo modulePageInfo = new ModulePageInfo(this, typeof(imageCopyrightUIPage), "Image Copyright", "Image Copyright",Resource1.Visual_Studio_2012,Resource1.Visual_Studio_2012);
            controlPanel.RegisterPage(modulePageInfo);
        }
    }

شیء ControlPanel مکانی است که قرار است آیکن ماژول نمایش داده شود. شکل زیر به خوبی نام همه قسمت‌ها را بر اساس نام کلاس و اینترفیس آن‌ها دسته بندی کرده است:

پس با تعریف این کلاس جدید ما روی صفحه‌ی کنترل پنل IIS، یک آیکن ساخته و صفحه‌ی رابط کاربری را به نام imageCopyrightUIPage، در آن ریجستر می‌کنیم. این کلاس را پایینتر شرح داده‌ایم. ولی قبل از آن اجازه بدهید تا انواع کلاس هایی را که برای ساخت صفحه کاربرد دارند، بررسی نماییم. در این مثال ما با استفاده از پایه‌ای‌ترین کلاس، ساده‌ترین نوع صفحه ممکن را خواهیم ساخت. 4 کلاس برای ساخت یک صفحه وجود دارند که بسته به سناریوی کاری، شما یکی را انتخاب می‌کنید.

 ModulePage   شامل اساسی‌ترین متدها و سورس‌ها شده و هیچگونه رابط کاری ویژه‌ای را در اختیار شما قرار نمی‌دهد. تنها یک صفحه‌ی خام به شما می‌دهد که می‌توانید از آن استفاده کرده یا حتی با ارث بری از آن، کلاس‌های جدیدتری را برای ساخت صفحات مختلف و ویژه‌تر بسازید. در حال حاضر که هیچ کدام از ویژگی‌های IIS فعلی از این کلاس برای ساخت رابط کاربری استفاده نکرده‌اند.
 ModuleDialogPage   یک صفحه شبیه به دیالوگ را ایجاد می‌کند و شامل دکمه‌های Apply و Cancel میشود به همراه یک سری متدهای اضافی‌تر که اجازه‌ی override کردن آنها را دارید. همچنین یک سری از کارهایی چون refresh  و از این دست عملیات خودکار را نیز انجام میدهد. از نمونه رابط‌هایی که از این صفحات استفاده می‌کنند میتوان  machine key و management service را اسم برد.
 ModulePropertiesPage   این صفحه یک رابط کاربری را شبیه پنجره property که در ویژوال استادیو وجود دارد، در دسترس شما قرار میدهد. تمام عناصر آن در یک حالت گرید grid لیست می‌شوند. از نمونه‌های موجود میتوان به CGI,ASP.Net Compilation اشاره کرد.
 ModuleListPage   این کلاس برای مواقعی کاربرد دارد که شما قرار است لیستی از آیتم‌ها را نشان دهید. در این صفحه شما یک ListView دارید که میتوانید عملیات جست و جو، گروه بندی و نحوه‌ی نمایش لیست را روی آن اعمال کنید.
در این مثال ما از اولین کلاس نامبرده که پایه‌ی همه کلاس هاست استفاده می‌کنیم. کد زیر را در کلاسی به اسم imageCopyrightUIPage  می‌نویسیم:
    public sealed class imageCopyrightUIPage : ModulePage
    {
        public string message;
        public bool featureenabled;
        public string color;

        ComboBox _colCombo = new ComboBox();
        TextBox _msgTB = new TextBox();
        CheckBox _enabledCB = new CheckBox();

        public imageCopyrightUIPage()
        {
            this.Initialize();
        }


        void Initialize()
        {
            Label crlabel = new Label();
            crlabel.Left = 50;
            crlabel.Top = 100;
            crlabel.AutoSize = true;
            crlabel.Text = "Enable Image Copyright:";
            _enabledCB.Text = "";
            _enabledCB.Left = 200;
            _enabledCB.Top = 100;
            _enabledCB.AutoSize = true;

            Label msglabel = new Label();
            msglabel.Left = 150;
            msglabel.Top = 130;
            msglabel.AutoSize = true;
            msglabel.Text = "Message:";
            _msgTB.Left = 200;
            _msgTB.Top = 130;
            _msgTB.Width = 200;
            _msgTB.Height = 50;

            Label collabel = new Label();
            collabel.Left = 160;
            collabel.Top = 160;
            collabel.AutoSize = true;
            collabel.Text = "Color:";
            _colCombo.Left = 200;
            _colCombo.Top = 160;
            _colCombo.Width = 50;
            _colCombo.Height = 90;
            _colCombo.Items.Add((object)"Yellow");
            _colCombo.Items.Add((object)"Blue");
            _colCombo.Items.Add((object)"Red");
            _colCombo.Items.Add((object)"White");

            Button apply = new Button();
            apply.Text = "Apply";
            apply.Click += new EventHandler(this.applyClick);
            apply.Left = 200;
            apply.AutoSize = true;
            apply.Top = 250;

            Controls.Add(crlabel);
            Controls.Add(_enabledCB);
            Controls.Add(collabel);
            Controls.Add(_colCombo);
            Controls.Add(msglabel);
            Controls.Add(_msgTB);
            Controls.Add(apply);
        }

        public void ReadConfig()
        {
            try
            {
                ServerManager mgr;
                ConfigurationSection section;
                mgr = new ServerManager();
                Configuration config =
                mgr.GetWebConfiguration(
                       Connection.ConfigurationPath.SiteName,
                       Connection.ConfigurationPath.ApplicationPath +
                       Connection.ConfigurationPath.FolderPath);

                section = config.GetSection("system.webServer/imageCopyright");
                color = (string)section.GetAttribute("color").Value;
                message = (string)section.GetAttribute("message").Value;
                featureenabled = (bool)section.GetAttribute("enabled").Value;

            }

            catch
            { }

        }
      
        void UpdateUI()
        {
            _enabledCB.Checked = featureenabled;
            int n = _colCombo.FindString(color, 0);
            _colCombo.SelectedIndex = n;
            _msgTB.Text = message;
        }


        protected override void OnActivated(bool initialActivation)
        {
            base.OnActivated(initialActivation);
            if (initialActivation)
            {
                ReadConfig();
                UpdateUI();
            }
        }



        private void applyClick(Object sender, EventArgs e)
        {
            try
            {
                UpdateVariables();
                ServerManager mgr;
                ConfigurationSection section;
                mgr = new ServerManager();
                Configuration config =
                mgr.GetWebConfiguration
                (
                       Connection.ConfigurationPath.SiteName,
                       Connection.ConfigurationPath.ApplicationPath +
                       Connection.ConfigurationPath.FolderPath
                );

                section = config.GetSection("system.webServer/imageCopyright");
                section.GetAttribute("color").Value = (object)color;
                section.GetAttribute("message").Value = (object)message;
                section.GetAttribute("enabled").Value = (object)featureenabled;

                mgr.CommitChanges();

            }

            catch
            { }

        }

        public void UpdateVariables()
        {
            featureenabled = _enabledCB.Checked;
            color = _colCombo.Text;
            message = _msgTB.Text;
        }
    }
اولین چیزی که در کلاس بالا صدا زده می‌شود، سازنده‌ی کلاس هست که ما در آن یک تابع تعریف کردیم به اسم initialize که به آماده سازی اینترفیس یا رابط کاربری می‌پردازد و کنترل‌ها را روی صفحه می‌چیند. این سه کنترل، یکی Combox برای تعیین رنگ، یک Checkbox برای فعال بودن ماژول و دیگری هم یک textbox جهت نوشتن متن است. مابقی هم که سه label برای نامگذاری اشیاست. بعد از اینکه کنترل‌ها روی صفحه درج شدند، لازم است که تنظیمات پیش فرض یا قبلی روی کنترل‌ها نمایش یابند که اینکار را به وسیله تابع readConfig انجام می‌دهیم و تنظیمات خوانده شده را در متغیر‌های عمومی قرار داده و با استفاده از تابع UpdateUI این اطلاعات را روی کنترل‌ها ست می‌کنیم و به این ترتیب UI به روز می‌شود. این دو تابع را به ترتیب پشت سر هم در یک متد به اسم OnActivated  که override کرده‌ایم صدا میزنیم. در واقع این متد یک جورایی همانند رویداد Load می‌باشد؛ اگر true برگرداند اولین فعال سازی رابط کاربری بعد از باز شدن IIS است و در غیر این صورت false بر میگرداند.

در صورتی که کاربر مقادیر را تغییر دهد و روی گزینه apply کلیک کند تابع applyClick اجرا شده و ابتدا به تابع UpdateVariables ارجاع داده میشود که در آن مقادیر خوانده شده و در متغیرهای Global قرار می‌گیرند و سپس با استفاده از دو شیء از نوع serverManger و ConfigSection جایگذاری یا ذخیره می‌شوند.
استفاده از دو کلاس Servermanager و Configsection در دو قسمت خواندن و نوشتن مقادیر به کار رفته‌اند. کلاس servermanager به ما اجازه دسترسی به تنظیمات IIS و قابلیت‌های آن را میدهد. در تابع ReadConfig مسیر وب سایتی را که در لیست IIS انتخاب شده است، دریافت کرده و به وب کانفیگ آن وب سایت رجوع نموده و تگ imageCopyright آن را که در تگ system.webserver قرار گرفته است، میخواند (در صورتی که این تگ در آن وب کانفیگ موجود نباشد، خواندن و سپس ذخیره مجدد آن روی تگ داخل فایل applicationHost.config اتفاق میفتد که نتیجتا برای همه‌ی وب سایت هایی که این تگ را ندارند یا مقدارهای پیش فرض آن را تغییر نداده‌اند رخ میدهد) عملیات نوشتن هم مشابه خواندن است. تنها باید خط زیر را در آخر برای اعمال تغییرات نوشت؛ مثل EF با گزینه Context.SaveChanges:
mgr.CommitChanges();
وقت آن است که رابط کاربری را به IIS اضافه کنیم: پروژه را Rebuild کنید. بعد از آن با خطوطی که قبلا در Post-Build Command نوشتیم باید dll ما در GAC ریجستر شود. برای همین آدرس زیر را در cmd تایپ کنید:
%vs110comntools%\vsvars32.bat
عبارت اول که مسیر ویژوال استودیوی  شماست و عدد 110 یعنی نسخه‌ی 11. هر نسخه‌ای را که استفاده میکنید، یک صفر جلویش بگذارید و جایگزین عدد بالا کنید. مثلا نسخه 8 می‌شود 80 و فایل بچ بالا هم دستورات visual studio را برای شما آزاد می‌کند.
سپس دستور زیر را وارد کنید:
GACUTIL /l ClassLibrary1
کلمه classLibrary1 نام پروژه‌ی ما بود که در GAC ریجستر شده است. با سوییچ l تمامی اطلاعات اسمبلی‌هایی که در GAC ریجستر شده‌اند، نمایش می‌یابند. ولی اگر اسم آن اسمبلی را جلویش بنویسید، فقط اطلاعات آن اسمبلی نمایش میابد. با اجرای خط فوق میتوانیم کلید عمومی public key اسمبلی خود را بدانیم که در شکل زیر مشخص شده است:

پس اگر کلید را دریافت کرده‌اید، خط زیر را به فایل administration.config در تگ <ModuleProviders> اضافه کنید:
<add name="imageCopyrightUI" type="ClassLibrary1.imageCopyrightUIProvider,   ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d0b3b3b2aa8ea14b"/>
عبارت ClassLibrary1.imageCopyrightUIProvider به کلاس imageCopyrightUIProvider اشاره می‌کند که در این کلاس UI معرفی می‌شود. مابقی عبارت هم کاملا مشخص است و در لینک‌های بالا در مورد Strong name توضیح داده شده اند. 
فایل administration.config  در مسیر زیر قرار دارد:
%windir%\system32\inetsrv\config\administration.config
حالا تنها کاری که نیاز است، باز کردن IIS است. به بخش وب سایت‌ها رفته و اپلیکیشنی که قبلا با نام mypictures را ایجاد کرده بودیم، انتخاب کنید. در سمت راست، آخر لیست، بخش others باید ماژول ما دیده شود. بازش کنید و تنظمیات آن را تغییر دهید و حالا یک تصویر را از اپلیکیشن mypictures، روی مرورگر درخواست کنید تا تغییرات را روی تگ مشاهده کنید:

 
حالا دیگر باید ماژول نویسی برای IIS را فراگرفته باشیم. این ماژول‌ها می‌توانند از یک مورد ساده تا یک کلاس مهم و امنیتی باشند که روی سرور شما برای همه یا بعضی از وب سایت‌ها در حال اجرا هستند و در صورت لزوم و اجازه شما، برنامه نویس‌ها میتوانند مثل همه‌ی تگ‌های موجود در وب کانفیگ سایتی را که  مینویسند، تگ ماژول شما و  تنظیمات آن را با استفاده از attribute یا خصوصیت‌های تعریف شده، بر اساس سلایق و نیازهایشان تغییر دهند و روی سرور شما آپلود کنند. الان شما یک سرور خصوصی سازی شده دارید.
از آنجا که این مقاله طولانی شده است، باقی موارد ویرایشی روی این UI را در مقاله بعدی بررسی خواهیم کرد.