مطالب
Debugging برنامه هایی که از خط فرمان پارامتر می گیرند!
بسیاری از برنامه‌ها وجود دارند که در زمان فراخوانی از خط فرمان (Command Line) پارامترهایی دریافت می‌کنند و نسبت به آن پارامترها رفتار مشخصی را از خود نشان می‌دهند.
یکی از کاربرد‌های پارامتر ورودی args که از نوع آرایه ای از رشته‌ها در متد Main برنامه‌های کنسول بطور پیش فرض تعریف شده است همین موضوع است. شما می‌توانید از طریق کنترل مقدار این پارامتر برنامه‌ی خود را توسعه دهید.
برای مثال برنامه ای جهت چاپ مجذور اعدادی که از خط فرمان خوانده می‌شوند را در نظر بگیرید. کد مورد استفاده در این برنامه به شکل زیر خواهد بود:
static void Main(string[] args)
{
    foreach (var arg in args)
    {
        var x = Convert.ToInt32(arg);
        Console.WriteLine("{0} ^ 2 = {1}", x, x * x);
    }
    Console.ReadKey();
}

اما نکته اصلی این مطلب در مورد Debugging این گونه برنامه‌ها است.
جهت دیباگ این قبیل برنامه‌ها در ویژوال استادیو از قسمت Solution Explorer بر روی نام پروژه کلیک راست کرده و گزینه Properties را انتخاب کنید.
در پنجره باز شده به زبانه Debug بروید و در قسمت Command Line Arguments پارامترهای ورودی خود را بطوریکه با فضای خالی (Space) از هم جدا شده اند وارد کنید.
 

حال می‌توانید پس از ذخیره کردن تنظیمات، کلید F5 را بزنید.
مطالب
آشنایی با XSLT
XSLT در واقع یک StyleSheet یا یک راهنما در مورد تبدیل فایل‌های xml به انواع و یا ساختارهای دیگری چون فایل‌های html، فایل‌های متنی و ... است که توسط کنسرسیوم وب ارائه شده‌است. این فایل حاوی یک سری دستورالعمل برای برنامه‌های پردازشگر است که به آن‌ها می‌گوید چگونه این فایل را تبدیل کنند.

اساس کار XSLT
در تصویر زیر، فایل xml به همراه xslt، به تجزیه کننده یا تحلیل کننده داده میشوند. در این قسمت هر دو فایل منبع تحلیل شده و از روی فایل xml، درختی در حافظه تهیه می‌شود و از فایل xslt یک سری قوانین استخراج می‌شوند. بعد این دو محتوای جدید تولید شده، در اختیار XSL Processor قرار گرفته و از روی آن‌ها به ساخت درخت نتیجه (نوع درخواستی) در حافظه می‌رسد که در نهایت آن را به نام یک فایل مستند میکند.

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

پروژه نمونه
در این مقاله ما یک فایل xml داریم که قصد داریم آلبوم‌هایی را طبق ساختار زیر، در آن قرار دهیم و سپس بر اساس قوانین xslt آن را در قالب یک فایل html نشان دهیم. فایل‌های تمرینی این مقاله در این آدرس قابل دسترسی است.

ساختار فایل xml:
<Albums>
  <Album>
    <name>Modern Talking</name>
    <cover>http://album.com/a.jpg</cover>
    <Genres>
      <Genre>POP</Genre>
      <Genre>Jazz</Genre>
      <Genre>Classic</Genre>
    </Genres>
    <Description>
      this is a marvelous Album
    </Description>
    <price>
      25.99$
    </price>
  </Album>
</Albums>

ساختار فایل Html
در ابتدا فایل، برای معرفی فایل و رعایت قرارداد، فضای نام مربوطه را یا به شکل زیر
<xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
یا بدین شکل
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
می‌نویسیم و سپس کل کدهای html را با تگ زیر محصور می‌کنیم:
<xsl:template match="/">
<html>
....
</html>
</xsl:template>
هر آدرسی که در match بنویسید، آدرس‌های دستورات داخلی در ادامه‌ی آن خواهند بود.

نمایش فیلدها
سپس در آن، کد html مورد نظر را وارد می‌کنیم و در میان این کدها، تگ‌های xslt را وارد می‌نماییم. کد زیر بخشی از صفحه هست که برای نمایش آلبوم‌ها استفاده می‌کنیم:
<div>
  <img src="" alt="image" />
  <h3>
    <xsl:value-of select="albums/album/name" />
  </h3>
  <h4>Artist Name 1</h4>
  <h5>POP</h5>
  <h6>25/5/2015</h6>
  <h7>this is description</h7>
  <div>
    <a href="#">More</a>
  </div>
</div>
تگ‌های xsl نشان دهنده دستورالعمل‌های این قالب هستند. این دستور وظیفه انتخاب یک آیتم و نمایش آن را دارد. در قسمت select، آدرس نام آلبوم را به صورت یک مسیر، از تگ والد به سمت پایین وارد کرده‌ایم. این دستور العمل با اولین گره که به آن برسد، نمایش می‌یابد ولی اگر بخواهیم کدهای بالا، به تعداد هر آیتم (آلبوم) تکرار شود، از دستور العمل حلقه استفاده می‌کنیم:
<xsl:for-each select="albums/album">
  <div>
    <img src="" alt="image" />
    <h3>
      <xsl:value-of select="name" />
    </h3>
    <h4>Artist Name 1</h4>
    <h5>POP</h5>
    <h6>25/5/2015</h6>
    <h7>this is description</h7>
    <div>
      <a href="#">More</a>
    </div>
  </div>
</xsl:for-each>
خطوط بالا، مرتبا گره‌های album را در گره Albums، یافته و همه تگ‌های داخلش را تکرار کرده و نام هر آلبوم را نیز چاپ می‌کند. موقعی که از حلقه استفاده می‌کنیم و مسیر گره والد در آن مشخص شده است، نیازی نیست که دیگر برای دریافت نام آلبوم، کل مسیر را ذکر کنید.

ایجاد ارتباط میان دو فایل XML و XSLT
برای اجرا و تست آن باید از طریق یک ابزار که توانایی تحلیل این دستورات را دارد، استفاده کنید. یکی از همین ابزارها، مرورگر شماست. برای اینکه به مرورگر ارتباط فایل xml و xsl را بفهمانیم، تکه کد زیر را در فایل xml جهت لینک شدن می‌نویسیم و سپس فایل xml را در مرورگر اجرا می‌کنیم:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="index.xslt"?>
الان تصویر بدین شکل نمایش داده می‌شود:



ساخت المان یا تگ
در ادامه بقیه فیلدها را تکمیل میکنیم. فیلد بعدی تصویر است که تصویر دیگر مانند متن، بین تگ‌ها قرار نمی‌گیرد و باید داخل attribute تگ تصویر درج شود. برای نمایش تصویر می‌توانیم به دو شکل عمل کنیم. کد را به صورت زیر بنویسیم:
<img src="{cover}" width="200px" height="200px">
  <xsl:value-of select="cover" />
</img>
یا اینکه المان مورد نظر را توسط xsl ایجاد کنیم:
<xsl:element name="img">
  <xsl:attribute name="src">
    <xsl:value-of select="cover"/>
  </xsl:attribute>
  <xsl:attribute name="width">
    200px
  </xsl:attribute>
  <xsl:attribute name="height">
    200px
  </xsl:attribute>
  <xsl:value-of select="cover"/>
</xsl:element>
حال با یکی از کدهای بالا، مرورگر را جهت تست اجرا می‌کنیم:


دستور العمل‌های بیشتر (مرتب سازی)
دستورات xslt فقط به چند تگ بالا خلاصه نمی‌شود؛ بلکه دستورات شرطی ،تعریف متغیرها، توابع داخلی جهت کار با اعداد، رشته‌ها و ... هم در آن وجود دارند. یکی از همین دستورات جذاب، مرتب سازی است. دستورالعل sort به ما اجازه می‌دهد تا بر اساس یک فیلد، داده‌ها را مرتب کنیم. با افزودن کد زیر بعد از دستور حلقه، لیست را بر اساس نام آلبوم مرتب می‌کنیم:
<xsl:for-each select="Albums/Album">
<xsl:sort select="name"/>

دستورات شرطی
حال تصمیم می‌گیریم که آلبوم‌های با قیمت بالاتر از 10 دلار را 2 دلار تخفیف دهیم. برای اینکار نیاز است تا با تگ if آشنا شویم:
<xsl:variable name="numprice" select="number(substring(price,1,string-length(price)-1))" />

<xsl:if test="$numprice>=10">
  <h4 >
    <span style='text-decoration:line-through'>
      <xsl:value-of select="price" />
    </span>
    <b>
      <xsl:value-of select="$numprice -2" />$
    </b>
  </h4>
</xsl:if>
در اینجا کمی مثال را پیچیده‌تر و از چند عنصر جدید در آن استفاده کرده‌ایم. در خط اول یک متغیر با نام numprice تعریف کرده‌ایم. متغیرها بر دو نوعند: محلی یا عمومی. برای تعریف متغیر عمومی لازم است آن را در بالای سند، بعد از تگ template ایجاد کنید و متغیرهای محلی فقط در داخل همان تگی که تعریف کرده‌اید، اعتبار دارند. از آنجا که قیمت‌ها به صورت رشته‌ای هستند و در انتها هم حرف $ را دارند، بهتر است که قیمت را بدون $ به عدد تبدیل کنیم تا بتوانیم بعدا در شرط، به عنوان عدد، مقایسه و در صورت صحت شرط، دو عدد از آن کم کنیم. در اینجا ما از تابع substring برای جداسازی رشته و از تابع string-lentgh برای دریافت طول رشته و در نهایت از تابع number برای تبدیل رشته به عدد استفاده کردیم و آن‌را برای استفاده‌های بعدی، در متغیر ذخیره کردیم (برای آشنایی بیشتر با این توابع به این آدرس رجوع کنید). سپس تگ if را صدا زدیم و در صورتی که مقدار داخل متغیر (علامت متغیر، استفاده از عبارت $ قبل از نام آن است ) از 10 بیشتر یا برابر آن بود، دو واحد از آن کم می‌کنیم و روی قیمت قبلی خط می‌کشیم. نتیجه حاصله در تصویر زیر مشخص است:

در حال حاضر مشکلی که وجود دارد این است که ما برای قیمت‌های زیر 10 دلار هیچ شرطی نداریم و این دستور if کمی سطحی برخورد می‌کند و برای قیمت‌های زیر 10 دلار مجددا به یک if نیازمندیم. ولی دستور دیگری، مشابه دستور switch وجود دارد که استفاده از آن در شرایط دو شرط بالا مقرون به صرفه است. نام این دستور choose می‌باشد. خطوط بالا را به شکل زیر تغییر می‌دهیم:
<xsl:variable name="numprice" select="number(substring(price,1,string-length(price)-1))" />
<xsl:choose>
  <xsl:when test="$numprice>=10">
    <h4 >
      <span style='text-decoration:line-through;color:red'>
        <xsl:value-of select="price" />
      </span>
      <b>
        <xsl:value-of select="$numprice -2" />$
      </b>
    </h4>
  </xsl:when>

  <xsl:otherwise>
    <b>
      <xsl:value-of select="$numprice" />$
    </b>
  </xsl:otherwise>
</xsl:choose>
شما می‌توانید به تعداد زیادی از تگ when برای اعمال دیگر شرط‌ها همانند دستور switch ...case استفاده کنید. در اینجا اگر قیمت‌ها زیر 10 دلار باشند، تغییری در قیمت ایجاد نکرده و خودش را نشان می‌دهیم. ولی اگر از 10 دلار به بالا باشد، قیمت‌ها دو دلار تخفیف می‌خورند. شکل زیر نتیجه حاصل از اضافه شدن کد بالاست:


