مطالب
4# آموزش سیستم مدیریت کد Git : نصب و پیکر‌‏بندی
برای نصب Git ابتدا به msysgit رفته و مطابق شکل زیر بر روی گزینه دانلود کلیک کنید. سپس در صفحه باز شده آخرین نسخه Git را دانلود نموده و فایل مربوطه را اجرا کنید:

شروع نصب:

--------------  

در این مرحله بخش Windows Explorer Integration اهمیت دارد. در صورت انتخاب این بخش، بعد از نصب، Git Bash و Git GUI به منوی راست کلیک شما اضافه می‏‌شود. به این ترتیب با سرعت بیشتری می‏توانید به Git در یک  پوشه خاص دسترسی داشته باشید.

--------------
در این مرحله از شما خواسته می‏‌شود تعیین کنید که آیا فقط می‏‌خواهید از طریق Git Bash با Git کار کنید یا با اضافه کردن فایل اجرایی Git به متغیر‏های محلی ویندوز از طریق Command Prompt  ویندوز نیز می‏خواهید به Git دسترسی داشته باشید. گزینه سوم هم Git و هم برخی از ابزار‏های یونیکسی را به متغیر‏های محلی اضافه می‏‌کند که سبب می‏‌شود شما یک خط فرمان قدرتمند‏تر در ویندوز داشته باشید. اما این کار ممکن است در برخی از برنامه‏‌های پیش فرض اختلال ایجاد کند بنابراین در انتخاب این گزینه احتیاط کنید.

--------------
در این مرحله کاراکتری را که نشان دهنده انتهای خط است تعیین می‏‌کنید. این کاراکتر در ویندوز و یونیکس متفاوت است. بنابراین Git از شما می‏‌خواهد که برای حفظ سازگاری در محیط‏ هایی که چند سیستمی هستند، آن‏را تعیین کنید.
گزینه اول به صورت فرمت یونیکس ذخیره و به شکل ویندوز بازیابی می‏‌شود (مناسب برای محیط ویندوز).
گزینه دوم ذخیره به فرمت یونیکسی است و مناسب محیط‌های یونیکس است.
و آخرین گزینه فایل را بدون تغییر ذخیره و بازیابی می‏کند (از این گزینه نیز می‏توان هم برای Unix و هم windows استفاده کرد).
بعد از این مرحله نصب آغاز می‏شود.


نکته: شما می‏‌توانید جهت دسترسی به یک محیط گرافیکی قوی از gitextensions   استفاده کنید. با دانلود این فایل، هم خود Git و هم GUI ‏هایی برای کار‏های مختلف، نظیر مشاهده تفاوت‏های دو فایل یا نمایش گرافیکی شاخه‏‌ها به سیستم شما اضافه می‏‌شود.