استفاده از template ها
برای خلاصه سازی کار و جمع و جور کردن کدها می‌توان از template‌ها استفاده کرد. شما هم در ابتدا، یک قالب یا template را برای کل سند ایجاد کردید. حالا سعی ما این است که اینبار، قالب‌های کوچکتر و جرئی‌تر و اختصاصی‌تر ساخته و آن‌ها را در قالب اصلی صدا بزنیم. برای ساخت قالب به ریشه xsl:stylesheet رفته و یک template جدید را به شکل زیر ایجاد میکنیم:
<xsl:template match="DateOfRelease">

  <xsl:variable name="date" select="."/>
  <xsl:variable name="day" select="substring($date,1,2)"/>
  <xsl:variable name="month" select="number(substring($date,4,2))"/>
  <xsl:variable name="year" select="substring($date,7,4)"/>
  Release Date:

  <xsl:choose>
    <xsl:when test="$month = 1">
      <xsl:value-of select="$day"/>,Jan/<xsl:value-of select="$year"/>
    </xsl:when>
    <xsl:when test="$month = 2">
      <xsl:value-of select="$day"/>,Feb/<xsl:value-of select="$year"/>
    </xsl:when>
    <xsl:when test="$month = 3">
      <xsl:value-of select="$day"/>,Mar/<xsl:value-of select="$year"/>
    </xsl:when>
    <xsl:when test="$month = 4">
      <xsl:value-of select="$day"/>,Apr/<xsl:value-of select="$year"/>
    </xsl:when>
    <xsl:when test="$month = 5">
      <xsl:value-of select="$day"/>,May/<xsl:value-of select="$year"/>
    </xsl:when>
    <xsl:when test="$month = 6">
      <xsl:value-of select="$day"/>,Jun/<xsl:value-of select="$year"/>
    </xsl:when>

    <xsl:when test="$month = 7">
      <xsl:value-of select="$day"/>,Jul/<xsl:value-of select="$year"/>
    </xsl:when>
    <xsl:when test="$month = 8">
      <xsl:value-of select="$day"/>,Aug/<xsl:value-of select="$year"/>
    </xsl:when>
    <xsl:when test="$month = 9">
      <xsl:value-of select="$day"/>,Sep/<xsl:value-of select="$year"/>
    </xsl:when>
    <xsl:when test="$month = 10">
      <xsl:value-of select="$day"/>,Oct/<xsl:value-of select="$year"/>
    </xsl:when>
    <xsl:when test="$month = 11">
      <xsl:value-of select="$day"/>,Nov/<xsl:value-of select="$year"/>
    </xsl:when>

    <xsl:otherwise>
      <xsl:value-of select="$day"/>,Dec/<xsl:value-of select="$year"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
در این قالب ما روی فیلد تاریخ کار میکنیم و قصد داریم تاریخ را در قالب نوشتاری دیگری درج کنیم. عبارت نقطه (.) در select خط اول به معنای گره جاری است. بعد از ساخت قالب جدید آن را در محل مورد نظر در قالب اصلی یا ریشه صدا می‌زنیم:
<xsl:for-each select="Albums/Album">
  <xsl:sort select="name"/>
  <div>
    <img src="{cover}" width="200px" height="200px">
      <xsl:value-of select="cover" />
    </img>
    <h3>
      <xsl:value-of select="name" />
    </h3>
    <h4>POP</h4>

    <xsl:variable name="numprice" select="number(substring(price,1,string-length(price)-1))" />
    <xsl:choose>
      <xsl:when test="$numprice>=10">
        <h4 >
          <span style='text-decoration:line-through;color:red'>
            <xsl:value-of select="price" />
          </span>
          <b>
            <xsl:value-of select="$numprice -2" />$
          </b>
        </h4>
      </xsl:when>

      <xsl:otherwise>
        <h4 >
          <b>
            <xsl:value-of select="$numprice -2" />$
          </b>
        </h4>
      </xsl:otherwise>
    </xsl:choose>
    <h4>
      <!-- محل صدا زدن قالب-->
      <xsl:apply-templates select="DateOfRelease"/>
    </h4>
    <h4>
      <xsl:value-of select="Description"/>
    </h4>
    <div>
      <a href="#">More</a>
    </div>
  </div>
</xsl:for-each>


فیلترسازی
یکی از خصوصیات دیگری که فایل XML داشت، فیلد ژانر موسیقی بود و قصد داریم با استفاده از فیلترسازی، تنها سبک خاصی مثل سبک پاپ را نمایش دهیم و موسیقی‌هایی را که خارج از این سبک هستند، از نتیجه حذف کنیم. به همین علت دستور for-each را به شکل زیر تغییر میدهیم:

<xsl:for-each select="Albums/Album[contains(Genres/Genre, 'POP')]">
دستور بالا میگوید که حلقه را آلبوم‌ها حرکت بده، به شرطی که در مسیر Genres/Genre مقداری برابر POP بیابی. حالا اگر بخواهیم این شرط را معکوس کنیم میتوانیم عبارت را به صورت زیر درج کنیم:
<xsl:for-each select="Albums/Album[not(contains(Genres/Genre, 'POP'))]">
برای شکیل شدن کار، بهتر است که نام سبک‌ها را در کنار تصویر هم درج کنیم. به همین علت دستور حلقه را مورد استفاده قرار می‌دهیم:
<xsl:for-each select="Genres/Genre">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
,
</xsl:if>
</xsl:for-each>
دستور بالا شامل دو تابع جدید هست که مقدار برگشتی آن‌ها اندیس گره فعلی و اندیس آخرین گره می‌باشد و در عبارت شرطی ما تعیین کرده‌ایم که بین هر سبکی که می‌نویسد، یک علامت جدا کننده , هم قرار گیرد؛ به جز آخرین گره که دیگر نیازی به علامت جدا کننده ندارد. تصویر زیر آلبوم هایی را نشان میدهد که در سبک پاپ قرار گرفته‌اند:


تبدیل xml و xsl در دات نت

string xmlfile = Application.StartupPath + "\\sampleXML.xml";

//بارگذاری فایل قوانین در حافظه
XslTransform xslt = new XslTransform();
xslt.Load("sample-stylesheet.xsl");

//XPath ایجاد یک سند جدید بر اساس استاندارد 
XPathDocument xpath = new XPathDocument(xmlfile);

//آماده سازی برای نوشتن فایل نهایی
XmlTextWriter xwriter = new XmlTextWriter(xmlfile + ".html", Encoding.UTF8);

//نوشتن فایل مقصد بر اساس قوانین مشخص شده
xslt.Transform(xpath, null, xwriter, null);
xwriter.Close();
در صورتی که فایل XSL نیازی به تگ‌هایی داشته باشد که در فایل xml نیست و خودتان قصد دارید که آن‌ها را از این طریق وارد کنید، می‌توانید خطوط زیر را به کد بالا اضافه کنید:
XsltArgumentList xslArgs = new XsltArgumentList();
xslArgs.AddParam("logo", "", logo);
xslArgs.AddParam("name", "", name); 
....
xslt.Transform(xpath, xslArgs, xwriter, null);

مطالب
مقدمه‌ای بر LINQ بخش اول
کلمه‌ی LINQ مخفف Language Integrated Query یا زبان پرس و جوی یکپارچه می‌باشد. LINQ برای اولین بار در ویژوال استودیوی 2008 و دات نت فریم ورک 3.5 برای پرکردن خلع بین دنیای اشیاء برنامه نویسی (Object Oriented World) و دنیای داده‌ها (Data World) ارائه شد.

چرا LINQ؟ 
در نگاهی کلی، مزایایی که از طریق LINQ حاصل می‌شوند عبارتند از:
• کاهش حجم کدنویسی 
• درک بهتر از عملکرد کد‌های نوشته شده
• پس از یادگیری اصول LINQ به راحتی می‌توانید از این اصول پرس و جو نویسی برای کار بر روی مجموعه داده‌های مختلف استفاده کنید
• کنترل صحت کدهای پرس و جو‌ها در زمان کامپایل ( Compile-Time Type Checking )

اجزای سازنده‌ی LINQ
دو جزء اصلی سازنده‌ی LINQ عبارت است از:
  • Elements عناصر
  • Sequences توالی‌ها
توالی‌ها می‌توانند لیستی از اطلاعات مختلف باشند. هر آیتم در لیست را عنصر می‌گوییم. توالی نمونه‌ای از یک کلاس است که اینترفیس <IEnumarable<T را پیاده سازی کرده باشد. این اینترفیس تضمین می‌کند که توالی قابلیت پیمایش عناصر را دارد.
به آرایه‌ی تعریف شده‌ی زیر دقت کنید:
int[] fibonacci = {0, 1, 1, 2, 3, 5};
متغیر fibonacci در اینجا نشان دهنده‌ی توالی و هر یک از اعداد آرایه، یک عنصر محسوب می‌شوند.
توالی می‌تواند درون حافظه‌ای باشد (In Memory Object) که به آن Local Sequence می‌گویند و یا می‌تواند یک بانک اطلاعاتی SQL Server باشد که به آن Remote Sequence می‌گویند.
در حالت Remote باید اینترفیس <IQuerable<T پیاده سازی شده باشد.
پرس و جو هایی را که بر روی توالی‌های محلی اجرا می‌شوند، اصطلاحا Local Query و یا LINQ-To-Object  نیز می‌نامند.
عملگرهای پرس و جوی  زیادی به شکل متد الحاقی در کلاس System.Linq.Enumerable طراحی شده‌اند. این مجموعه از عملگرهای پرس جو را اصطلاحا Standard Query Operator می‌گویند.
نکته‌ی مهم این است که عملگرهای پرس و جو تغییری را در توالی ورودی نمی‌دهند و نتیجه‌ی خروجی یک مجموعه جدید و یا یک مقدار عددی می‌باشد.

توالی خروجی و مقدار بازگشتی Scalar
در بخش قبل گفتیم که خروجی یک پرس و جو می‌تواند یک مجموعه و یا یک مقدار عددی باشد. در مثال زیر عملگر Count را بر روی مجموعه‌ی fibonacci  اعمال کردیم و عددی که نشان دهنده‌ی تعداد عناصر مجموعه است، بعنوان خروجی بازگردانده شده است.
int[] fibonacci = { 0, 1, 1, 2, 3, 5 };
int numberOfElements = fibonacci.Count();
Console.WriteLine($"{numberOfElements}");
IEnumerable<int> distinictNumbers = fibonacci.Distinct();
Console.WriteLine("Elements in output sequence:");
foreach (var number in distinictNumbers)
{
    Console.WriteLine(number);
}
در کد بالا توسط تابع Distinct، عناصر یکتا را از توالی ورودی استخراج کرده و بازگردانده‌ایم.
خروجی برنامه‌ی فوق به شکل زیر است :
6
Elements in output sequence:
0
1
2
3
5

مفهوم Deffer Execution  (
اجرای به تاخیر افتاده )
عمده‌ی عملگر‌های پرس و جو بلافاصله پس از ایجاد، اجرا نمی‌شوند. این عملگرها در طول اجرای برنامه اجرا خواهند شد (اجرای با تاخیر). به همین خاطر می‌توان بعد از ساخت پرس و جو  تغییرات دلخواهی را به توالی ورودی اعمال کرد.
در کد زیر  قبل از اجرای پرس و جو ، توالی ورودی ویرایش شده :
int[] fibonacci = { 0, 1, 1, 2, 3, 5 };
// ایجاد پرس و جو 
IEnumerable<int> numbersGreaterThanTwoQuery = fibonacci.Where(x => x > 2);
// در این مرحله پرس و جو ایجاد شده ولی هنوز اجرا نشده است
// تغییر عنصر اول توالی
fibonacci[0] = 99;
// حرکت بر روی عناصر توالی باعث اجرای پرس و جو می‌شود
foreach (var number in numbersGreaterThanTwoQuery)
{
   Console.WriteLine(number);
}
پرس و جو تا زمان اجرای حلقه‌ی Foreach اجرا نخواهد شد. خروجی مثال بالا به شکل زیر است :
99
3
5

به غیر از بعضی از عملگرها مثل Count,Min,Last سایر عملگر‌ها بصورت اجرای با تاخیر عمل می‌کنند. عملگری مثل Count باعث اجرای فوری پرس و جو می‌شود.
تعدادی عملگر تبدیل (Conversion Operator) هم وجود دارد که باعث می‌شوند پرس و جو بلافاصله اجرا شود :
• ToList
• ToArray
• ToLookup
• ToDictionary
عملگر‌های فوق پس از اجرا، خروجی را در یک ساختمان داده‌ی جدید باز می‌گردانند.
در کد زیر اصلاح توالی متغیر Fibonacci بعد از اجرای تابع ToArray صورت گرفته است.
int[] fibonacci = { 0, 1, 1, 2, 3, 5 };
// ساخت پرس و جو
IEnumerable<int> numbersGreaterThanTwoQuery = fibonacci.Where(x => x > 2) .ToArray();
// در این مرحله به خاطر عملگر استفاده شده پرس و جو اجرا می‌شود
// تغییر اولین عنصر توالی
fibonacci[0] = 99;
// حرکت بر روی نتیجه
foreach (var number in numbersGreaterThanTwoQuery)
{
   Console.WriteLine(number);
}
خروجی مثال بالا:
3
5
همانطور که می‌بینید عدد 99 در خروجی مشاهده نمی‌شود. علت فراخوانی عملگر ToArray است که بلافاصله باعث اجرای پرس و جو شده و خروجی را باز می‌گرداند . به همین خاطر تغییر عنصر اول توالی ورودی، تاثیری بر روی نتیجه‌ی خروجی ندارد. 
مطالب
قابل ویرایش کننده‌ی فوق العاده x-editable ؛ قسمت دوم
در قسمت قبلی با نحوه‌ی اجرا و ویژگی‌های فنی و خصوصیات کدنویسی x-editable آشنا شدیم. غیر از این خصوصیات، خصوصیات دیگری هم هستند که فقط مختص نوع کنترلی هست که در قسمت type مشخص کرده‌اید.

کنترلهای زیر جهت ورود اطلاعات در ویرایشگر پشتیبانی می‌شوند:
  • text
  • textarea
  • select
  • date
  • datetime
  • dateui
  • combodate
  • html5types
  • checklist
  • wysihtml5
  • typeahead
  • typeaheadjs
  • select2 
text
 clear دکمه‌ای جهت حذف محتوای کادر متنی است. مقدار پیش فرض آن true است.
 escape  برای دفاع در برابر کدهای مخرب html به کار میرود و کاراکترهای مدنظر را در صورت true بودن غیرفعال می‌کند. البته اگر از خاصیت display استفاده کنید این گزینه تاثیرش را از دست خواهد داد.
 inputclass یک کلاس css را به کادر متنی اعمال می‌کند.
placeholder
مقدار داده شده را در صورتی که کادر متنی خالی باشد، نشان می‌دهد.
 tpl به معنی یک قالب. شما می‌توانید کد html تگ input خود را وارد کنید؛ ولی توصیه نمی‌شود.


 TextArea

همان خاصیت‌های قبلی را دارد بعلاوه rows که نمایانگر مقدار ارتفاع آن است.

select

خاصیت‌های escape,input,class و tpl را دارد به‌علاوه خاصیت‌های زیر:

 prepend  همانند گزینه پایینی است ولی قبل از آن داده‌های خود را اضافه می‌کند.
 source از آنجا که یک لیست، لیستی از آیتم‌ها را دارد و کاربر یکی از آن‌ها را انتخاب می‌کند، این بخش، منبع آیتم‌ها را معرفی می‌کند. این خاصیت چهار نوع داده می‌پذیرد: آرایه یا شیء‌ایی از مقادیر. تابعی که بعد از انجام هر عملی، اطلاعات به آن پاس می‌شوند و یا از نوع رشته که این رشته یک آدرس سمت سرور است که با درخواست از آن آدرس، اطلاعات را دریافت می‌کند.
 sourceCache
 اگه خاصیت بالا با آدرسی پر شده باشد که از سمت سرور بخواند، در دفعات بعدی مقدار دریافتی را از کش خواهد خواند.
 sourceError  یک پیام خطا هنگام بارگزاری اطلاعات
 sourceOptions  در صورتیکه قصد اضافه کردن پارامتری را به درخواست ایجکسی دارید. یک شیء از پارامترها را به آن نسبت می‌دهیم و برای رونویسی پارامترها از یک تابع استفاده می‌کنیم که نحوه‌ی تغییرات را قبلا در جدول شماره یک دیده‌اید.
 
date
خاصیت‌های مشترک قبلی : tpl,input,class,escape و clear است.
 datepicker  پیکربندی تقویم را بر عهده دارد. برای اطلاعات بیشتر در مورد پیکربندی تقویم به این لینک مراجعه فرمایید.
{ weekStart: 0, startView: 0, minViewMode: 0, autoclose: false }
 format قالب بندی فرمت تاریخ جهت ارسال به سرور\ حالت پیش فرض yyyy-mm-dd
مقادیری که میتوان به کار برد: yy   yyyy mm   m  dd   d
 viewformat  این فرمت هنگام نمایش به کار می‌آید و در صورتیکه مقدار عنصر در این قالب نباشد، آن را تبدیل می‌کند.


 datetime در بوت استراپ

کاملا مشترک با مورد قبلی.


dateUI

مختص JqueryUI است و کاملا مشترک با مورد قبلی.


combodate

موارد مشترک قبلی را دارد ولی به جای خاصیت datepicker از combodate استفاده می‌شود که پیکربندی آن در این لینک قرار دارد.


نوع‌های HTML 5

شامل موارد زیر است:

  • password
  • email
  • url
  • tel
  • number
  • range
  • time 
html5 شامل عناصر زیادی است که ویژگی‌های جالبی را مد نظر دارند؛ ولی ممکن است بعضی المان‌ها در بعضی مرورگرها کارآیی مناسبی نداشته باشند که در این صفحه سازگاری مرورگرها با این نوع المان‌ها ذکر شده است.
خاصیت‌های ذکر شده در مورد نوع text، در مورد آن‌ها نیز صدق می‌کند.

checklist
همانند نوع select است؛ فقط خاصیت separator را دارد که کارش جدا کردن مقادیر است و مقدار پیش فرض آن علامت ',' است.


wysihtml5
سورس و دمو ی این نوع ادیتور که بر پایه‌ی بوت استرپ بنا شده است و زحمت اضافه کردن کتابخانه‌ها به صفحه، بر عهده شماست.
مداخل زیر را به طور دستی به صفحه اضافه کنید:
<link href="js/inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.css" rel="stylesheet" type="text/css"></link>  
<script src="js/inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/wysihtml5-0.3.0.min.js"></script>  
<script src="js/inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.min.js"></script>
و همچنین اسکریپت x-editable برای کار با این عنصر را هم اضافه کنید:
<script src="js/inputs-ext/wysihtml5/wysihtml5.js"></script>
این فایل در بسته‌ای که دانلود کرده‌اید موجود است. شامل خاصیت‌های escape,inputclass,placeholder,tpl است و خاصیت wysihtml5 شامل تنظیمات و پیکربندی ادیتور است که پیکریندی آن را می‌توانید در اینجا مطالعه بفرمایید.

typeahead
این گزینه فقط مختص بوت استرپ 2 است و یک کنترل autocomplete به شمار می‌آید. منبع داده‌های آن از طریق خاصیت source به دو صورت آرایه و object تامین می‌گردد.
['text1', 'text2', 'text3' ...]

//or

[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]
شامل خاصیت‌های clear,escape,prepend,source,sourceOptions,sourceError,sourceCache,inputclass,tpl است و شامل خاصیت typeahead جهت پیکربندی آن می‌شود.

typeaheadjs
همانند قبلی است و بر اساس twitterBootstrap است و شامل همان خصوصیات قبلی است. تنها خصوصیت typeahead آن است که باید از این پیکربندی استفاده کنید.

Select2
این المان بر اساس این کتابخانه  سورس باز ایجاد می‌شود. و مستندات آن شامل جزئیات و پیکربندی آن می‌شود. برای معرفی آن فایل‌های زیر را به صفحه معرفی کنید.
<link href="select2/select2.css" rel="stylesheet" type="text/css"></link>  
<script src="select2/select2.js"></script>
برای دریافت استایل بوت استرپی آن این فایل را صدا بزنید:
<link href="select2-bootstrap.css" rel="stylesheet" type="text/css"></link>
نکته: در حال حاضر خاصیت autotext روی این المان جواب نمی‌دهد و می‌توانید از خاصیت data-value به جای آن استفاده کنید.

شامل خاصیت‌های inputclass , escape , placeolder , source , tpl می‌باشد و از select2 برای دریافت پیکربندی‌های کنترل استفاده می‌کند و علامت جدا کننده آن توسط viewseperator صورت می‌گیرد.


قالبی نو برای ویرایشگر

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

fn.editableform.template$
 مقدار پیش فرض آن که حتما باید شامل تگ فرم و کلاس‌های مدنظر باشد:
    <form>
        <div>
             <div><div></div><div></div></div>
             <div></div>
        </div> 
    </form>
در صورتی که قصد تغییر کلاس‌های آن را دارید باید کلاس‌های زیر را رونویسی کنید:
  • control-group
  • editable-input
  • editable-buttons
  • editable-error-block

fn.editableform.buttons$ 
    <button type="submit">ok</button>
    <button type="button">cancel</button>
کلاس‌های editable-sumit و editable-cancel به طور خودکار به کلاس editable-buttons تزریق می‌شوند.
و نهایتا جهت تغییر loading

fn.editableform.loading$  
<div></div>

گاهی اوقات نیاز است که خصوصیات این ویرایشگر را در شرایط متغیر صفحه کنترل کنیم، برای مثال گاهی پیش می‌آید که بخواهید در یک شرایط خاص ویرایشگر یک المان خاص را غیرفعال کنید. کد زیر مثال این تغییرات است.
$('#favsite').editable('option', 'disabled', false);

متدها و رویدادها


متدهایی که روی آن قابل اجراست:
editable
ویرایشگر را بر اساس مقادیر اولیه روی عنصر مشخص شده فعال می‌کند.
() activate  فوکوس را به input ویرایشگر باز می‌گرداند.
() destory  حذف ویژگی ویرایش از روی عنصر
() disable  غیرفعال کردن ویرایشگر
() enable  فعال سازی آن
 ()getvalue
باعث بازگردانی مقدار جاری همه عناصر توسط شیء جفت کلید مقدار می‌شود و عناصری که شامل متن یا مقداری نیستند، از آن حذف می‌شوند. در صورتیکه قصد دارید مقدار تنها یک عنصر قابل دریافت باشد، با خاصیت isSingle آن را true کنید.
    $('#username, #fullname').editable('getValue');
    //result:
    {
    username: "superuser",
    fullname: "John"
    }
    //isSingle = true
    $('#username').editable('getValue', true);
    //result "superuser"
 ()hide  مخفی کردن تگ فرم ویرایشگر