پیکربندی Git:
برای پیکربندی Git شما باید یک فایل config ایجاد کنید و با استفاده از دستوراتی که در ادامه می‏‌آید این تنظیمات را انجام دهید. البته پیکربندی Git از طریق ابزار‏های گرافیکی که در محله قبل نصب کردید نیز امکان‏پذیر است.
Git دارای سه نوع دسترسی برای پیکره‌بندی است:
سیستمی: این تنظیمات بر روی کل سیستمی که git برای روی آن نصب شده اعمال می‏‌شود. فایل  gitconfig در مسیر program files/Git/etc/gitconfig قرار دارد و برای تغییر آن باید از سوئیچ system-- استفاده نمود.
در سطح کاربر: فایل  config.در مسیر [users/[username/ برای این منظور است و تغییر این تنظیمات تنها بر روی همین کاربر اعمال می‏شود برای درسترسی به این فایل باید از سوئیچ global-- استفاده کرد.
در سطح Repository: برای هر پوشه repository  این فایل موجود است و اگر از دستور config بدون هیچ سوئیچی استفاده کنیم تغییرات بر روی این فایل اعمال می‏شود.

نکته: معمولا فایل پیکربندی git در سطح سیستم را تغییر نمی‏‌دهند.

دستورات پیکربندی:
همان‏طور که گقته شد هر Commit حاوی اطلاعات فردی است که آن‏را انجام داده است. این اطلاعات را می‏‌توان به صورت زیر تنظیم کرد:
نام کاربر:
git config --global user.name "Hessam"
ایمیل کاربر:
git config --global user.email "hessam@localhost.com"
با استفاده از دستور زیر می‏‌توان تنظیماتی را که تا کنون انجام شده ببینیم:
git config --global --list
همچنین می‏‌توان ویرایشگر متن پیش فرضی برای git تعیین کرد. از این ویرایشگر می‏‌توان به عنوان مثال بعد از فرخوانی دستور commit استفاده نمود تا دلیل commit مشخص شود. در صورت تعیین این ویرایشگر، git  آن‏را خودکار باز می‏کند:
git config --global core.editor notepad
من در اینجا notepad را انتخاب کردم توجه کنید که مسیر ویرایشگر باید در متغیر‌های محلی ویندوز باشد.
و در نهایت جهت نمایش بهتر پیام‏های git می‏توانیم تنظیم کنیم که آن‏ها را با رنگ‏های متفاوتی نمایش دهد:
git config --global color.ui auto
 البته تنظیمات بیشتری را می‏‌توان در اینجا انجام داد، مانند تعیین برنامه پیش فرض برای نمایش اختلاف فایل‏ها و یا برنامه پیش فرض برای حل کردن مشکل conflict و غیره که این تنظیمات در همان بخش‏‌ها گفته خواهد شد.
در قسمت بعد دستورات اولیه کار با git به صورت محلی گفته خواهد شد.
مطالب
آشنایی با WPF قسمت دوم: Layouts بخش اول
layout‌‌‌ها یکی از مهمترین قسمت‌های یک برنامه‌ی کاربردی هستند. چیدمان کنترل‌ها روی یک ناحیه با دادن مختصات پیکسلی ثابت، ممکن است در یک محیط محدود خود را خوب نشان بدهد ولی به زودی با تغییر محیط برنامه و یا تغییر وضوح تصویر صفحه نمایش، برنامه از کنترل خارج خواهد شد؛ در نتیجه استفاده از Layout‌ها یا پنل‌ها در WPF امری حیاتی و مهم هستند. layout‌‌ها که با نام container هم شناخته می‌شوند وظیفه دارند که بگویند چه کنترل‌هایی در کجا و چگونه باید در صفحه‌ی برنامه قرار بگیرند. پنل‌های توکار در WPF به دسته‌های زیر تقسیم می‌شوند:
  • Grid Panel
  • Stack Panel
  • Dock Panel
  • Wrap Panel
  • Canvas Panel

StackPanel

این پنل یکی از ساده‌ترین و سودمندترین پنل هاست که بر اساس جهت Orientation افقی یا عمودی که به آن تنظیم می‌شود، کنترل‌ها را کنار یکدیگر یا زیر یکدیگر قرار می‌دهد. این کنترل برای ایجاد و تهیه لیست‌های مختلف مناسب است. در ساختار داخلی کنترل‌های لیست و کامبو و منو‌ها که در WPF موجود هستند این پنل استفاده شده است. کد زیر یک نمونه کاربرد Stack Panel را نشان می‌دهد که به صورت عمودی چینش شده است.
<StackPanel>
  <TextBlock Margin="10" FontSize="20">How do you like your coffee?</TextBlock>
  <Button Margin="10">Black</Button>
  <Button Margin="10">With milk</Button>
  <Button Margin="10">Latte machiato</Button>
  <Button Margin="10">Chappuchino</Button>
</StackPanel>

نکته‌ی مهم اینکه میتوانید در اینجا از یک nested layout هم استفاده کنید بدین صورت که یک layout را داخل یک layout دیگر قرار دهید. کد زیر ترکیب دو stack panel را به صورت افقی و عمودی به ما نشان می‌دهد:

<StackPanel Orientation="Vertical"> <!-- Vertical is the default -->
  <Label Background="Red">Red 1</Label>
  <Label Background="LightGreen">Green 1</Label>
  <StackPanel Orientation="Horizontal">
    <Label Background="Red">Red 2</Label>
    <Label Background="LightGreen">Green 2</Label>
    <Label Background="LightBlue">Blue 2</Label>
    <Label Background="Yellow">Yellow 2</Label>
    <Label Background="Orange">Orange 2</Label>
  </StackPanel>
  <Label Background="LightBlue">Blue 1</Label>
  <Label Background="Yellow">Yellow 1</Label>
  <Label Background="Orange">Orange 1</Label>
</StackPanel>


Dock Panel

احتمالا به خاطر نامش، نحوه کارش را حدس زده اید. این پنل، اشیاء موجود را در 4 جهت و مرکز می‌چسباند. مشخص نمودن جهت چسبیده شدن هر کنترل توسط خاصیت DockPanel.Dock صورت می‌گیرد و مقدار Left، مقدار پیش فرض است. در صورتی که بخواهید المانی را در مرکز بچسبانید باید آن را به عنوان آخرین المان معرفی کرده و در Dock Panel مقدار خاصیت LastChildFill را با True برابر کنید.

<DockPanel LastChildFill="True">
    <Button Content="Dock=Top" DockPanel.Dock="Top"/>
    <Button Content="Dock=Bottom" DockPanel.Dock="Bottom"/>
    <Button Content="Dock=Left"/>
    <Button Content="Dock=Right" DockPanel.Dock="Right"/>
    <Button Content="LastChildFill=True"/>
</DockPanel>


به نحوه‌ی تعریف خاصیت DockPanel.Dock دقت کنید به این نوع خاصیت‌ها،  Attached Dependency Property (شاید در فارسی بتوانیم خاصیت‌های وابستگی متصل صدا بزنیم) می‌گویند. این خاصیت‌ها نوع خاصی از خاصیت‌های وابستگی هستند که به شما اجازه می‌دهند مقداری را به شیء‌ایی نسبت دهید که آن شیء چیزی در مورد آن نمی‌داند. بهترین مثال در مورد این ویژگی، پنل‌ها هستند که یکی از موارد استفاده‌ی از آن را در بالا می‌بینید. هر پنل می‌تواند تا بی نهایت المان فرزند داشته باشد که هر المان باید خواصش توسط پنل مشخص گردد. ولی اگر پنل ما تعداد زیادی فرزند داشته باشد، نوشتن خواص هر کدام از فرزندها داخل تگ پنل، کاری غیر ممکن است. اینجاست که این نوع خاصیت‌ها خودشان را نشان می‌دهند. پس به این نحو مقادیر، داخل کنترل هر تگ تعریف می‌شود ولی توسط پنل مورد استفاده قرار می‌گیرد. نحوه‌ی نوشتن این نوع خاصیت: ابتدا یک پیشوند از نوع تگ پنل را در ابتدا آورده و سپس بعد از .(نقطه) نام خاصیت را ذکر می‌کنیم.

نحوه‌ی تعریف این نوع خاصیت‌ها در یک کلاس به صورت زیر است که برای شیء یا پنل canvas می‌باشد:

public static readonly DependencyProperty TopProperty =
    DependencyProperty.RegisterAttached("Top", 
    typeof(double), typeof(Canvas),
    new FrameworkPropertyMetadata(0d,
        FrameworkPropertyMetadataOptions.Inherits));
 
public static void SetTop(UIElement element, double value)
{
    element.SetValue(TopProperty, value);
}
 
public static double GetTop(UIElement element)
{
    return (double)element.GetValue(TopProperty);
}
در مثال dockPanel بالا در هر طرف تنها یک المان قرار دادیم. برای قرار دادن المان‌های بیشتر در طرفین، تنها ذکر یک المان جدید با خاصیت Dockpanel.dock کافی است و الویت نمایش آن‌ها بر اساس ترتیب نوشتن تگها توسط شماست. مثال زیر این نکته را نشان می‌دهد:
    <Button Content="Dock=Top" DockPanel.Dock="Top"/>
        <Button Content="Dock=Bottom" DockPanel.Dock="Bottom"/>
        <Button Content="Dock=Left"/>
        <Button Content="Dock=Left2"/>
        <Button Content="Right2" DockPanel.Dock="Right"/>
        <Button Content="Dock=Right" DockPanel.Dock="Right"/>
        <Button Content="LastChildFill=True"/>



Wrap Panel
این پنل بسیار شبیه StackPanel هست ولی مثل آن اشیاء را در یک سطر یا ستون ادامه نمی‌دهد؛ بلکه با رسیدن به انتهای پنجره، سطر یا ستون جدیدی را آغاز می‌کند. در stack panel با پایان پنجره، ادامه اشیا آن قابل مشاهده نبود ولی در این شیء با اتمام و رسیدن به لبه‌ی پنجره، اشیاء در سر جدید (افقی) یا ستون جدید (عمودی) نمایش داده می‌شوند. این پنل‌ها می‌توانند در ساخت تب‌ها و نوار ابزار استفاده شوند.

Canvas Panel

پایه‌ای‌ترین layout موجود در WPF است. موقعیت قرارگیری المان‌های فرزندش بر اساس نقاط تعیین شده است.این پنل بیشتر برای رسم اشکال و گرافیک دو بعدی مناسب است و اصلا برای قرارگیری کنترل‌های WPF روی آن توصیه نمی‌شود و مشکل winform‌ها در آن رخ خواهد داد.

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

  • Canvas.LEFT
  • Canvas.RIGHT
  • Canvas.TOP
  • Canvas.BOTTOM

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

<Canvas>
    <Rectangle Canvas.Left="40" Canvas.Top="31" Width="63" Height="41" Fill="Blue"  />
    <Ellipse Canvas.Left="130" Canvas.Top="79" Width="58" Height="58" Fill="Blue"  />
    <Path Canvas.Left="61" Canvas.Top="28" Width="133" Height="98" Fill="Blue" 
          Stretch="Fill" Data="M61,125 L193,28"/>
</Canvas>

ترتیب قرارگیری اشکال روی هم در canvas به ترتیبی انجام می‌گیرد که در XAML نوشته اید ولی می‌توان با استفاده از خاصیت Canvas.ZIndex این ترتیب را تغییر داد.

<Canvas>
    <Ellipse Fill="Green" Width="60" Height="60" Canvas.Left="30" Canvas.Top="20"    
             Canvas.ZIndex="1"/>
    <Ellipse Fill="Blue"  Width="60" Height="60" Canvas.Left="60" Canvas.Top="40"/>
</Canvas>
شکل زیر در سمت راست، نتیجه‌ی کد بالاست ولی بدون ذکر ZIndex شکل سمت چپ نتیجه‌ی کار خواهد بود.

ViewBox
شاید عده‌ای به سختی آن را یک Layout بدانند و بیشتر آن را یک کنترل معمولی می‌شناسند ولی وظیفه‌ی آن بسیار شبیه Layout هاست. خصوصیتی که این شیء دارد این است که با تغییر اندازه محیط برنامه به هر نحوی، یک تغییر مقیاس روی اشیاء داخل آن رخ می‌دهد و کنترل‌ها به همراه متون و هر چیزی که در درخت منطقی و بصری است Scale آن تغییر می‌یابند.
نمونه‌ی کد زیر را تست کنید تا تفاوت بین دو Button را ببینید:
  <StackPanel Orientation="Vertical">
        <Button Content="Test" />
        <Viewbox Stretch="Uniform">
            <Button Content="Test" />
        </Viewbox>
    </StackPanel>
نتیجه‌ی کار:

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

مطالب
ارتباط بین کامپوننت‌ها در Vue.js - قسمت اول ارتباط بین Parent و Child

برنامه‌های Vue.jsای از چندین کامپوننت برای بخش بندی هر قسمت تشکیل میشوند و این بخش بندی برای مدیریت بهتر تغییرات، خطایابی، نگهداری و استفاده مجدد (reusable) می‌باشد. فرض کنید تعدادی کامپوننت در برنامه داریم و اطلاعات این کامپوننت‌ها بهم وابسته می‌باشند؛ بطور مثال یک کامپوننت انتخاب دسته بندی را داریم و به محض تغییر این مقدار، میخواهیم لیستی از محصولات پیشنهادی یا پرفروشِ آن دسته بندی، در کامپوننت پایین صفحه نمایش داده شود و یا خرید یک محصول را در نظر بگیرید که بلافاصله محتوای نمایش سبد خرید، بروزرسانی شود. در این مقاله ارتباط از نوع Parent و Child بین کامپوننت‌ها بررسی میشود.

 نکته: برای ارسال اطلاعات از کامپوننتِ Parent به Child، از  Props استفاده میشود و برای ارتباط از Child به Parent، از emit$ استفاده میشود.


یک برنامه Vue.js با نام vue-communication-part-1 ایجاد نمایید. سپس دو کامپوننت را با نام‌های Parent و Child، در پوشه components ایجاد کنید:



در کامپوننت Parent، یک تابع با نام increase وجود دارد که مقدار متغیر parentCounter را افزایش میدهد. چون قصد داریم مقدار متغیر parentCounter در کامپوننت Child نیز بروزرسانی شود، آن را به کامپوننت Child پاس میدهیم:

<Child :childCounter="parentCounter"/>

محتوای کامپوننت Parent:

<template>
  <div>
    <div>
      <h2>Parent Component</h2>
      <!-- را نمایش میدهید parentCounter مقدار -->
      <h1>{{ parentCounter }}</h1>
      <button @click="increase">Increase Parent</button>
    </div>
    <div>
      <!-- پاس میدهید Child در کامپوننت childCounter را به پراپرتی parentCounter مقدار -->
      <!--از طریق  decreaseParent سبب اتصال و فراخوانی تابع  @callDecreaseParent به  decreaseParent با انتساب -->
      <!-- میشود Child  در کامپوننت  callDecreaseMethodInParent تابع   -->
      <Child :childCounter="parentCounter" @callDecreaseParent="decreaseParent"/>
    </div>
  </div>
</template>

<script>
//برای استفاده در کامپوننت جاری Child ایمپورت کردن کامپوننت
import Child from "./Child.vue";

export default {
  // در این بخش متغیرهای مورد نیاز کامپوننت را تعریف میکنیم
  data() {
    return {
      parentCounter: 0
    };
  },
  components: {
    // میتوان آرایه ای از کامپوننت‌ها را در یک کامپوننت استفاده نمود
    // در این مثال فقط از یک کامپوننت استفاده شده
    Child
  },
  methods: {
    //را یک واحد افزایش میدهد parentCounter این متد مقدار
    increase() {
      this.parentCounter++;
    },
    decreaseParent() {
      this.parentCounter--;
    }
  }
};
</script>

<style>
.parent-block,
.child {
  text-align: center;
  margin: 20px;
  padding: 20px;
  border: 2px gray solid;
}
</style>


در کامپوننت Child  قصد دریافت مقدار پراپرتیِ childCounter را داریم که از طریق کامپوننت Parent، مقدارش تنظیم و بروزرسانی میشود. به این منظور در قسمت props  یک متغیر بنام childCounter را ایجاد میکنیم. 

Data is the private memory of each component where you can store any variables you need. Props are how you pass this data from a parent component down to a child component

محتوای کامپوننت Child

<template>
  <div>
    <h3>Child Component</h3>
    <!-- را نمایش میدهید childCounter مقدار -->
    <h3>{{ childCounter }}</h3>
    <button @click="increase">Increase Me</button>
    <button @click="callDecreaseMethodInParent">Call Decrease Method In Parent</button>
  </div>
</template>

<script>
export default {
  // استفاده میشود Child به  Parent برای ارتباط بین کامپوننت  props از
  props: {
    childCounter: Number
  },
  data() {
    return {};
  },
  methods: {
    //را یک واحد افزایش میدهد childCounter این متد مقدار
    increase() {
      this.childCounter++;
    },
    // فراخوانی میکند Parent را در کامپوننت decreaseParent تابع
    callDecreaseMethodInParent() {
      this.$emit("callDecreaseParent");
    }
  }
};
</script>


محتوای کامپوننت اصلی برنامه  App.vue:

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <parent></parent>
  </div>
</template>

<script>
import Parent from "./components/Parent.vue";

export default {
  name: "app",
  components: {
    parent: Parent
  }
};
</script>
<style>
#app {
  width: 50%;
  margin: 0 auto;
  text-align: center;
}
</style>

اکنون برنامه را با دستور زیر اجرا کنید:

npm run serve

بعد از اجرای دستور فوق، روی گزینه زیر ctrl+click میکنیم تا نتیجه کار در مرورگر قابل رویت باشد: 

نمایش صفحه زیر نشان دهنده‌ی درستی انجام کار تا اینجا است:

اکنون روی دکمه‌ی Increase Parent کلیک میکنیم. همزمان مقدار شمارشگر، در هر دو کامپوننت Parent و Child افزایش می‌یابد و این بدین معناست که با استفاده از Props میتوانیم داده‌های دلخواهی را در کامپوننت Child بروز رسانی کنیم. هر زمانی روی دکمه‌ی Increase Me در کامپوننت Child کلیک کنیم، فقط به مقدار شمارشگر درون خودش اضافه میشود و تاثیری را بر شمارشگر Parent ندارد. در واقع یک کپی از مقدار شمارشگر Parent را درون خود دارد.

در ادامه قصد داریم بروزرسانی داده را از Child به Parent انجام دهیم. برای انجام اینکار از emit$ استفاده میکنیم. در دیکشنری Cambridge Dictionary معنی emit به ارسال یک سیگنال ترجمه شده‌است. در واقع بااستفاده از emit میتوانیم یک تابع را در کامپوننت Parent فراخوانی کنیم و در آن تابع، کد دلخواهی را برای دستکاری داده‌ها مینویسیم.

در تابع callDecreaseMethodInParent در کامپوننت Child، کد زیر را قرار میدهیم:

 this.$emit("callDecreaseParent");

هر زمان‌که این تابع اجرا شود، یک سیگنال از طریق کد زیر برای کامپوننت Parent ارسال میشود:

      <Child  @callDecreaseParent="decreaseParent"/>

در کد فوق مشخص شده که با ارسال سیگنال callDecreaseParent، تابع decreaseParent در کامپوننت Parent فراخوانی شود.


کد کامل مثال بالا 

نکته:  برای اجرای برنامه و دریافت پکیج‌های مورد استفاده در مثال جاری، نیاز است دستور زیر را اجرا کنید: 

npm install



چند نکته

this.$emit 
//dispatches an event to its parent component

کد فوق سبب اجرای یک تابع در کامپوننتِ Parent خودش میشود.

this.$parent
// gives you a reference to the parent component

ارجاعی به کامپوننت Parent خودش را فراهم میکند:

this.$root
// gives you a reference to the root component

زمانیکه چندین کامپوننت تو در تو را داریم یا به اصطلاح  nested component، سبب ارجاعی به بالاترین کامپوننت Parent میگردد.

this.$parent.$emit
// will make the parent dispatch the event to its parent

سبب اجرای تابعِ Parent کامپوننتِ Parent جاری میشود. به بیان ساده اگر این کد در کامپوننت فرزند فراخوانی شود، سبب اجرای تابعی در کامپوننت پدربزرگِ خود میشود.

this.$root.$emit
// will make the root dispatch the event to itself

سبب اجرای تابعی در کامپوننت root میشود (بالاترین کامپوننتِ پدرِ کامپوننت جاری).


تابع emit$ دارای آگومان‌های دیگری برای پاس دادن اطلاعات از کامپوننت Child به Parent می‌باشد؛ مثل زمانیکه قصد دارید اطلاعاتی در مورد محصول خریداری شده را به سبد خرید پاس دهید. در مثال دیگری که در ادامه قرار میگیرد نحوه کارکرد ارتباط کامپوننت Parent و Child را در یک برنامه بهتر تجربه میکنیم.

پیاده سازی یک سبد خرید ساده با روش مقاله‌ی جاری 

نکته:  برای اجرای برنامه و دریافت پکیج‌های مورد استفاده در مثال جاری، نیاز است دستور زیر را اجرا کنید:  

npm install

همچنین نیاز هست تا پکیچ node-sass را با دستور زیر برای این مثال نصب کنید.

npm install node-sass

اشتراک‌ها
ذخیره تنظیمات کاربر در یک برنامه Blazor WebAssembly

در یک برنامه، شما اغلب باید تنظیمات کاربر مانند تم انتخاب شده و یا هرگونه پیکربندی دیگری از برنامه یا نام کاربری آنها را ذخیره کنید. این تنظیمات باید:

  • از هرجای اپلیکیشن در دسترس باشند.
  • پایدار باشد، بنابراین می‌توانید هنگام شروع مجدد کاربر از برنامه، آنها را فرا بخوانید 
  • قابلیت به اشتراک گذاری در نمونه‌های مختلف را داشته باشند.
ذخیره تنظیمات کاربر در یک برنامه Blazor WebAssembly
نظرات مطالب
طراحی افزونه پذیر با ASP.NET MVC 4.x/5.x - قسمت سوم
قسمت post build برای کپی فایل‌ها مهم است. اگر این قسمت اجرا نمی‌شود، یعنی یا مسیرها را اشتباه وارد کرده‌اید و یا پروژه‌ی مدنظر build نمی‌شود.
- مسیرها را بررسی کنید.
- کل solution را build کنید.
- روی پروژه اصلی کلیک راست کنید و گزینه‌ی project dependencies را انتخاب کنید. در اینجا می‌شود مشخص کرد، زمانیکه این پروژه build می‌شود، چه پروژه‌هایی حتما باید build شوند (در قسمت depends on آن، تیک همه‌ی موارد را قرار دهید).
مطالب
ASP.NET Web API - قسمت چهارم
آشنایی با مفهوم مسیریابی در Web API
در این قسمت با نحوه‌ی تناظر آدرس‌ها توسط Web API به متدهای موجود در Controller آشنا می‌شوید.
در هر درخواستی که ارسال می‌شود، Web API، انتخاب Controller مناسب را با رجوع به جدولی با نام جدول مسیرها انجام می‌دهد. زمانی که یک پروژه‌ی جدید با استفاده از ASP.NET MVC 4 ایجاد می‌کنید، یک route پیش فرض به صورت ذیل در متد RegisterRoutes قرار می‌گیرد.
routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);
عبارت api، ثابت است و قسمت‌های {controller} و {id} توسط آدرس مقداردهی می‌شوند. زمانی که آدرسی با این الگو تطبیق داشته باشد، کارهای ذیل انجام می‌گیرد:
  • {controller} به نام Controller تناظر پیدا می‌کند.
  • نوع درخواست ارسالی (GET، POST، PUT، DELETE) به نام متد تناظر پیدا می‌کند.
  • اگر قسمت {id} در آدرس وجود داشته باشد، به پارامتر id متد انتخاب شده پاس داده می‌شود.
  • اگر آدرس دارای Query String باشد، به پارامترهای همنام خود در متد، تناظر پیدا می‌کنند.