(option(key,value
 تغییر خصوصیات یک عنصر که در بالا هم نمونه کد آن را دیدیم.
(setvalue(value,convertStr  ست کردن مقدار جدید کنترل و پارامتر دوم وضعیت تبدیل این مقدار به فرمت داخلی است که برای آن تعریف شده است مثل date
() show  نمایش ویرایشگر
( submit(options  در صورتی که خاصیت ارسال خودکار به سمت سرور را غیر فعال کرده باشید، با این گزینه می‌توانید همه اطلاعات و تغییرات را ارسال کنید. برای ایجاد فرم بر اساس ویرایشگرها و ارسال اطلاعات با کلیک بر روی دکمه submit کاربرد دارد. یک مثال در این زمینه .
پارامترهای options به شرح زیر هستند:
url
data
ajaxoptions
(error(obj
(success(obj,config

از نسخه 1.5.1 میتوان این گزینه را به راحتی روی یک المان خاص هم صدا زد:
$('#username').editable('submit')
() toggle  کدی که صدا زده می‌شود بین دو وضعیت show و hide سوئیچ می‌کند.
() toggleDisabled  تغییر وضعیت بین دو حالت enable و disable
() validate  انجام اعتبارسنجی بر روی همه کنترل ها.
    $('#username, #fullname').editable('validate');
    // possible result:
    {
      username: "username is required",
      fullname: "fullname should be minimum 3 letters length"
    }


رویدادها

 hidden این رویداد زمانی رخ می‌دهد که ویرایشگر دیگر قابل مشاهده نیست و شامل دو پارامتر event و reason است. reason دلیل اینکه چرا ویرایشگر از دید خارج شده است را با یکی از گزینه‌های زیر مشخص می‌کند.
save
cancel
onblur
nochange
manual

    $('#username').on('hidden', function(e, reason) {
        if(reason === 'save' || reason === 'cancel') {
            //auto-open next editable
            $(this).closest('tr').next().find('.editable').editable('show');
        } 
    });
init
موقعی صدا زده میشود که متد editable روی عنصر صدا زده می‌شود و به یاد داشته باشید که این رویداد باید قبل از آن ست شده باشد.
    $('#username').on('init', function(e, editable) {
        alert('initialized ' + editable.options.name);
    });
    $('#username').editable();
save
 موقعی که مقدار جدید، با موفقیت تایید می‌شود. دو پارامتر event و params را باز می‌گرداند که params شامل دو خصوصیت newValue و response است که به ترتیب مقدار جدید و اطلاعات برگشت داده شده از درخواست آژاکس است.
    $('#username').on('save', function(e, params) {
        alert('Saved value: ' + params.newValue);
    });
shown
موقعیکه ویرایشگر نمایش می‌یابد و فرم با موفقیت رندر شده است. برای اشیایی چون select باید صبر کنید تا مقادیر آن‌ها بارگذاری شوند.
    $('#username').on('shown', function(e, editable) {
        editable.input.$input.val('overwriting value of input..');
    });
 

حل مشکل این ابزار در کندو

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


برای حل این مشکل فایل kendo.common-xxx را باز کنید. xxx بر اساس قالبی که برای کندو انتخاب کرده‌اید، می‌تواند متفاوت باشد. در مثال‌های کندو عموما این xxx به نام default شناخته می‌شود یا برای مثال من، bootstrap بود.
بعد از اینکه باز کردید، به دنبال چنین استایلی بگردید:
div.k-treeview{
border-width: 0px;
background: transparent none repeat scroll 0px center;
overflow: auto;
white-space: nowrap;
}
خط زیر را از آن حذف کنید تا مشکل حل شود.
overflow: auto;

نکته بعدی اینکه وقتی ویرایشگر در حالت popup قرار می‌گیرد، مقدار خاصیت title نمایش می‌یابد که عموما با مضامینی چون "کلمه جدید را وارد نمایید" و ... پر می‌شود که به طور پیش فرض سمت چپ قرار گرفته است. کد زیر را در صفحه وارد کنید تا متن در سمت راست قرار بگیرد:
  .popover-title {
        text-align: right;
    }

مطالب
مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت هشتم - تنظیمات پروژه‌های React برای کار با Mobx decorators
تا اینجا دو مثالی را که از Mobx بررسی کردیم (مثال ورود متن و مثال کامپوننت شمارشگر)، به عمد به همراه decoratorهای @ دار آن نبودند. برای مثال در قسمت قبل، یک کلاس را با یک خاصیت ایجاد کردیم که روش مزین سازی خاصیت value آن را با  observable decorator، توسط متد decorate انجام دادیم و این هم یک روش کار با MobX است؛ بدون اینکه نیاز به تنظیمات خاصی را داشته باشد:
import { decorate } from "mobx";

class Count {
  value = 0;
}

decorate(Count, { value: observable });
const count = new Count();
اما اگر همان مثال بسیار ساده‌ی ورود متن را بخواهیم توسط decoratorهای @ دار MobX پیاده سازی کنیم ... پروژه‌ی استاندارد React ما کامپایل نخواهد شد که در این قسمت، روش رفع این مشکل را بررسی می‌کنیم.


بازنویسی مثال ورود متن و نمایش آن با Mobx decorators

در اینجا یک text-box، به همراه دو div در صفحه رندر خواهند شد که قرار است با ورود اطلاعاتی در text-box، یکی از آن‌ها (text-display) این اطلاعات را به صورت معمولی و دیگری (text-display-uppercase) آن‌را به صورت uppercase نمایش دهد. روش کار انجام شده هم مستقل از React است و به صورت مستقیم با استفاده از DOM API عمل شده‌است. این مثال را پیشتر در اولین قسمت بررسی MobX، ملاحظه کردید. اکنون اگر بخواهیم بجای شیءای که توسط متد observable کتابخانه‌ی MobX محصور شده‌است:
const text = observable({
  value: "Hello world!",
  get uppercase() {
    return this.value.toUpperCase();
  }
});
از یک کلاس ES6 به همراه Mobx decorators استفاده کنیم، به یک چنین پروژه‌ی جدیدی خواهیم رسید:
ابتدا یک پروژه‌ی جدید React را ایجاد می‌کنیم:
> create-react-app state-management-with-mobx-part3
> cd state-management-with-mobx-part3
> npm start
در ادامه کتابخانه‌ی mobx را نیز نصب می‌کنیم. برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save mobx
در ادامه، ابتدا فایل public\index.html را جهت نمایش دو div و یک text-box، ویرایش می‌کنیم:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>MobX Basics, part 3</title>
    <meta charset="UTF-8" />
    <link href="src/styles.css" />
  </head>

  <body>
    <main>
      <input id="text-input" />
      <p id="text-display"></p>
      <p id="text-display-uppercase"></p>
    </main>

    <script src="src/index.js"></script>
  </body>
</html>
سپس محتویات فایل src\index.js را نیز به نحو زیر تغییر می‌دهیم:
import { autorun, computed, observable } from "mobx";

const input = document.getElementById("text-input");
const textDisplay = document.getElementById("text-display");
const loudDisplay = document.getElementById("text-display-uppercase");

class Text {
  @observable value = "Hello World";
  @computed get uppercase() {
    return this.value.toUpperCase();
  }
}

const text = new Text();

input.addEventListener("keyup", event => {
  text.value = event.target.value;
});

autorun(() => {
  input.value = text.value;
  textDisplay.textContent = text.value;
  loudDisplay.textContent = text.uppercase;
});
تنها تفاوت این نگارش با نگارش قبلی آن، استفاده از کلاس Text که یک کلاس ES6 به همراه MobX Decorators است، بجای یک شیء ساده‌ی جاوا اسکریپتی می‌باشد. در اینجا خاصیت value به صورت observable@ تعریف شده و در نتیجه‌ی تغییر مقدار آن در کدهای برنامه، خاصیت محاسباتی وابسته‌ی به آن یا همان uppercase که با computed@ تزئین شده، به صورت خودکار به روز رسانی خواهد شد. متد autorun نیز به این تغییرات که حاصل فشرده شدن کلیدها هستند، واکنش نشان داده و متن دو div موجود در صفحه را به روز رسانی می‌کند.

اکنون اگر در همین حال، برنامه را با دستور npm start اجرا کنیم، با خطای زیر متوقف خواهیم شد:
./src/index.js
SyntaxError: \src\index.js: Support for the experimental syntax 'decorators-legacy' isn't currently enabled (8:3):

   6 | 
   7 | class Text {
>  8 |   @observable value = "Hello World";
     |   ^
   9 |   @computed get uppercase() {
  10 |     return this.value.toUpperCase();
  11 |   }

راه حل اول: از Decorators استفاده نکنیم!

یک راه حل مشکل فوق این است که بدون هیچ تغییری در ساختار پروژه‌ی React خود، اصلا از decorator syntax استفاده نکنیم. برای مثال اگر یک کلاس متداول MobX ای چنین شکلی را دارد:
import { observable, computed, action } from "mobx";

class Timer {
  @observable start = Date.now();
  @observable current = Date.now();

  @computed
  get elapsedTime() {
    return this.current - this.start + "milliseconds";
  }

  @action
  tick() {
    this.current = Date.now();
  }
}
می‌توان آن‌را بدون استفاده از decorator syntax، به صورت زیر نیز تعریف کرد:
import { observable, computed, action, decorate } from "mobx";

class Timer {
  start = Date.now();
  current = Date.now();

  get elapsedTime() {
    return this.current - this.start + "milliseconds";
  }

  tick() {
    this.current = Date.now();
  }
}

decorate(Timer, {
  start: observable,
  current: observable,
  elapsedTime: computed,
  tick: action
});
نمونه‌ی این روش را در قسمت قبل با تعریف شیء شمارشگر مشاهده کرده‌اید. در اینجا با توجه به اینکه Decorators در جاوا اسکریپت چیزی نیستند بجز بیان زیبای higher-order functions و higher-order functions هم توابعی هستند که توابع دیگر را با ارائه‌ی قابلیت‌های بیشتری، محصور می‌کنند، به همین جهت هر کاری را که بتوان با تزئین کننده‌ها انجام داد، همان را با توابع معمولی جاوا اسکریپتی نیز می‌توان انجام داد. اینکار را در مثال فوق توسط متد decorate مشاهده می‌کنید. این متد ابتدا نوع کلاس خاصی را دریافت کرده و سپس در پارامتر دوم آن می‌توان شیءای را تعریف کرد که خواص آن، همان خواص کلاس پارامتر اول است و مقادیر این خواص، تزئین کننده‌هایی هستند که قرار است برای آن‌ها بکار گرفته شوند. مزیت این روش بدون تغییر باقی ماندن تعریف کلاس Timer در اینجا و همچنین انجام هیچگونه تغییری در ساختار پروژه‌ی React، بدون نیاز به نصب بسته‌های کمکی اضافی است.

همچنین در این حالت بجای استفاده از کامپوننت‌های کلاسی، باید از روش بکارگیری متد observer برای محصور کردن کامپوننت تابعی تعریف شده استفاده کرد (تا دیگر نیازی به ذکر observer class@ نباشد):
const Counter = observer(({ count }) => {
  return (
   // ...
  );
});


راه حل دوم: از تایپ‌اسکریپت استفاده کنید!

create-react-app امکان ایجاد پروژه‌های React تایپ‌اسکریپتی را با ذکر سوئیچ typescript نیز دارد:
> create-react-app my-proj1 --typescript
پس از ایجاد پروژه، فایل tsconfig.json آن‌را یافته و experimentalDecorators آن‌را به true تنظیم کنید:
{
  "compilerOptions": {
    // ...
    "experimentalDecorators": true
  }
}
این تنها تغییری است که مورد نیاز می‌باشد و پس از آن برنامه‌ی React جاری، بدون مشکلی می‌تواند با decorators کار کند.


فعالسازی MobX Decorators در پروژه‌های استاندارد React مبتنی بر ES6

MobX از legacy" decorators spec" پشتیبانی می‌کند. یعنی اگر پروژه‌ای از spec جدید استفاده کند، دیگر نخواهد توانست با MobX فعلی کار کند. این هم مشکل MobX نیست. مشکل اینجا است که باید دانست کلا decorators در زبان جاوااسکریپت هنوز در مرحله‌ی آزمایشی قرار دارند و تکلیف spec نهایی و تائید شده‌ی آن مشخص نیست.
برای فعالسازی decorators در یک پروژه‌ی React استاندارد مبتنی بر ES6، شاید کمی جستجو کنید و به نتایجی مانند افزودن فایل babelrc. به ریشه‌ی پروژه و نصب افزونه‌هایی مانند babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties@ برسید. اما ... این‌ها بدون اجرای دستور npm run eject کار نمی‌کنند و اگر این دستور را اجرا کنیم، در نهایت به یک فایل package.json بسیار شلوغ خواهیم رسید (اینبار ارجاعات به Babel، Webpack و تمام ابزارهای دیگر نیز ظاهر می‌شوند). همچنین این عملیات نیز یک طرفه‌است. یعنی از این پس قرار است کنترل تمام این پشت صحنه، در اختیار ما باشد و به روز رسانی‌های بعدی create-react-app را با مشکل مواجه می‌کند. این گزینه صرفا مختص توسعه دهندگان پیشرفته‌ی React است. به همین جهت نیاز به روشی را داریم تا بتوانیم تنظیمات Webpack و کامپایلر Babel را بدون اجرای دستور npm run eject، تغییر دهیم تا در نتیجه، decorators را در آن فعال کنیم و خوشبختانه پروژه‌ی react-app-rewired دقیقا برای همین منظور طراحی شده‌است.

بنابراین ابتدا بسته‌های زیر را نصب می‌کنیم:
> npm i --save-dev customize-cra react-app-rewired
بسته‌ی react-app-rewired، امکان بازنویسی تنظیمات webpack پروژه‌ی react را بدون eject آن میسر می‌کند. customize-cra نیز با استفاده از امکانات همین بسته، نگارش‌های جدیدتر create-react-app را پشتیبانی می‌کند.

پس از نصب این پیشنیازها، فایل جدید config-overrides.js را به ریشه‌ی پروژه، جائیکه فایل package.json قرار گرفته‌است، با محتوای زیر اضافه کنید تا پشتیبانی ازlegacy" decorators spec" فعال شوند:
const {
  override,
  addDecoratorsLegacy,
  disableEsLint
} = require("customize-cra");

module.exports = override(
  // enable legacy decorators babel plugin
  addDecoratorsLegacy(),

  // disable eslint in webpack
  disableEsLint()
);
در ادامه فایل package.json را گشوده و قسمت scripts آن‌را برای استفاده‌ی از react-app-rewired، به صورت زیر بازنویسی کنید تا امکان تغییر تنظیمات webpack به صورت پویا در زمان اجرای برنامه، میسر شود:
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
  },
پس از این تغییرات، نیاز است دستور npm start را یکبار دیگر از ابتدا اجرا کنید. اکنون برنامه بدون مشکل کامپایل شده و خروجی بدون خطایی در مرورگر نمایش داده خواهد شد.


تنظیمات ESLint مخصوص کار با decorators

فایل ویژه‌ی eslintrc.json. که در ریشه‌ی پروژه قرار می‌گیرد (این فایل بدون نام است و فقط از پسوند تشکیل شده)، برای پروژه‌های MobX، باید حداقل تنظیم زیر را داشته باشد تا ESLint بتواند legacyDecorators را نیز پردازش کند:
{
  "extends": "react-app",
  "parserOptions": {
    "ecmaFeatures": {
      "legacyDecorators": true
    }
  }
}
و یا یک نمونه‌ی غنی شده‌ی فایل eslintrc.json. مخصوص برنامه‌های React به صورت زیر است:
{
    "env": {
        "node": true,
        "commonjs": true,
        "browser": true,
        "es6": true,
        "mocha": true
    },
    "settings": {
        "react": {
            "version": "detect"
        }
    },
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true,
            "legacyDecorators": true
        },
        "ecmaVersion": 2018,
        "sourceType": "module"
    },
    "plugins": [
        "babel",
        "react",
        "react-hooks",
        "react-redux",
        "no-async-without-await",
        "css-modules",
        "filenames",
        "simple-import-sort"
    ],
    "rules": {
        "no-const-assign": "warn",
        "no-this-before-super": "warn",
        "constructor-super": "warn",
        "strict": [
            "error",
            "safe"
        ],
        "no-debugger": "error",
        "brace-style": [
            "error",
            "1tbs",
            {
                "allowSingleLine": true
            }
        ],
        "no-trailing-spaces": "error",
        "keyword-spacing": "error",
        "space-before-function-paren": [
            "error",
            "never"
        ],
        "spaced-comment": [
            "error",
            "always"
        ],
        "vars-on-top": "error",
        "no-undef": "error",
        "no-undefined": "warn",
        "comma-dangle": [
            "error",
            "never"
        ],
        "quotes": [
            "error",
            "double"
        ],
        "semi": [
            "error",
            "always"
        ],
        "guard-for-in": "error",
        "no-eval": "error",
        "no-with": "error",
        "valid-typeof": "error",
        "no-unused-vars": "error",
        "no-continue": "warn",
        "no-extra-semi": "warn",
        "no-unreachable": "warn",
        "no-unused-expressions": "warn",
        "max-len": [
            "warn",
            80,
            4
        ],
        "react/prefer-es6-class": "warn",
        "react/jsx-boolean-value": "warn",
        "react-hooks/rules-of-hooks": "error",
        "react-hooks/exhaustive-deps": "warn",
        "react/prop-types": "off",
        "react-redux/mapDispatchToProps-returns-object": "off",
        "react-redux/prefer-separate-component-file": "off",
        "no-async-without-await/no-async-without-await": "warn",
        "css-modules/no-undef-class": "off",
        "filenames/match-regex": [
            "off",
            "^[a-zA-Z]+\\.*\\b(typescript|module|locale|validate|test|action|api|reducer|saga)?\\b$",
            true
        ],
        "filenames/match-exported": "off",
        "filenames/no-index": "off",
        "simple-import-sort/sort": "error"
    },
    "extends": [
        "react-app",
        "eslint:recommended",
        "plugin:react/recommended",
        "plugin:react-redux/recommended",
        "plugin:css-modules/recommended"
    ],
    "globals": {
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly",
        "process": true
    }
}
البته برای اینکه این تنظیمات کار کند، باید افزونه‌های زیر را نیز به صورت محلی در ریشه‌ی پروژه‌ی جاری نصب کنید (این مورد از ESLint 6x به بعد اجباری است و از بسته‌های global استفاده نمی‌کند):
>npm i --save-dev eslint babel-eslint eslint-config-react-app eslint-loader eslint-plugin-babel eslint-plugin-react eslint-plugin-css-modules eslint-plugin-filenames eslint-plugin-flowtype eslint-plugin-import eslint-plugin-no-async-without-await eslint-plugin-react-hooks eslint-plugin-react-redux eslint-plugin-redux-saga eslint-plugin-simple-import-sort eslint-loader typescript
پس از آن می‌توان فایل config-overrides.js را به صورت زیر نیز بر اساس تنظیمات فوق، بهبود بخشید:
const {
  override,
  addDecoratorsLegacy,
  useEslintRc
} = require("customize-cra");

module.exports = override(
  addDecoratorsLegacy(),
  useEslintRc(".eslintrc.json")
);


رفع اخطار مرتبط با decorators در VSCode

تا اینجا کار تنظیم کامپایلر babel، جهت پردازش decorators انجام شد. اما خود VSCode نیز چنین اخطاری را در پروژه‌هایی که از decorates استفاده می‌کنند، نمایش می‌دهد:
Experimental support for decorators is a feature that is subject to change in a future release.
Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.ts(1219)
برای رفع آن، فایل جدید tsconfig.json را در ریشه‌ی پروژه ایجاد کرده و آن‌را به صورت زیر تکمیل کنید تا ادیتور تایپ‌اسکریپتی VSCode، دیگر خطاهای مرتبط با decorators را نمایش ندهد:
{
    "compilerOptions": {
        "experimentalDecorators": true,
        "allowJs": true
    }
}

کدهای کامل این قسمت را می‌توانید از اینجا دریافت کنید: state-management-with-mobx-part3.zip
مطالب
مروری بر کتابخانه ReactJS - قسمت هفتم - ورودی‌های کاربر

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


رویداد‌‌های ترکیبی  React

React روش مدیریت رویداد‌های خودش را دارد و به آنها رویداد‌های Synthetic یا ترکیبی گفته میشود. در زیر مقایسه‌ای داریم از رویداد‌های معمول در JavaScript و رویدادهای React و تفاوت‌ها را بررسی میکنیم.

<!-- HTML Buttons -->
<button type="button" onclick="console.log('Button Clicked')">Click Me</button>

// React Buttons
<button type="button" onClick={console.log("Button Clicked")}>Click Me</button>
  • باید نام رویداد‌ها را بصورت camelCase تایپ کنیم. 
  • از جاوااسکریپت به طور مستقیم استفاده میکنیم؛ نه بین quotation mark‌ها.
  • برای رویداد‌ها از توابع استفاده میکنیم و بهتر است تابع اجرایی هر رویداد در خود کامپوننت ساخته شود.
  • رویداد onClick در React به نوعی override شده رویداد onclick مرورگر است و به جای آن عمل میکند.

رفتار رویداد‌های React در مرورگر‌های مختلف یکسان است. برای مثال رویداد onChange هر تغییری را برای هر نوع تگ ورودی اعمال میکند. هر کلیدی که در یک input یا textarea زده شود، اگر یک check box را انتخاب یا از انتخاب خارج کنیم و یا اگر موردی را از یک drop-down انتخاب کنیم، React رویداد onChange را اجرا میکند. React اکثر رویداد‌های مرسوم را پوشش میدهد و همچنین رویداد‌هایی را برای کار با کلیپ‌برد، رسانه‌های مختلف و تصاویر دارد. برای اطلاعات بیشتر به مستندات آن رجوع کنید.

وقتی با کتابخانه React کار میکنیم، همه چیز مجازی اتفاق می‌افتد؛ مانند ساخت تگ و نمایش آنها، همچنین مدیریت تگ‌ها و رویدادها. اما به این معنا نیست که ارتباط React با HTML DOM در مرورگر قطع است. اگر لازم باشد به HTML DOM در کامپوننت‌ها دسترسی داشته باشیم میتوانیم از خاصیت ref در React استفاده کنیم. برای مثال فرض کنید یک ورودی را برای ایمیل به‌صورت <input type="email" /> تعریف کرده‌ایم. میخواهیم پیش از ذخیره بدانیم آیا داده وارد شده به فرمت ایمیل هست یا نه. 

const EmailForm = React.createClass({
    clickHandler() {
        if (this.inputEmail.checkValidity())
            console.log("Email is OK to save it.");
        else
            console.log("Email is not in right format.");
    },
    render() {
        return (
            <div>
                <input type="email" ref={inputEmail => this.inputEmail = inputEmail} />
                <button type="submit" onClick={this.clickHandler}>Save</button>
            </div>
        )
    }

در مثال بالا clickHandler وظیفه مدیریت رویداد کلیک دکمه را به عهده دارد. در ادامه، وقتی از خاصیت ref در تگ input استفاده میکنیم و مقدار آن را یک تابع قرار میدهیم، React این تابع را زمانیکه کامپوننت به طور کامل در HTML DOM ساخته شد، اجرا میکند. React همچنین ارجاعی را به عنوان پارامتر این تابع به DOM همراه با تابع ارسال میکند (inputEmail). داخل تابع ref میتوانیم به نمونه ساخته شده از کامپوننت در DOM دسترسی داشته باشیم. inputEmail که به صورت ارجاع به تابع فرستاده شده، تگ ساخته شده input را برمیگرداند، در نتیجه میتوانیم در کامپوننت به آن دسترسی داشته باشیم.

تغییر وضعیت کامپوننت

اگر از کامپوننت‌های Sateful که دارای وضعیت هستند استفاده میکنیم، میتوانیم وضعیت کامپوننت را بر اساس ورودی‌های کاربر تغییر دهیم. مثال بالا را به این شکل تغییر میدهیم که در ابتدا وضعیت کامپوننت، یک ایمیل پیش‌فرض باشد و اگر کاربر آدرس متفاوتی را وارد کرد، آدرس جدید به عنوان وضعیت جدید کامپوننت در نظر گرفته شود. 

const EmailForm = React.createClass({
    getInitialState() {
        return {
            currentEmail: this.props.currentEmail
        }
    },
    setCurrentEmailState(se) {
        this.setState({ currentEmail: se.target.value });
    },
    clickHandler() {
        if (this.inputEmail.checkValidity())
            console.log("Email is OK to save it.");
        else
            console.log("Email is not in right format.");
    },
    render() {
        return (
            <div>
                <input type="email" ref={inputEmail => this.inputEmail = inputEmail} 
                       value={this.state.currentEmail} onChange={this.setCurrentEmailState} />
                <button type="submit" onClick={this.clickHandler}>Save</button>
            </div>
        )
    }
})

در خط 20 از مثال بالا با قرار دادن مقدار value برابر با ایمیل جاری (وضعیت کامپوننت)، کاربر آدرس پیش‌فرض را در input میبیند، اما هیچ تغییری را نمیتواند در آن ایجاد کند و input عملا تبدیل به یک تگ فقط خواندنی میشود. علت این است که React دو وضعیت را ایجاد کرده، یکی در حافظه به عنوان وضعیت پیش‌فرض و دیگری وضعیتی که در DOM ساخته. وقتی در سطح DOM تغییری را ایجاد میکنیم، React به صورت خودکار متوجه آن نمیشود و ما باید با روشی React را در جریان این تغییرات قرار دهیم! برای این کار رویداد onChange را برای تگی که قرار است تغییر کند پیاده‌سازی میکنیم. در مثال بالا متد setCurrentEmailState و رویداد onChange برای همین منظور به کار گرفته شده‌اند. 

در قسمت بعد که آخرین قسمت است، به مسئله چرخه زندگی (Lifecycle) کامپوننت‌های React میپردازیم.

مطالب
Iterators در ES 6
یکی از اهداف ES 6، استاندارد سازی کار با Iterators و Iterables است. فرض کنید شیءایی را داریم که مجموعه‌ای از عناصر را در بر دارد. این مجموعه می‌تواند آرایه‌ای از عناصر باشد و یا set و map اضافه شده به ES 6 و یا حتی اشیایی که در زمان اجرا ایجاد می‌شوند. اگر این مجموعه Iterable باشد، حرکت بر روی آن یک Iterator را تولید می‌کند که امکان حرکت در این مجموعه را آیتم به آیتم میسر خواهد کرد:
 


هر Iterator شیءایی است که دارای متد next می‌باشد. هر بار که این متد فراخوانی می‌شود، عضو بعدی مجموعه، بازگشت داده خواهد شد. خروجی هر مرحله، درون یک شیء با دو خاصیت value و done قرار داده می‌شود. value‌، مقدار مرحله‌ی بعد است و done مشخص می‌کند که آیا به پایان مجموعه رسیده‌ایم یا خیر (بنابراین در اینجا تعداد اعضای Iterator مشخص نیست).


مثالی از پیمایش یک آرایه با چندین روش مختلف

در مثال زیر، آرایه‌ای از اعداد را داریم که نیاز است جمع اعضای آن محاسبه شود:
 let sum = 0;
let numbers = [1,2,3,4];
این‌کار را می‌توان با استفاده از یک حلقه‌ی for متداول انجام داد. از آنجائیکه آرایه دارای خاصیت length است، می‌توان از آن جهت تعیین حد بالایی آرایه استفاده کرد:
 // for loop
sum = 0;
for(let i =0; i < numbers.length; i++){
     sum += numbers[i];
}
//sum = 10
روش دوم انجام این‌کار، استفاده از حلقه‌ی for in است. این حلقه هربار ایندکس بعدی قابل استفاده‌ی آرایه را بازگشت می‌دهد که از آن می‌توان جهت دسترسی به اعضای آرایه استفاده کرد:
 // for in
sum = 0;
for(let i in numbers) {
    sum += numbers[i];
}
//sum = 10
روش دیگر انجام این‌کار با استفاده از Iterators است:
 // iterator
sum = 0;

let iterator = number.values();
let next = iterator.next();
while(!next.done){
    sum += next.value;
    next = iterator.next();
}
//sum = 10
برای کار با Iterators، ابتدا باید یک شیء Iterator را از مجموعه‌ایی که در اختیار داریم، تولید کنیم. برای مثال آرایه‌ها دارای متدی به نام values هستند که با فراخوانی ()number.values، سبب تولید یک Iterator می‌شوند. این Iterator امکان حرکت بین مقادیر آرایه را در اینجا میسر می‌کند.
مرحله‌ی بعدی، فراخوانی متد next این Iterator است. این عملیات باید در طی یک حلقه، تا پایان کار Iterator انجام شود. همانطور که در ابتدای بحث نیز عنوان شد، خروجی متد next یک شیء است که دارای خواص value و done می‌باشد. اگر done مساوی true شد، یعنی به پایان کار پیمایش رسیده‌ایم.
البته هدف از این مثال، صرفا نمایش سطح پایین کار با Iterators بود. در عمل از حلقه‌ی جدیدی به نام for of برای انجام این پیمایش استفاده می‌شود.


معرفی حلقه‌ی for of

جاوا اسکریپت سال‌ها است که دارای حلقه‌ی for in می‌باشد و نمونه‌ای از کاربرد آن‌را در مثال قبل مشاهده کردید. اگر این حلقه بر روی آرایه‌ها فراخوانی شود، هربار ایندکس پیمایش شده را بازگشت می‌دهد و اگر بر روی یک شیء فراخوانی شود، خواص آن شیء را بازگشت می‌دهد:
 var person = { first: "Vahid", last "N" };
for(let i in person) {
   console.log(person[i]);
}
در این مثال، i بار اول به first و بار دوم به خاصیت last اشاره می‌کند.
چون این حلقه صرفا ایندکس‌ها و کلیدها را بازگشت می‌دهد، جهت کار با Iterators که نیاز است به مقادیر اعضاء دسترسی پیدا کنیم، مناسب نیست. به همین جهت در ES 6، حلقه‌ی جدیدی به نام for of برای کار با Iterators معرفی شده‌است:
 let numbers = [1,2,3,4];
for(let i of numbers) {
   console.log(i);
}
این حلقه برخلاف for in، بر روی values، کار پیمایش را انجام می‌دهد. همچنین به صورت خودکار در پشت صحنه متد next یک Iterator را فراخوانی می‌کند و خاصیت done آن‌را بررسی کرده و زمانیکه این خاصیت مساوی true شد، حلقه را خاتمه می‌دهد. برای نمونه مثال سطح پایین while دار ابتدای بحث را به نحو ساده‌تری با حلقه‌ی for of ذیل می‌توان جایگزین کرد:
let sum = 0;
let numbers = [1,2,3,4];

for(let n of numbers){
   sum += n;
}

for of یکی از روش‌های پیمایش Iterators است. پارامترهای rest و همچنین Array.from نیز چنین قابلیتی را فراهم می‌کنند.

امکان پیاده سازی Iterators سفارشی نیز وجود دارد که پیشنیاز آن، درک مبحث جدید Symbols است که به صورت جداگانه‌ای بررسی خواهد شد.
مطالب
کار با Docker بر روی ویندوز - قسمت دوم - نصب Docker
پس از آشنایی با مفهوم Containers، در این قسمت قصد داریم برنامه‌ی تقریبا 500 مگابایتی Docker for Windows Installer.exe را نصب کنیم.
 
پیش‌نیازهای نصب Docker بر روی ویندوز

مطابق مستندات آن، برای نصب داکر بر روی ویندوز به حداقل‌های زیر نیاز است:
- استفاده از ویندوز 10 نگارش enterprise، که شماره نگارش آن حداقل 1607 باشد (حداقل Anniversary Update باشد).
- مجازی سازی در BIOS فعال شده باشد.
البته مجازی سازی عموما به صورت پیش‌فرض فعال است. برای بررسی آن، taskmanager ویندوز را اجرا کرده و در برگه‌ی Performance آن، جائیکه مشخصات CPU را نمایش می‌دهد، یک سطر به Virtualization اختصاص دارد که مقدار آن باید enabled باشد (تصویر زیر) و اگر نیست، برای فعال کردن آن باید به تنظیمات BIOS سیستم خود مراجعه کنید:


روش دیگر دریافت این اطلاعات، اجرای دستور systeminfo در خط فرمان، با دسترسی مدیریتی است. در خروجی آن، عبارت «Virtualization Enabled In Firmware» را جستجو کنید که باید مقدار آن yes باشد.

- داشتن CPU با قابلیت SLAT یا Second Level Address Translation.
برای یافتن این موضوع، برنامه‌ی coreinfo را دریافت کرده و آن‌را به صورت coreinfo -v اجرا کنید. خروجی آن سه سطر مرتبط با مجازی سازی را به همراه دارد. اگر قابلیتی موجود نباشد، جلوی آن یک خط تیره و اگر قابلیتی موجود باشد، روبروی آن یک ستاره را مشاهده خواهید کرد.

روش دیگر بررسی آن، اجرای دستور msinfo32 در قسمت run ویندوز و سپس enter است. در قسمت system summary، اطلاعات Second Level Address Translation قابل مشاهده هستند (اگر No باشد، امکان اجرای containerهای لینوکسی را بر روی ویندوز نخواهید داشت):


- داشتن حداقل 4 گیگابایت RAM.
- فعال بودن Hyper-V نیز برای اجرای Linux Containers بر روی ویندوز، ضروری است (نصاب Docker، این‌کار را به صورت خودکار انجام می‌دهد).


دریافت نصاب Docker for Windows

برای دریافت نصاب داکر مخصوص ویندوز، به آدرس زیر مراجعه کنید:
https://store.docker.com/editions/community/docker-ce-desktop-windows
که بلافاصله با تصویر کریه زیر مواجه خواهید شد:


برای رفع این مشکل، می‌توان از روش مطرح شده‌ی در مطلب «یک روش ساده برای دور زدن تحریم‌ها!» استفاده کرد؛ یعنی تنظیم DNS به 178.22.122.100 به صورت زیر:


پس از این تغییر، چون IP قابل مشاهده‌ی سیستم شما توسط سایت داکر تغییر می‌کند، اینبار صفحه‌ی دریافت Docker Community Edition for Windows به صورت زیر ظاهر می‌شود:


همانطور که مشاهده می‌کنید، عنوان کرده‌است که لطفا لاگین کنید تا بتوانید این برنامه را دریافت کنید. به همین جهت بر روی لینک آن کلیک کرده، یک اکانت جدید را در سایت docker ایجاد کنید (با یک ایمیل واقعی که تائیدیه آن‌را دریافت خواهید کرد). پس از آن، با این اکانت جدید به سایت داکر وارد شوید تا لینک دریافت فایل exe نصاب آن‌را دریافت کنید.
در این حالت مرورگر و یا حتی دانلودمنیجر شما بدون مشکل می‌توانند این فایل را دریافت کنند و همان تنظیم DNS فوق، مشکل عدم دسترسی را برطرف می‌کند.


نصب Docker for Windows

پس از اجرای نصاب آن و پایان عملیات نصب (که تنها کافی است در صفحه‌ی ابتدایی آن تیک مربوط به Windows Containers را نیز قرار دهید)، نیاز دارد تا شما را یکبار از سیستم Logout و login کند. پس از ورود به سیستم، تنظیمات ابتدایی آن به صورت خودکار صورت گرفته و در صورت فعال نبودن Hyper-V، پیام زیر را مشاهده خواهید کرد:


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

یک نکته: کاری که در قسمت فعالسازی Hyper-V به صورت خودکار انجام می‌شود، شامل اجرای سه دستور زیر، در کنسول پاور شل، با دسترسی مدیریتی و سپس ری استارت سیستم است:
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All -Verbose
Enable-WindowsOptionalFeature -Online -FeatureName Containers -All -Verbose
bcdedit /set hypervisorlaunchtype Auto
پس از آن، خط فرمان را باز کرده و با ستفاده از docker CLI نصب شده، دستور docker info را صادر کنید، تا بتوانید مشخصات نگارش نصب شده را مشاهده نمائید.
C:\Users\Vahid>docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 18.06.1-ce
OSType: windows
OSType را در صورتیکه سیستم شما توانمندی‌های سخت افزاری لازم را داشته باشد، می‌توان به Linux نیز تغییر داد.


بررسی تنظیمات سوئیچ کردن بین Linux Containers و Windows Containers

پس از اتمام ری‌استارت‌ها، برای آزمایش فعال بودن Hyper-V، در قسمت Run ویندوز، عبارت Virtmgmt.msc را نوشته و enter کنید. اگر تصویر زیر را مشاهده نمی‌کنید:


یکبار بر روی آیکن Docker در قسمت Tray Icons ویندوز کلیک راست کرده و گزینه‌ی switch to Linux containers را انتخاب کنید تا پس از مدتی، آیکن MobyLinuxVM در قسمت virtual machines (تصویر فوق) ظاهر شود.


اگر پس از انتخاب این گزینه، پیام زیر را دریافت کردید:


و یا اگر بر روی این ماشین مجازی کلیک راست کردید و گزینه‌ی Start آن‌را انتخاب کردید و پیام زیر ظاهر شد:


قسمت «پیش‌نیازهای نصب Docker بر روی ویندوز» را با دقت بررسی کنید (خصوصا قسمت BIOS و SLAT). نبود یکی از موارد ذکر شده، سبب بروز این مشکل می‌شود.
برای مثال اجرای دستور coreinfo -v بر روی سیستم من چنین خروجی را به همراه دارد:
E:\>coreinfo -v

AuthenticAMD
Microcode signature: 00000000
HYPERVISOR      -       Hypervisor is present
SVM             *       Supports AMD hardware-assisted virtualization
NP              -       Supports AMD nested page tables (SLAT)
روبروی HYPERVISOR و همچنین SLAT یک - را قرار داده‌است. یعنی این موارد یا پشتیبانی نمی‌شوند و یا در BIOS فعال نشده‌اند.
همانطور که مشاهده می‌کنید، قابلیت SLAT در CPU این سیستم وجود ندارد. به همین جهت نمی‌توان به Linux containers سوئیچ کرد. هرچند windows containers آن کار می‌کند.

روش دیگر مشاهده‌ی این خطا، مراجعه‌ی به event viewer ویندوز است. در قسمت خطاهای سیستم، ممکن است خطای زیر را مشاهده کنید:
Hypervisor launch failed; Second Level Address Translation is required to launch the hypervisor.


آزمایش Docker نصب شده

پس از نصب docker، خط فرمان ویندوز را گشوده و دستور زیر را صادر کنید:
docker run hello-world
اگر از قسمت قبل به‌خاطر داشته باشید، hello-world در اینجا نام یک image است. البته چون این image بر روی سیستم ما موجود نیست، این دستور، ابتدا آن‌را از docker hub دریافت کرده و سپس اجرا می‌کند. بنابراین می‌شد ابتدا دستور pull را صادر کرد و سپس run. اما دستور run قادر است هر دو عمل را با هم انجام دهد.

یک نکته: این image، یک image لینوکسی است. به همین جهت پیش از اجرای این دستور، همانطور که پیشتر نیز عنوان شد، یکبار بر روی آیکن Docker در قسمت Tray Icons ویندوز کلیک راست کرده و گزینه‌ی switch to Linux containers را انتخاب کنید. سپس دستور docker run hello-world را اجرا نمائید.

و یا در همین حال دستور docker run -p 80:80 nginx را صادر کنید تا وب سرور لینوکسی nginx را بتوانید تحت ویندوز اجرا کنید. پس از خاتمه‌ی عملیات دریافت و اجرای وب سرور، با توجه به تنظیم  p 80:80-، پورت 80 میزبان (اولین عدد)، به پورت 80 کانتینر نگاشت شده‌است. به همین جهت تنها با اجرای دستور http://localhost، خروجی این وب سرور را می‌توانید در مرورگر سیستم خود مشاهده کنید.
همانطور که مشاهده می‌کنید، با استفاده از داکر، پیش از آنکه بدانیم چگونه باید یک نرم افزار را نصب کرد، می‌توان از آن استفاده کرد!


روش متوقف کردن Containers در حال اجرا

اگر دستور docker ps را در خط فرمان ویندوز اجرا کنید، لیست پروسه‌های اجرا شده‌ی توسط آن قابل مشاهده هستند. در این لیست container id در حال اجرا نیز مشخص است. برای خاتمه‌ی کار آن، تنها کافی است دستور docker stop id را اجرا کنید.
یک نکته: ضرورتی به ذکر کامل id نیست. برای مثال ذکر سه حرف اول آن نیز کفایت می‌کند.


روش اجرای مجدد یک Container

فرض کنید می‌خواهیم سرور nginx را مجددا اجرا کنیم. یک روش آن، اجرای مجدد دستور docker run -p 80:80 nginx است که پیشتر آن‌را انجام دادیم. در این حالت این image تبدیل به container شده و همانند روش‌های متداول نصب نرم افزار، اکنون به عنوان یک نرم افزار نصب شده در دسترس است. برای مشاهده‌ی لیست آن‌ها، دستور docker ps -a را اجرا کنید. این لیست تا این لحظه باید شامل containerهای nginx و hello-world باشد. متوقف کردن یک container، سبب تخریب یا حذف آن نمی‌شود. در این حالت در لیستی که توسط دستور docker ps -a نمایش داده شده‌است، باز هم container idها قابل مشاهده هستند. فقط کافی است برای اجرای یکی از آن‌ها، دستور docker start id را اجرا کرد. به این صورت دیگر نیازی به ذکر دستور کامل docker run با تمام پارامترهای آن نیست. این id نیز همانطور که ذکر شد، می‌تواند سه حرف ابتدایی این id باشد تا حدی که نسبت به سایر idهای موجود، منحصربفرد شناخته شود. یا بجای container id می‌توان container name نمایش داده شده‌ی در این لیست را استفاده کرد.
پس از اجرای nginx توسط دستور docker start id، دو روش برای بررسی در حال اجرا بودن آن وجود دارد:
الف) مرورگر را باز کنیم و آدرس http://localhost را بررسی کنیم.
ب) دستور docker ps را در خط فرمان اجرا کنیم، تا مشخص شود که آیا پروسه‌ی nginx در حال اجرا است یا خیر؟

بنابراین دستور docker ps -a لیست تمام containers در حال اجرا و همچنین متوقف شده را نمایش می‌دهد. اما دستور docker ps تنها لیست containers در حال اجرا را نمایش خواهد داد.


روش حذف Containers از Docker

همانطور که در قسمت قبل نیز بحث شد، معادل نصب نرم افزار در اینجا، ایجاد یک container از یک image دریافتی از docker hub است. روش عکس آن، یعنی تخریب یک container، دقیقا معادل عزل نرم افزار از سیستم، در حالت‌های متداول است. برای اینکار مجددا دستور docker ps -a را اجرا می‌کنیم تا لیست تمام containerهای در حال اجرا و همچنین متوقف شده نمایش داده شوند. لیستی که در اینجا نمایش داده می‌شود، شبیه به لیستی است که در قسمت add/remove programs ویندوز مشاهده می‌کنید. این لیست معادل لیست نرم افزارهای نصب شده‌ی بر روی سیستم است و یا برای مشاهده‌ی لیست imageهای دریافتی از docker hub می‌توان دستور docker images را صادر کرد.
قبل از حذف یک container نیاز است آن‌را متوقف کنیم. برای این منظور از دستور docker stop id استفاده می‌شود. سپس اجرای دستور docker rm id، سبب حذف کامل این container خواهد شد. برای آزمایش آن، مجددا دستور docker ps -a را اجرا کنید.
دستور docker rm چندین id را نیز می‌پذیرد. می‌توان این idها و یا حتی سه حرف ابتدایی آن‌ها را با فاصله در اینجا ذکر کرد. علاوه بر id، ذکر نام containers نیز مجاز است.


روش حذف Imageهای دریافتی از Docker Hub

دستور docker rm، فقط containers را از سیستم حذف می‌کند (نرم افزارهای نصب شده). اما خود imageهای اصلی دریافت شده‌ی از docker hub را حذف نمی‌کند (معادل همان فایل‌های zip دریافت نرم افزار یا برنامه‌های نصاب، در حالت متداول و سنتی نصب نرم افزار). برای آزمایش آن دستور docker images را اجرا کنید. هنوز هم در لیست آن، تمام موارد دریافتی موجود هستند.
برای حذف یک image می‌توان از دستور docker rmi id استفاده کرد (rmi بجای rm). این id نیز در لیست docker images ظاهر می‌شود و ذکر قسمتی از آن، تا حدی که نسبت به سایر idهای لیست شده منحصربفرد باشد، کافی است. در اینجا بجای id، از نام image نیز می‌توان استفاده کرد. همچنین ذکر چندین id و یا نام نیز پس از دستور docker rmi، میسر است.


روش جستجوی imageها در Docker Hub توسط Docker CLI

فرض کنید می‌خواهیم image مربوط به راهنمای Docker را از Docker Hub دریافت کنیم. یک روش آن مراجعه‌ی مستقیم به سایت آن است و استفاده از امکانات جستجوی فراهم شده‌ی در آن سایت. روش دیگر، استفاده از Docker CLI است. اگر دستور docker search docs را در خط فرمان اجرا کنیم، لیست تمام مخازن کدی که در آن‌ها واژه‌ی docs قرار دارد، نمایش داده می‌شود. البته پیش از نصب image آن بهتر است به برگه‌ی tags مخزن کد آن نیز مراجعه کنید تا بتوانید حجم آن‌را نیز مشاهده نمائید که حدود یک گیگابایت است. مخازن docker hub، حاوی imageهای نصاب containerهای متناظر هستند. برای دریافت و اجرای آن می‌توان دستور docker run -p 4000:4000 docs/docker.github.io را اجرا کرد.
پس از دریافت یک گیگابایت مستندات، container آن بر روی پورت 4000 در سیستم ما (http://localhost:4000)، به صورت یک وب سایت استاتیک، قابل دسترسی خواهد بود. به این صورت می‌توان به مستندات کامل داکر به صورت آفلاین دسترسی داشت.


مفهوم Interactive Terminal در Docker

زمانیکه دستور اجرای مستندات آفلاین را صادر می‌کنید، در انتهای آن عنوان می‌کند که وب سایت محلی آن بر روی پورت 4000 قابل دسترسی است. سپس در ذیل آن ذکر شده‌است که اگر ctrl+c را فشار دهید، اجرای آن به پایان می‌رسد. اما عملا اینطور نیست و اگر دستور docker ps را صادر کنید، هنوز container در حال اجرای آن را می‌توان مشاهده کرد.
اما اگر اینبار دستور اجرای docker run را به همراه یک interactive terminal با سوئیچ it و نام docs صادر کنیم:
 docker run -p 4000:4000 -it --name docs docs/docker.github.io
اکنون اگر ctrl+c را فشار دهیم و پس از آن دستور docker ps را صادر کنیم، دیگر در لیست آن، container در حال اجرای docs مشاهده نمی‌شود.
سوئیچ it یا interactive terminal سبب می‌شود تا یک container در foreground، بجای background اجرا شود. به این ترتیب دستور ctrl+c، سبب خاتمه‌ی واقعی پروسه‌ی درحال اجرای در container می‌شود.
روش دیگر خاتمه‌ی این container، استفاده از نام ذکر شده‌است؛ یعنی اجرای دستور docker stop docs.

یک نکته: اگر می‌خواهید از terminal باز شده قطع شوید (مجددا به command prompt باز گردید) اما سبب خاتمه‌ی container آن نشوید، از ترکیب ctrl+p+q استفاده کنید.


اجرای containerهای ویندوزی

در مورد نحوه‌ی سوئیچ بین نوع‌های مختلف containerهای ویندوزی و لینوکسی پیشتر توضیح دادیم. برای این منظور می‌توان بر روی آیکن Docker در قسمت Tray Icons ویندوز کلیک راست کرده و گزینه‌ی switch to Windows/Linux containers را انتخاب کرد. باید دقت داشت که پشتیبانی از containerهای ویندوزی، از ویندوز 10، نگارش  1607، یا همان Anniversary Update آن به بعد، به ویژگی‌های ویندوز اضافه شده‌اند که به صورت خودکار توسط docker فعالسازی می‌شوند:



اجرای IIS به عنوان یک Windows Container

تا اینجا imageهای دریافتی، لینوکسی بودند. اگر گزینه‌ی Windows Containers را به روشی که گفته شد، فعال کنید، اینبار با اجرای دستورات docker ps و یا docker images، هیچ خروجی را دریافت نخواهید کرد. از این جهت که کانتینرهای ویندوزی و لینوکسی، به صورت کاملا ایزوله‌ای از هم اجرا و مدیریت می‌شوند. علت آن‌را هم در MobyLinuxVM که پیشتر با اجرای دستور Virtmgmt.msc بررسی کردیم، می‌توان یافت. Containerهای لینوکسی، در داخل MobyLinuxVM اجرا می‌شوند.
در اینجا به عنوان مثال می‌توان image رسمی مربوط به IIS را از docker hub دریافت و به صورت یک کانتینر ویندوزی اجرا کرد. البته پیش از اجرای دستورات آن بهتر است به برگه‌ی tags آن مراجعه کرده و حجم‌های نگارش‌های مختلف آن‌را بررسی کرد. اجرای دستور docker pull microsoft/iis به معنای دریافت tag ای به نام latest است (به حجم 6 گیگابایت!)؛ یعنی با دستور docker pull microsoft/iis:latest یکی است. بنابراین در اینجا بر اساس tagهای مختلف، می‌توان دستور pull متفاوتی را صادر کرد. برای مثال اگر دستور docker pull microsoft/iis:nanoserver را صادر کردید، نگارش مخصوص nano server آن‌را که فقط 449 مگابایت است، دریافت می‌کند. بنابراین از این پس به tagهای هر مخزن docker hub خوب دقت کنید و نگارش مختص به سیستم عامل خود را دریافت نمائید. عدم ذکر tag ای، همواره tag ویژه‌ای را به نام latest، دریافت می‌کند.
با اجرای دستور زیر
 docker run -p 81:80 -d --name iis microsoft/iis:nanoserver
داکر، ابتدا image مخصوص nanoserver آن‌را دریافت و سپس اجرا می‌کند. چون وب سرور است، نیاز به تنظیمات پورت آن‌را داریم. p 81:80- به این معنا است که پورت 80 کانتینر را (پورتی که IIS درون آن بر روی آن اجرا می‌شود)، به پورت 81 بر روی سیستم میزبان (یا همین ویندوز فعلی که داکر را اجرا می‌کند)، نگاشت کن. پارامتر d در اینجا به معنای detach است. یعنی به محض اجرای این دستور، کار اجرای این کانتینر در background انجام شده و سپس به خط فرمان، بازگشت داده می‌شویم. همچنین نامی نیز به این container انتساب داده شده‌است تا ساده‌تر بتوان با آن کار کرد.

یک نکته: مشکلی با اجرای IIS مخصوص نانوسرور بر روی ویندوز 10 به این صورت و توسط داکر نیست. بنابراین پس از اجرای دستور فوق، کار دریافت image و ساخت container و سپس اجرای آن به صورت خودکار انجام شده و بلافاصله به command prompt بازگشت داده می‌شویم (به علت استفاده‌ی از پارامتر d). اکنون اگر دستور docker ps را صادر کنیم، مشاهده می‌کنیم که کانتینر IIS مخصوص نانوسرور، هم اکنون بر روی ویندوز 10 در حال اجرا است و در آدرس http://localhost:81 قابل دسترسی است.

جهت تکمیل این بحث، بهتر است image مخصوص nanoserver را نیز از docker hub دریافت و اجرا کنیم:
 docker run microsoft/windowsservercore
حجم image آن 6GB است.


تنظیمات کارت شبکه‌ی Containers

هنگامیکه پروسه‌ای درون یک container اجرا می‌شود، ایزوله سازی‌های بسیاری نیز در مورد آن اعمال خواهد شد؛ به همین جهت گاهی از اوقات عده‌ای containerها را با ماشین‌های مجازی نیز مقایسه می‌کنند. برای مثال کانتینرها به همراه network adapter خاص خود نیز هستند؛ درست مانند اینکه یک کامپیوتر مجزای از سیستم جاری می‌باشند و اگر این network adapter را ping کنیم، می‌توان به این صورت نیز به آن کانتینر، دسترسی داشته باشیم.
برای یافتن آن، دستور docker inspect iis را صادر می‌کنیم. خروجی آن به همراه یک قسمت network نیز هست که داخل آن یک IP Address قابل مشاهده است. این IP است که مختص و منحصربفرد این container است. در ابتدا برای آزمایش آن، می‌توان آن‌را ping کرد؛ مانند ping 172.27.49.47. همچنین به تمام برنامه‌های داخل این container توسط این IP نیز می‌توان دسترسی یافت. برای مثال فراخوانی http://172.27.49.47:81 در مرورگر، سبب نمایش صفحه‌ی اول IIS می‌شود. البته اگر اینکار را انجام دهیم، کار نمی‌کند. علت اینجا است، نگاشت پورتی را که تعریف کرده‌ایم (پورت 81)، به پورتی در کامپیوتر میزبان است و نه این IP ویژه. برنامه‌ی اصلی IIS در داخل container، به پورت 80 بر روی این آدرس IP گوش فرا می‌دهد. اکنون اگر آدرس http://172.27.49.47:80 را در کامپیوتر میزبان فراخوانی کنیم، کار می‌کند.
بنابراین هرچند containerها به معنای نرم افزارهای از پیش نصب شده‌ی در حال اجرا هستند، اما ... به همراه ایزوله سازی‌های قابل توجهی بر روی کامپیوتر میزبان اجرا می‌شوند؛ درست مانند یک کامپیوتر مجزای از آن.
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت اول - نصب پیشنیازها
یک نکته‌ی تکمیلی

اگر می‌خواهید تمام مراحل ذکر شده را فقط با دو دستور ساده به پایان برسانید:
الف) ابتدا وابستگی‌های nodejs را نصب کنید.
ب) سپس angular-cli را نصب کنید (اجرای دستور عمومی ذیل در خط فرمان):
npm install -g angular-cli
ج) در آخر یک برنامه‌ی جدید را شروع کنید (ابتدا از طریق خط فرمان به پوشه‌ی مدنظر وارد شده و سپس دستور ذیل را صادر کنید):
ng new AngularCLIDemoApp
اجرای همین چند مرحله، برای تشکیل قالب استاندارد شروع به کار با AngularJS 2.0 کفایت می‌کنند.
نظرات مطالب
نحوه‌ی نگاشت فیلدهای فرمول در Fluent NHibernate
ببینید، توابع ویژه نمایشی سی شارپ شما، یعنی سمت کلاینت. موضوع بحث فوق سمت سرور بانک اطلاعاتی است. مقادیر در سمت سرور مطابق فرمول شما تشکیل می‌شوند. به آخرین کوئری ذکر شده در مطلب فوق دقت کنید. در حال حاضر فقط SQL Server است که امکان استفاده از توابع دات نتی را هم سمت سرور میسر کرده (از نگارش 2005 به بعد). بنابراین اگر می‌خواهید توابع ویژه‌ای را در همان سمت سرور اعمال کنید که منطق آن مثلا با سی شارپ پیاده سازی شده، باید یک CLR function مخصوص اس کیوال سرور درست کنید. بعد فرمول نگاشت فوق را بر اساس این CLR function تعیین کنید و کار می‌کند. چیزی شبیه به همان آخرین کوئری تشکیل شده را خواهید داشت. خلاصه اینکه به نحوی باید این پیاده سازی دات نتی خودتون رو به سمت سرور ببرید.
اما سمت کلاینت شما هر کاری را می‌‌توانید انجام دهید. برای مثال زمان نمایش اطلاعات در WPF یا سیلورلایت از یک Converter استاندارد آن (با پیاده سازی اینترفیس IValueConverter) در حین Binding استفاده کنید. اگر با ASP.NET Webforms کار می‌کنید حین نمایش اطلاعاتی که هم اکنون در سمت کلاینت مهیا است ، مثلا جهت نمایش در یک GridView یا موارد مشابه شما خواهید داشت myFunc(Eval("field")) و شبیه به این که myFunc باید در کدبیهایند شما پیاده سازی شود. در سایر فناوری‌ها که می‌تواند شامل موارد قبل هم باشند، نهایتا شما یک لیست دریافتی از سرور را دارید، یک حلقه با LINQ یا حالت معمولی تشکیل شده و مقادیر مدل مورد نظر ویرایش می‌شوند تا جهت نمایش مناسب شوند.
تمام این‌ها در حالتی است که قصد شما فقط و فقط تغییر نحوه‌ی نمایش است. به عبارتی الان کل دیتای فیلتر شده سمت کاربر مهیا است. شما می‌خواهید به آن شکل دهید.

حالت دیگر (حالت غیر نمایشی و استفاده در کوئری‌ها):
اگر با LINQ کمی بیشتر از اطلاعات موجود در وب کار کرده باشید احتمالا به این سوال رسیده‌اید که آیا می‌شود متد سفارشی خودمان را هم حین تهیه کوئری‌هایی از این دست استفاده کنیم؟ چون فقط یک سری extension method مشخص بیشتر وجود ندارند. اگر من extension method سفارشی خودم را تهیه کردم چطور؟
این سوال دو پاسخ دارد:
- متدهای سفارشی شما حتما روی کل اطلاعات دریافتی از سرور کار می‌کنند؛ اما بهینه نیستند. چون برای مثال myFunc سی شارپ من معادل SQL ایی ندارد که بتوانم مستقیما آن‌را سمت سرور اجرا کنم. چون نهایتا LINQ to NHibernate باید به SQL یا T-SQL ترجمه شود. به همین جهت مجبورم کل اطلاعات را دریافت کنم، مثلا 100 هزار رکورد، حالا که اشیاء دات نتی من تشکیل و کامل شده، متد سفارشی LINQ خودم را بر روی این‌ها اجرا می‌کنم. این روش کار می‌کنه ولی از لحاظ کارآیی فاجعه است.
- روش دیگر: در NH 3.0 این امکان وجود دارد ... بسط پروایدر LINQ آن با صور مختلف. که اگر وقت شد یک مطلب کامل در مورد آن خواهم نوشت.