در ذیل، مثال هایی را از چند آدرس درخواستی و نتیجه‌ی حاصل از فراخوانی آنها مشاهده می‌کنید. 

  • آدرس api/products/ با نوع درخواست GET به متد ()GetAllProducts
  • آدرس api/products/1/ با نوع درخواست GET به متد (1)GetProductById
  • آدرس api/products?category=hardware/ با نوع درخواست GET به متد ("GetProductByCategory("hardware


در آدرس اول، عبارت "products" به ProductsController تطبیق پیدا می‌کند. درخواست نیز از نوع GET است، بنابراین Web API به دنبال متدی در Controller می‌گردد که نام آن با عبارت GET "آغاز" شده باشد. همچنین، آدرس شامل قسمت {id} نیز نیست. بنابراین، Web API متدی را انتخاب می‌کند که پارامتر ورودی ندارد. متد GetAllProducts در ProductsController، تمامی این شروط را دارد، پس انتخاب می‌شود.
در دومین آدرس، همان حالت قبل وجود دارد، با این تفاوت که در آدرس درخواستی، قسمت {id} وجود دارد. از آنجا که نوع قسمت {id} در متد int ،GetProductById تعریف شده است، باید یک عدد صحیح بعد از آدرس /api/products وجود داشته باشد تا متد GetProductById فراخوانی شود. این عدد به طور خودکار به نوع int تبدیل شده و در پارامتر اول متد GetProductById قرار می‌گیرد. در ذیل، برخی آدرس‌ها را ملاحظه می‌کنید که معتبر نیستند و باعث بروز خطا می‌شوند.

  • آدرس api/products/ با نوع درخواست POST، باعث خطای 405Method Not Allowed می‌شود.
  • آدرس api/users/ با نوع درخواست GET، باعث خطای 404Not Found می‌شود.
  • آدرس api/products/abc/ با نوع درخواست GET، باعث خطای 400Bad Request می‌شود.


در آدرس اول، Client یک درخواست از نوع POST ارسال کرده است. Web API به دنبال متدی می‌گردد که نام آن با عبارت Post آغاز می‌شود. اما متدی با این شرط در ProductsController وجود ندارد. بنابراین، پاسخی که دریافت می‌شود، عبارت "405 Method Not Allowed" است. درخواست برای آدرس /api/users/ نیز معتبر نیست، چون Controllerیی با نام UsersController وجود ندارد. و سومین آدرس نیز بدین دلیل نامعتبر است که قسمت abc نمی‌تواند به یک عدد صحیح تبدیل شود. 

مشاهده‌ی درخواست ارسالی و پاسخ دریافتی
زمانی که با یک وب سرویس کار می‌کنید، مشاهده‌ی محتویات درخواست ارسالی و پاسخ دریافتی می‌تواند کاربرد زیادی در درک نحوه‌ی تعامل بین Client و وب سرویس و کشف خطاهای احتمالی داشته باشد. در Firefox با استفاده از افزونه‌ی Firebug و در Internet Explorer 9 به بالا با ابزار Developer Tools آن می‌توان درخواست‌ها و پاسخ‌ها را مشاهده کرد. در Internet Explorer، کلید F12 را برای اجرای Developer Tools فشار دهید. از قسمت Network بر روی دکمه‌ی Start Capturing کلیک کنید. حال کلید F5 را برای بارگذاری مجدد صفحه فشار دهید. Internet Explorer، درخواست و پاسخ رد و بدل شده بین مرورگر و Web Server  را مانیتور کرده و گزارشی را نشان می‌دهد (شکل ذیل).

از ستون URL، آدرس /api/products/ را انتخاب و بر روی دکمه‌ی Go to detailed view کلیک کنید. در قسمتی که باز می‌شود، گزینه هایی برای مشاهده‌ی هدرهای درخواست، پاسخ و همچنین بدنه‌ی هر یک وجود دارد. به عنوان مثال، اگر قسمت Request headers را انتخاب کنید، خواهید دید که Internet Explorer از طریق هدر Accept، تقاضای پاسخ در قالب JSON را کرده است (شکل ذیل).

اگر قسمت Response body را انتخاب کنید، پاسخ دریافت شده در قالب JSON را خواهید دید. 

در قسمت بعد، با مدیریت کدهای وضعیت HTTP برای اعمال چهارگانه‌ی CRUD آشنا می‌شوید.
  
نظرات اشتراک‌ها
معرفی کتابخانه‌ی DNTCaptcha.Core
با سلام؛ من از این کپچا برای سایتم استفاده می‌کردم تا اینکه هفته پیش مشکلی عجیبی پیش اومد هنگام استفاده از فایرفاکس فیلد کپچا در فرم خالی پست می‌شد اما با کروم بدون مشکل کار می‌کرد، با چند مرورگر فایرفاکس در اندروید، ویندوز 10 و ویندوز سرور 2012 تست کردم حتی تعدادی از کاربران سایت هم با ما تماس گرفتند و من پیشنهاد دادم که اون روز از برای لاگین از کروم استفاده کنند جالب اینجاست که آخرین ورژن فایرفاکس (64) این مشکل رو داشت اما ورژن قدیمی (52) مشکلی نداشت. ورژن سایت asp.net core 2.1 بود که به 2.2 ارتقا دادم و DNTCaptcha رو بروزرسانی کردم متاسفانه مشکل حل نشده و الان reCaptcha v3 نصبه که به اجبار دو اسکریپت به قالب اضافه شده ورژن sdk هم 2.2.103 هست. به نظرتون مشکل از چی می‌تونه باشه.
مطالب
آشنایی با ساختار 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 را در مقاله بعدی بررسی خواهیم کرد. 
نظرات مطالب
ASP.NET MVC #19
ممنون آقای نصیری .

آیا راه حلی هست که  قسمتی که Layout برای view تنظیم می‌شود را از caching خارج کرد ؟
خواستم از Response.WriteSubstitution  استفاده کنم و مقدار برگشتی رو در Layout قرار دهم که نمیشود به دلیل Response مستقیم در خروجی .