مطالب
نادیده گرفته شدن فایل gitignore توسط Visual Studio
در مورد کاربرد فایل  gitignore . می‌توانید این پست را مطالعه فرمایید.

در هنگام اولین بارگزاری پروژه در مخزن Git ، گاها دیده می‌شود که Visual Studio  فایل gitignore . ایی را که شما آماده کرده‌اید، نادیده گرفته و فایل gitignore . پیش فرض خود را در مخزن Push می‌کند. در این پست یک راه حل ممکن برای حل این مشکل ارائه می‌دهیم.

1- در Visual Studio  از مسیر File->Add to Source Control و با انتخاب Git پروژه را آماده کنید:



2- بدون هیچ تغییری، پروژه را ببندید و در مسیر ذخیره سازی پروژه، به پوشه‌ی git . (مخفی است ( وارد شوید.
3- فایل
ms-persist.xml را حذف کنید.
4- پروژه را باز کنید. در قسمت
Team Explorer وارد Setting شوید. در قسمت Ignore File، فایل gitignore . را مطابق نیاز خود ویرایش و ذخیره کنید.
5- در قسمت
Team Explorer وارد قسمت Changes شوید. یک نام مناسب را وارد و سپس Commit  کنید.



6 - در بالا با کلیک روی sync به قسمت commits unsync وارد خواهید شد. در این جا آدرس مخزن پروژه را وارد کنید و گزینه Publish را وارد کنید.

7- تا این مرحله فایل  gitignore مورد نظر شما وارد مخزن شده است. برای بارگزاری مابقی پروژه به ترتیب مراحل 1 ، 5 و سپس 6 را تکرار کنید با این تفاوت که در مرحله 6 نیازی به Publish نبوده و صرفا انتخاب گزینه  sync کافی است.

مطالب
ساخت کلیدهای امنیتی GunPG
یکی از روش‌های ارسال و رمزگذاری اطلاعات، استفاده از کلیدهای امنیتی مورد استفاده‌ی در سیستم یونیکس یا GnuPG است. استفاده از نرم افزار Gnu Privacy Guard یا گارد حفاظتی گنو، به ما این اجازه را می‌دهد که بتوانیم اطلاعاتمان را در بسترهای ارتباطی، با خیالی راحت‌تر ارسال کنیم و تا حد زیادی مطمئن باشیم که تنها فرد هدف توانایی دسترسی به اطلاعات را خواهد داشت. گارد امنیتی گنو زیر مجموعه‌ای از پروژه‌ی گنو است که دولت آلمان پایه ریز اصلی آن بوده است. این نرم افزار از یک روش رمزگذاری ترکیبی استفاده می‌کند که الگوریتم‌های کلیدهای برابر(متقارن) و کلید‌های عمومی (نامتقارن) جهت تبادل آسان کلید را شامل می‌شود. در حال حاضر که نسخه‌ی دو این برنامه ارائه شده است، برای رمزگذاری‌ها از کتابخانه‌ای به اسم libgcrypt استفاده می‌کند. یکی از مشکلات فعلی این پروژه، عدم وجود api‌های مناسبی جهت دسترسی راحت‌تر است و برای حل این مشکل، GPGME که مخفف GnuPG Made Easy ایجاد شد. بسیاری از برنامه‌ها و پلاگین‌های ارسال اطلاعات، امروزه همچون ارسال ایمیل، از این کلیدها بهره می‌برند.

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

  • OpenPGP: یک برنامه نیست و یک قانون و استانداری برای تهیه‌ی آن است؛ که رعایت اصول آن الزامی است و برنامه‌ی بالا، یک پیاده سازی از این استاندارد است.
  • PGP: یک برنامه، برای رمزگذاری اطلاعات است که مخفف Pretty Good Privacy است.
  • و GnuPG یا GPG که در بالا به آن اشاره شد.
برای ساخت کلید، ما از دستور یا برنامه‌ی GPG که که عمدتا در همه‌ی لینوکس‌ها مثل دبیان و مشتقات آن نصب است، استفاده می‌کنیم و اگر نصب نیست از طریق توزیع آن اقدام نمایید.
در صورتیکه از ویندوز استفاده می‌کنید، نیاز است ابتدا خط فرمان یونیکس را روی آن نصب کنید. برنامه‌ی Cygwin این امکان را به شما می‌دهد تا خط فرمان یونیکس و دستورات پیش فرض آن را داشته باشید. این برنامه در دو حالت ۳۲ بیتی و ۶۴ بیتی ایجاد شده است. از آنجا که گفتیم این برنامه شامل دستورات پیش فرض آن است، برای همین GPG باید به صورت یک بسته‌ی جداگانه نصب شود که در سایت آن می‌توانید بسته‌های مختلف آن‌را برای پلتفرم‌های مختلف را مشاهده کنید.

ساخت کلید


برای ساخت کلید دستور زیر را صادر کنید:
gpg --gen-key
اگر از نسخه‌های جدیدتر GPG استفاده می‌کنید، گزینه‌هایی به شکل زیر ایجاد می‌شوند؛ ولی اگر خیر، ممکن است تعداد و شماره‌ی گزینه‌ها متفاوت باشند که در این مورد دقت کنید. من در اینجا همان حالت پیش فرض، یعنی ۱ را انتخاب می‌کنم. این گزینه نحوه‌ی امضاء و یا رمزگذاری شما با استفاده از الگوریتم‌های RSA و DSA را مشخص می‌کند.
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
در کل در هر حالتی، استفاده‌ی از RSA پیشنهاد می‌شود. بعد از آن، از شما اندازه‌ی کلید را می‌پرسد که همان مقدار پیش فرض خودش را وارد می‌کنیم:
What keysize do you want? (2048)
البته بسیاری ۱۰۲۴ بایت را نیز کافی می‌دانند.
بعد از آن مدت زمان اعتبار این کلید را از شما جویا می‌شود:
Key is valid for? (0)
هنگام این پرسش نحوه‌ی ورود زمان را به شما خواهد گفت که می‌تواند به شکل‌های زیر باشد:
دو هفته
2w
دو سال
2y
پس از آن هم یک تاییدیه از شما می‌گیرد و تاریخ انقضاء را به طور کامل برای شما می‌نویسد و سپس نیاز است که اطلاعاتی از قبیل نام و ایمیل و توضیح را وارد کنید:
You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: ali yeganeh.m
Email address: yeganehaym@gmail.com
Comment: androidbreadcrumb
You selected this USER-ID:
    "ali yeganeh.m (androidbreadcrumb) <yeganehaym@gmail.com>"
بعد از آن از شما می‌خواهد که کل عملیات را تایید و یا کنسل کنید؛ یا اگر اطلاعات بالا را اشتباه وارد کرده‌اید، اصلاح کنید. با زدن کلید O عملیات را تایید کنید. در این حین از شما یک کلید برای رمزگذاری می‌پرسد که باید آن را دو بار بدهید و کارتان در اینجا به پایان می‌رسد و کلید ایجاد می‌شود.
اگر مشکلی در ساخت کلید نباشد با ارسال دستور زیر باید آن را در لیست کلیدها ببینید:
ali@alipc:~$ gpg --list-keys
/home/ali/.gnupg/pubring.gpg
----------------------------
pub   2048R/8708016A 2015-10-23 [expires: 2065-10-10]
uid                  ali yeganeh.m (androidbreadcrumb) <yeganehaym@gmail.com>
sub   2048R/533B7E96 2015-10-23 [expires: 2065-10-10]
در اینجا کلید عمومی در خط pub بعد از  / قرار دارد؛ یعنی عبارت ۸۷۰۸۰۱۶A کلید عمومی ماست که بر روی هر سیستم و هر کلیدی متفاوت است.

تبدیل کد متنی به کد دودویی
یکی از روش‌های ارسال کدهای دودویی تبدیل آنان به یک قالب متنی ASCII است که به آن قالب ASCII Armor هم می‌گویند. سایت‌های زیادی وجود دارند که این عبارت متنی را از شما می‌خواهند. چرا که مثلا این امکان وجود دارد که کلیدی که کاربر به سمت آنان می‌فرستد، آسیب دیده باشد یا اینکه KeyServer‌ها در دسترس نباشند. در مورد این سرورها در ادامه صحبت خواهیم کرد. مثلا یکی از سایت‌هایی که به این عبارت‌ها نیاز دارد ‌‌‌‌Bintray است.

برای دریافت این کلید متنی باید دستور زیر را صادر کنید:
gpg --output mykey.asc --export -a $GPGKEY
که برای مثال ما می‌شود:
gpg --output mykey.asc --export -a 8708016A
و اگر کلید را با یک ویرایشگر متنی باز کنید، محتوایی شبیه محتوای زیر را خواهید دید:
ali@alipc:~$ cat mykey.asc
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1

mQENBFYqAJABCADcw5xPonh5Vj7nDk1CxDskq/VsO08XOa/i2OLOzatB4oK5x+0x
jxORxXMnIAR83PCK5/WkOBa64jnu3eiP3jKEwAykGGz/Z1bezC9TIP8y+PnsiDhT
aFArluUJx+RT5q7s27aKjqoc3fR/xuwLWopZt9uYzE/DQAPDsHdUoUg+fh4Hevm+
a8/3ncR7q6nM8gc9wk621Urb1HaRrILdmeh7ZpJcl8ZUbc+NObw357fGsjnpfHXO
rdCr7ClvNUq6I+IeGMQG/6040LeeaqhaRxPrUhbFjLA155gkSqzecxl7wQaYc71M
Zdlv+6Pt1B8nPAA3WXq0ypjU8A5bvmAQRD5LABEBAAG0OGFsaSB5ZWdhbmVoLm0g
KGFuZHJvaWRicmVhZGNydW1iKSA8eWVnYW5laGF5bUBnbWFpbC5jb20+iQE+BBMB
AgAoBQJWKgCQAhsDBQld/A8ABgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDS
Lhq8hwgBanaHB/4reGxUjR6dB08ykfwQOx+raYHGqJlgawisE4qUHTkGaspyQaNy
yxh0vwKkGvg6nNy2VN1XFBc7jlHlrYqPPuPdg2B+1LvEghb30ESDbHUvk8NrJgDJ
C0257gxqWvUQTWvMC3FkSLdw3tyQ8dF7FxmSU79XcxVqGeseaDzMQrEasP0yJHsm
NJf8pvuD6qiWu3KSSoQmI/17Sj8s7eGJMh6o5YRFGHc1Bt9tCD+52bvt579Ju4vZ
tmQvxR4fNQo9sAeMqAJhIpF7IYcuyCEy+CQ847UkzE4f/OCCPxfV3samV/nnBJJ9
Ouu+68lk6Fpx4A0a3nEwqoAmMWxrbSSUFW97uQENBFYqAJABCAC4CzrUOKskE4hK
GVCjaOJKxhbuUdOrep6n3vof0fscs5Dy7h2oVh2vb12WH9X6pijJVPiUpGR4Mpu0
lO2Bu9Rwt38AQ6mRmL/hfzjEXSvKkdX7osk+1CVnnUaSdM9Ek2hWUH8JcN28z/WT
X9Bw8MCdZF7j1HvX/5ojghzMZyYM4elWJLBr1gON6xXAI6HR7DlnRkaVr8L9SYGm
FyAXZ0LzWYwG1Z1AnTyxff6v/Mn3p1/1E3aBA+LkQqBzHg2nBm4jCaFWfeCdiNBf
CHkY9r/Evo9hUPD+CtBNFwsUm1D4maZ0FFtIQ701QhVmupnub+rKoObC0AFj3abK
MCw9uo8TABEBAAGJASUEGAECAA8FAlYqAJACGwwFCV38DwAACgkQ0i4avIcIAWrz
rAf+K1IIMtBq3WlabfZQrgzFHQ62ugVJO/yI1ITkm4l08XHDf+ShqDg4urNuMDEe
oQD35MvB2BhER1jL6VR3qjLkZyZYJ+EQiSxEDWXooav3KvpWjhcqjQy79GFs8waH
E7ssGmWwaugVS/PJAmGQ+s8YWDNa6aCClmp2dJRiwBTyFdewNBLA2V32xzWCYxhI
YtEp+Kg15XuCDTRatOPWSFGSPe/paytmpGZc0XzU/W9sBpabhxVmcL4H6L07uCef
IOn/S5QXo3P9X/3ckmJ9GUb7rjdq1ivYgX53xI75jlePsmN/2f+3fNffUaZgFTTd
Uls+XCun7OVYSBBfjgRfQbTvoA==
=6j7i
-----END PGP PUBLIC KEY BLOCK-----
در صورتی که قصد دارید متن کلید خصوصی را به دست بیاورید، لازم است بعد از export- عبارت secret-key- را نیز اضافه کنی د؛ یعنی:
gpg --output mykey.asc --export-secret-key -a 8708016A


آپلود کلید به سرورهای کلید (Key Servers
)

 یکی از روش‌های به اشتراک گذاری کلید برای کاربران این است که از سرورهای کلید استفاده کنیم. یکبار آپلود روی یکی از این سرورها باعث می‌شود که به بقیه‌ی سرورها هم اضافه شود. یکی از این سرورهای کلید که خودم از آن استفاده می‌کنم، سرور ابونتو است و با استفاده از دستور زیر، همان کلید بالا را برای آن سرور ارسال می‌کنم:
gpg --send-keys --keyserver keyserver.ubuntu.com $GPGKEY

==>
gpg --send-keys --keyserver keyserver.ubuntu.com 8708016A
سپس از طریق کلید متنی، کلید آپلود شده را تایید می‌کنیم. به این آدرس رفته و محتوای کلید متنی خود را به طور کامل به همراه تگ‌های شروع و پایان کپی کنید و حتی می‌توانید کلید خود را از طریق کادر جست و جو پیدا کنید.

رمزگذاری
ابتدا در محیط یونیکس، یک فایل متنی ساده با متن hello ubuntu را ایجاد میکنم. در ادامه قصد دارم این فایل را رمزنگاری کنم:
ali@alipc:~$ cat >ali.txt
hello ubuntu
سپس همین فایل را رمزنگاری می‌کنم:
ali@alipc:~$ gpg --output myali.gpg --encrypt --recipient yeganehaym@gmail.com ali.txt
در این دستور ابتدا گفتیم که نام فایل خروجی ما myali.gpg است و می‌خواهیم آن را رمزگذاری کنیم که توسط کلیدی با ایمیل yeganehaym@gmail.com می‌باشد فایل ali.txt را رمزگذاری می‌کنیم.

رمزگشایی
برای رمزگشایی می‌توانید از طریق دستور زیر اقدام کنید:
gpg --output output.txt --decrypt myali.gpg

You need a passphrase to unlock the secret key for
user: "ali yeganeh.m (androidbreadcrumb) <yeganehaym@gmail.com>"
2048-bit RSA key, ID 533B7E96, created 2015-10-23 (main key ID 8708016A)
در اینجا دستور دادیم محتوای فایل رمزشده‌ی myali.gpg را رمزگشایی کن و محتوای آن را داخل فایلی با نام output.txt قرار بده. بعد از اجرای این دستور از شما عبارت رمزی را که در مرحله‌ی ساخت کلید دوبار از شما پرسید، درخواست می‌کند. در بعضی سیستم‌ها در همان ترمینال می‌پرسد، ولی بعضی سیستم‌ها مثل ابونتو که من از آن استفاده می‌کنم، به صورت گرافیکی یک کادر باز کرده و از شما خواهش می‌کند عبارت رمز را وارد کنید.
عبارت رمز را وارد کنید و حالا فایل output.txt را باز کنید:
ali@alipc:~$ cat output.txt 
hello ubuntu
مطالب
مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت سوم - روش اتصال Redux به برنامه‌های React
پس از بررسی ساختار کتابخانه‌ی Redux به صورت مستقل و متکی به خود، اکنون در این قسمت، نحوه‌ی اتصال آن‌را به برنامه‌های React بررسی می‌کنیم.


نصب پیشنیازها

می‌توان همانند قسمت قبل، تمام کارها را با کتابخانه‌ی redux انجام داد و یا می‌توان قسمت به روز رسانی UI آن‌را و همچنین مدیریت state را به کتابخانه‌ی ساده کننده‌ی دیگری به نام react-redux واگذار کرد. به همین جهت در ادامه‌ی همان برنامه‌ی قسمت قبل، دو کتابخانه‌ی redux و همچنین react-redux را به همراه types آن نصب می‌کنیم (نصب types، سبب ارائه‌ی intellisense بهتری در VSCode می‌شود؛ حتی اگر نخواهیم با TypeScript کار کنیم).
برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های ctrl+` را فشرده (ctrl+back-tick) و دستورات زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save redux react-redux
> npm install --save-dev @types/react-redux
به علاوه در ادامه توئیتر بوت استرپ 4 را نیز نصب می‌کنیم:
> npm install --save bootstrap
سپس برای افزودن فایل bootstrap.css به پروژه‌ی React خود، ابتدای فایل index.js را به نحو زیر ویرایش خواهیم کرد:
import "bootstrap/dist/css/bootstrap.css";
این import به صورت خودکار توسط webpack ای که در پشت صحنه کار bundling & minification برنامه را انجام می‌دهد، مورد استفاده قرار می‌گیرد.


معرفی ساختار ابتدایی برنامه

برنامه‌ای را که در این قسمت بررسی می‌کنیم، ساختار بسیار ساده‌ای را داشته و به همراه دو دکمه‌ی افزایش و کاهش مقدار یک شمارشگر است؛ به همراه دکمه‌ی برای به حالت اول در آوردن آن. هدف اصلی دنبال شده‌ی در اینجا نیز نحوه‌ی برپایی redux و همچنین react-redux و اتصال آن‌ها به برنامه‌ی React جاری است:


به همین جهت ابتدا کامپوننت جدید src\components\counter.jsx را به نحو زیر تشکیل می‌دهیم تا markup ابتدایی فوق را به همراه سه دکمه و یک span، برای نمایش مقدار شمارشگر، رندر کند:
import React, { Component } from "react";

class Counter extends Component {
  render() {
    return (
      <section className="card mt-5">
        <div className="card-body text-center">
          <span className="badge m-2 badge-primary">0</span>
        </div>
        <div className="card-footer">
          <div className="d-flex justify-content-center align-items-center">
            <button className="btn btn-secondary btn-sm">+</button>
            <button className="btn btn-secondary btn-sm m-2">-</button>
            <button className="btn btn-danger btn-sm">Reset</button>
          </div>
        </div>
      </section>
    );
  }
}

export default Counter;
سپس المان آن‌را جهت نمایش در برنامه، به فایل src\App.js اضافه می‌کنیم:
import "./App.css";

import React from "react";

import Counter from "./components/counter";

function App() {
  return (
    <main className="container">
      <Counter />
    </main>
  );
}

export default App;


پوشه بندی مخصوص برنامه‌های مبتنی بر Redux


هدف ما در ادامه ایجاد یک store مخصوص redux است و سپس اتصال آن به کامپوننت شمارشگر برنامه. به همین جهت نیاز به 4 پوشه‌ی جدید، برای مدیریت بهتر برنامه خواهیم داشت:
- پوشه constants: برای اینکه نام رشته‌ای نوع اکشن‌های مختلف را بتوانیم در قسمت‌های مختلف برنامه استفاده کنیم، بهتر است فایل جدید src\actions\index.js را ایجاد کرده و این ثوابت را داخل آن export کنیم.
- پوشه‌ی actions: در فایل جدید src\actions\index.js، تمام متدهای ایجاد کننده‌ی شیء خاص action، که در قسمت قبل در مورد آن بحث شد، قرار می‌گیرند. نمونه‌ی آن، متد createAddAction قسمت قبل است.
- پوشه‌ی reducers: تمام توابع reducer برنامه را در فایل‌های مجزایی در پوشه‌ی reducers قرار می‌دهیم. سپس در فایل src\reducers\index.js با استفاده از متد combineReducer آن‌ها را یکی کرده و به متد createStore ارسال می‌کنیم.
- پوشه‌ی containers: این پوشه جائی است که کار فراخوانی متد connect کتابخانه‌ی react-redux به ازای هر کامپوننت استفاده کننده‌ی از redux store، صورت می‌گیرد.

این موارد را با جزئیات بیشتری در ادامه بررسی می‌کنیم.



ایجاد نام نوع اکشن متناظر با دکمه‌ی افزودن مقدار

می‌خواهیم با کلیک بر روی دکمه‌ی +، مقدار شمارشگر افزایش یابد. به همین جهت نیاز به یک نام وجود دارد تا در تابع Reducer متناظر و قسمت‌های دیگر برنامه، بتوان بر اساس آن، این اکشن خاص را شناسایی کرد و سپس عکس العمل نشان داد. به همین جهت فایل جدید src\constants\ActionTypes.js را ایجاد کرده و به صورت زیر تکمیل می‌کنیم:
export const Increment = "Increment";
البته هرچند مرسوم است نام و مقدار این نوع ثوابت را تشکیل شده‌ی از حروف بزرگ، معرفی کنند ولی این موضوع اختیاری است.


ایجاد متد Action Creator

در قسمت قبل مشاهده کردیم که شیء ارسالی به یک reducer از طریق dispatch یک action خاص، دارای فرمت ویژه‌ی زیر است:
{
    type: "ADD",
    payload: {
      amount // = amount: amount
    },
    meta: {}
}
به همین جهت برای نظم بخشیدن به تعریف این نوع اشیاء و یک‌دست سازی آن‌ها، فایل جدید src\actions\index.js را ایجاد کرده و آن‌را به صورت زیر تکمیل می‌کنیم:
import * as types from "../constants/ActionTypes";

export const incrementValue = () => ({ type: types.Increment });
همانطور که ملاحظه می‌کنید در این متد، فعلا فقط نام رشته‌ای نوع این اکشن، بیشتر مدنظر است تا بر اساس action.type رسیده در reducer متناظر با آن، عملی رخ دهد. بنابراین فقط قسمت type آن‌را مقدار دهی کرده‌ایم. مقدار ثابت رشته‌ای types.Increment نیز از فایل مجزای src\constants\ActionTypes.js که پیشتر تعریف کردیم، تامین شده‌است.


ایجاد تابع reducer مخصوص افزودن مقدار

ابتدا فایل جدید src\reducers\counter.js را با محتوای زیر ایجاد می‌کنیم:
import * as types from "../constants/ActionTypes";

const initialState = {
  count: 0
};

export default function counterReducer(state = initialState, action) {
  if (action.type === types.Increment) {
    return {
      count: state.count + 1
    };
  }
  return state;
}
- اگر دقت کرده باشید، کامپوننت شمارشگر این قسمت، دارای state نیست و همچنین نمی‌خواهیم هم که دارای state باشد؛ چون قرار است توسط redux مدیریت شود. به همین جهت state اولیه را به صورت initialState که محتوای یک شیء با خاصیت count با مقدار اولیه‌ی صفر است، خارج از کلاس کامپوننت، ایجاد کرده‌ایم.
- سپس می‌خواهیم رویداد کلیک بر روی دکمه + را مدیریت کنیم. به همین جهت نیاز به یک اکشن جدید به نام Increment داریم که توسط مقدار ثابت رشته‌ای types.Increment، از فایل مجزای src\constants\ActionTypes.js، تامین می‌شود.
- پس از مشخص کردن نوع action ای که قرار است مدیریت شود و همچنین ایجاد متدی برای تولید شیء حاوی اطلاعات آن که در فایل src\actions\index.js قرار دارد، اکنون می‌توان متد reducer را که state و action را دریافت می‌کند و سپس state جدیدی را بر اساس action.type دریافتی و در صورت نیاز بازگشت می‌دهد، ایجاد کرد. این متد بررسی می‌کند که آیا action.type رسیده همان ثابت Increment است؟ اگر بله، بجای تغییر مستقیم state.count، یک شیء جدید را بازگشت می‌دهد. البته روش صحیح‌تر اینکار را در قسمت اول این سری با معرفی روش‌هایی برای کپی اشیاء و آرایه‌ها، بررسی کردیم. در اینجا جهت سادگی بیشتر، یک شیء کاملا جدید را دستی ایجاد می‌کنیم. در آخر اگر action.type رسیده قابل پردازش نبود، همان state ابتدایی دریافتی را بازگشت می‌دهیم تا در صورت وجود چندین reducer تعریف شده‌ی در سیستم، زنجیره‌ی آن‌ها قابل پردازش باشد. این مورد را در قسمت قبل، ذیل عنوان «بررسی تابع combineReducers با یک مثال» بیشتر بررسی کردیم.

پس از ایجاد reducer اختصاصی عمل افزودن مقدار شمارشگر، فایل جدید src\reducers\index.js را نیز با محتوای زیر ایجاد می‌کنیم:
import { combineReducers } from "redux";

import counterReducer from "./counter";

const rootReducer = combineReducers({
  counterReducer
});

export default rootReducer;
کار این فایل، مدیریت مرکزی تمام reducerهای سفارشی تعریف شده‌ی در برنامه‌است. لیست آن‌ها را به متد combineReducers ارسال کرده و در نهایت یک rootReducer ترکیب شده‌ی از تمام آن‌ها را دریافت می‌کنیم.


ایجاد store مخصوص Redux

تا اینجا رسیدیم به یک rootReducer متشکل از تمام reducerهای سفارشی برنامه. اکنون بر اساس آن در فایل src\index.js، یک store جدید را ایجاد می‌کنیم:
import { createStore } from "redux";
import reducer from "./reducers";

//...

const store = createStore(
  reducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

//...
نکته 1: چون شیء rootReducer در فایل src\reducers\index.js واقع شده‌است، دیگر در حین import، نیازی به ذکر نام فایل index آن نیست.
نکته 2: در اینجا روش فعالسازی افزونه‌ی redux-devtools را نیز ملاحظه می‌کنید. ابتدا بررسی می‌شود که آیا متد ویژه‌ی فراخوانی این افزونه وجود دارد یا خیر؟ اگر بله، فراخوانی می‌شود. بدون این پارامتر دوم، افزونه‌ی redex dev tools، هیچ خروجی را نمایش نخواهد داد.


اتصال React به Redux

کتابخانه‌ی react-redux تنها به همراه دو شیء مهم connect و Provider است. شیء Provider آن شبیه به Context API خود React است و هدف آن، ارسال ارجاعی از store ایجاد شده، به برنامه‌ی React است. پس از ایجاد store در فایل src\index.js، اکنون نوبت به اتصال آن به برنامه‌ی React ای جاری است. به همین جهت در بالاترین سطح برنامه، ابتدا شیء کامپوننت App را با شیء Provider محصور می‌کنیم:
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducer from "./reducers";

// ...
const store = createStore(
  reducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);
کامپوننت Provider، از طریق props خود نیاز به دریافت store تعریف شده را دارد. به این ترتیب هر کامپوننتی که در درخت کامپوننت‌های App قرار می‌گیرد، می‌تواند با redux store کار کند.


تامین state کامپوننت شمارشگر از طریق props

همانطور که عنوان شد، کامپوننت Counter به همراه state نیست و ما قصد نداریم در آن از state خود React استفاده کنیم؛ البته فلسفه‌ی آن‌را در قسمت اول این سری بررسی کردیم و همچنین اگر کامپوننتی نیاز به اشتراک گذاری اطلاعات خودش را با لایه‌های زیرین یا بالاتر از خود ندارد، شاید اصلا نیازی به Redux نداشته باشد و همان state استاندارد React برای آن کافی است. بنابراین می‌توان برنامه‌ای را داشت که ترکیبی از state استاندارد React، در کامپوننت‌های متکی به خود و Redux، در کامپوننت‌هایی که باید اطلاعاتی را با هم به اشتراک بگذارند، باشد. برای مثال، کامپوننت مثال جاری، واقعا نیازی را به Redux، برای مدیریت حالت خود، ندارد؛ هدف ما در اینجا بررسی نحوه‌ی برقراری ارتباطات یک سیستم مبتنی بر Redux، در برنامه‌های React است.
بنابراین در اینجا و کامپوننتی که قرار است از Redux برای مدیریت حالت خود استفاده کند، هر اطلاعاتی که به آن از طریق react-redux store وارد می‌شود، از طریق props به آن ارسال خواهد شد. برای مثال در اینجا مقدار count، از طریق props خوانده می‌شود و همچنین امکان ارسال action ای خاص به متد reducer تعریف شده نیز باید تعریف شود. بنابراین در ادامه نیاز داریم تا یک کامپوننت React را به redux store متصل کنیم. برای این منظور فایل جدید src\containers\Counter.js را با محتوای زیر ایجاد می‌کنیم:
import { connect } from "react-redux";

import { incrementValue } from "../actions";
import Counter from "../components/counter";

const mapStateToProps = state => {
  return state;
};

const mapDispatchToProps = dispatch => {
  return {
    increment() {
      dispatch(incrementValue());
    }
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(Counter);
ابتدا متد connect را از react-redux دریافت می‌کنیم. connect خود متدی است که منتظر یک کامپوننت React است؛ مانند Counter. همچنین به عنوان پارامتر، توابعی را دریافت می‌کند که اطلاعات redux store را به کامپوننت، نگاشت می‌کنند؛ مانند props و actions. در اینجا دو تابع نگاشت state به props و همچنین dispatch به props را ملاحظه می‌کنید (توابع mapStateToProps و mapDispatchToProps)؛ هرچند الزامی نیست، ولی بهتر است از همین روش نامگذاری استفاده شود.

زمانیکه در مورد store در redux صحبت می‌شود، داخل آن یک شیء بزرگ state قرار گرفته‌است که حاوی کل state برنامه‌است. اما شاید هر کامپوننت به تمام آن نیازی نداشته باشد. برای مثال شاید کامپوننت شمارشگر، اهمیتی به اطلاعات خطاهای سیستم و یا کاربر وارد شده‌ی به سیستم که در شیء کلی state موجود در store وجود دارند، ندهد. به همین جهت متد mapStateToProps، کل state برنامه را دریافت کرده و به ما اجازه می‌دهد تا تنها اطلاعاتی را که از آن نیاز داریم، به صورت props دریافت کنیم. به این ترتیب از رندر مجدد این کامپوننت نیز جلوگیری خواهد شد؛ چون این کامپوننت دیگر وابسته‌ی به تغییرات سایر اجزای کل state برنامه، نخواهد بود و اگر آن‌ها تغییر کردند، این کامپوننت رندر مجدد نخواهد شد.
بنابراین می‌توان متد mapStateToProps را به صورت کلی زیر نیز تعریف کرد:
const mapStateToProps = (state) => { return state };
هرچند این روش در مثال ما بدون مشکل کار می‌کند، اما چون کل state را دریافت می‌کند، مشکل رندر مجدد کامپوننت را به ازای هر تغییری در state کلی برنامه به همراه خواهد داشت.

یک نکته: اگر کامپوننتی نیاز به تامین state خود را از طریق props نداشت و فقط کارش صدور رخ‌دادها است، می‌توان پارامتر اول متد connect را نال وارد کرد.

پارامتر dispatch متد mapDispatchToProps، به متد store.dispatch اشاره می‌کند. بنابراین توسط آن امکان ارسال actions را میسر کرده و می‌توان state را توسط reducerهای تعریف شده، تغییر داد که در نتیجه‌ی آن props جدیدی به کامپوننت منتقل می‌شوند. این تابع نیز یک شیء را باز می‌گرداند. این شیء را فعلا با یک متد دلخواه مقدار دهی می‌کنیم که توسط پارامتر dispatch رسیده‌ی به آن، متد action creator تعریف شده‌ی در فایل src\actions\index.js را به نام incrementValue، فراخوانی می‌کند؛ دقیقا عملی شبیه به فراخوانی store.dispatch(createAddAction(2)) در قسمت قبل که از آن برای ارسال یک اکشن، به reducer متناظری استفاده شد.

یک نکته: اگر کامپوننتی کار صدور رخ‌دادها را انجام نمی‌دهد، می‌توان پارامتر دوم متد connect را بطور کامل حذف کرد و قید نکرد.


استفاده از کامپوننت جدید خروجی متد connect، جهت تامین props کامپوننت شمارشگر

در انتهای فایل src\components\counter.jsx، چنین سطری درج شده‌است:
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
این شیء حاصل، به خودی خود، سبب بروز تغییری در کامپوننت شمارشگر نمی‌شود. بلکه یک کامپوننت دربرگیرنده‌ی کامپوننت Counter را ایجاد می‌کند (به همین جهت آن‌را در پوشه‌ی containers یا دربرگیرنده‌ها قرار دادیم). بنابراین برای استفاده‌ی از آن، به کامپوننت src\App.js مراجعه کرده و جائیکه المان کامپوننت Counter قبلی درج شده، آن‌را به صورت زیر تغییر می‌دهیم:
import "./App.css";

import React from "react";

import CounterContainer from "./containers/Counter";

function App() {
  return (
    <main className="container">
      <CounterContainer />
    </main>
  );
}

export default App;
ابتدا کامپوننت جدید CounterContainer را که تبادل اطلاعات بین کامپوننت Counter اصلی و state و action مخزن redux را برقرار می‌کند، import کرده و سپس المان جدید آن‌را جایگزین المان کامپوننت شمارشگر اصلی می‌کنیم.

اکنون کامپوننت شمارشگر src\components\counter.jsx، دو شیء را از طریق props دریافت می‌کند؛ یکی کل state است که خاصیت count داخل آن قرار دارد و از طریق mapStateToProps تامین می‌شود. دیگری متد increment ای است که در متد mapDispatchToProps تعریف کردیم و کار صدور رخ‌دادی را به reducer متناظر، انجام می‌دهد. به همین جهت تغییرات ذیل را در کامپوننت Counter اعمال می‌کنیم:
import React, { Component } from "react";

class Counter extends Component {
  render() {
    console.log("props", this.props);
    const {
      counterReducer: { count },
      increment
    } = this.props;
    return (
      <section className="card mt-5">
        <div className="card-body text-center">
          <span className="badge m-2 badge-primary">{count}</span>
        </div>
        <div className="card-footer">
          <div className="d-flex justify-content-center align-items-center">
            <button className="btn btn-secondary btn-sm" onClick={increment}>
              +
            </button>
            <button className="btn btn-secondary btn-sm m-2">-</button>
            <button className="btn btn-danger btn-sm">Reset</button>
          </div>
        </div>
      </section>
    );
  }
}

export default Counter;
لاگ اولین بار دریافت this.pros این کامپوننت که اکنون توسط دربرگیرنده‌ی آن ارائه می‌شود، به صورت زیر است:


به همین جهت، خاصیت تو در توی this.props.counterReducer.count و همچنین اشاره‌گر به متد increment، توسط Object Destructuring به صورت زیر از this.props دریافتی، تجزیه شده‌اند:
    const {
      counterReducer: { count },
      increment
    } = this.props;
سپس مقدار count، توسط span نمایش داده و همچنین دکمه +  را به صورت onClick={increment} تکمیل کرده‌ایم تا با کلیک بر روی آن، متد increment که در حقیقت معادل فراخوانی store.dispatch(incrementValue()) است، اجرا شود. حاصل آن، افزایش مقدار شمارشگر است:


جزئیات کار با Redux store را نیز می‌توان در افزونه‌ی redux dev tools مشاهده کرد:


این افزونه در نوار ابزار پایین آن، امکان export کل state و سپس import و بازیابی آن‌را نیز به همراه دارد.


دریافت props از طریق کامپوننت دربرگیرنده و ارسال آن به کامپوننت اصلی

فرض کنید نیاز باشد تا اطلاعاتی را به صورت متداول React از طریق props، به کامپوننت دربرگیرنده‌ی کامپوننت شمارشگر ارسال کرد:
function App() {
  const prop1 = 123
  return (
    <main className="container">
      <CounterContainer prop1={prop1} />
    </main>
  );
}
برای دسترسی به آن، پارامتر دومی به متد mapStateToProps به نام ownProps اضافه می‌شود که حاوی props ارسالی به کامپوننت container است:
const mapStateToProps = (state, ownProps) => {
  console.log("mapStateToProps", { state, ownProps });
  return state;
};
در این حالت اگر نیاز به انتقال آن به کامپوننت اصلی بود، می‌توان شیء بازگشت داده شده‌ی از mapStateToProps را به همراه یک سری خواص سفارشی دریافتی از ownProps، تعریف کرد.


پیاده سازی دکمه‌ی کاهش مقدار شمارشگر

پس از آشنایی با روش کلی برقراری اتصالات سیستم react-redux، پیاده سازی دکمه‌ی کاهش مقدار شمارشگر بسیار ساده‌است و شامل مراحل زیر است:
1)  ایجاد نام نوع اکشن متناظر با دکمه‌ی کاهش مقدار
به فایل src\constants\ActionTypes.js، نوع جدید کاهشی را اضافه می‌کنیم:
export const Decrement = "Decrement";
2) ایجاد متد Action Creator
در فایل src\actions\index.js، متد ایجاد کننده‌ی شیء اکشن ارسالی به reducer متناظری را تعریف می‌کنیم تا بتوان بر اساس نوع آن در reducer کاهشی، منطق کاهش را پیاده سازی کرد:
export const decrementValue = () => ({ type: types.Decrement });
3) ایجاد تابع reducer مخصوص کاهش مقدار
اکنون در فایل src\reducers\counter.js، بر اساس نوع شیء رسیده، تصمیم به کاهش یا افزایش مقدار موجود در state گرفته می‌شود:
export default function counterReducer(state = initialState, action) {

  // ...

  if (action.type === types.Decrement) {
    return {
      count: state.count - 1
    };
  }

  return state;
}
4) تامین state کامپوننت شمارشگر از طریق props
در ادامه نیاز است بتوان اکشن کاهش را به این reducer ارسال کرد. به همین جهت به کامپوننت دربرگیرنده‌ی کامپوننت شمارشگر در فایل src\containers\Counter.js مراجعه کرده و به شیء خروجی متد mapDispatchToProps، متد کاهش را اضافه می‌کنیم:
import { decrementValue, incrementValue } from "../actions";
// ...

const mapDispatchToProps = dispatch => {
  return {
    // ...
    decrement() {
      dispatch(decrementValue());
    }
  };
};
5) استفاده از نتایج دریافتی از props
در آخر به فایل src\components\counter.jsx مراجعه کرده و اشاره‌گر به متد decrement را از طریق this.props دریافت می‌کنیم:
const {
      // ...
      decrement
    } = this.props;
 سپس آن‌را به onClick دکمه‌ی کاهش، انتساب خواهیم داد:
<button
  className="btn btn-secondary btn-sm m-2"
  onClick={decrement}
>
  -
</button>

به عنوان تمرین، پیاده سازی دکمه‌ی Reset را نیز انجام دهید که جزئیات آن بسیار شبیه به دو مثال قبلی افزودن و کاهش مقدار شمارشگر است.


بهبود کیفیت کدهای کامپوننت دربرگیرنده‌ی کامپوننت Counter

متد mapDispatchToProps فایل src\containers\Counter.js اکنون چنین شکلی را پیدا کرده‌است:
const mapDispatchToProps = dispatch => {
  return {
    increment() {
      dispatch(incrementValue());
    },
    decrement() {
      dispatch(decrementValue());
    }
  };
};
می‌توان با استفاده از تابع bindActionCreators که در قسمت قبل در مورد آن بحث شد، تعریف آن‌را به صورت زیر خلاصه کرد:
import { bindActionCreators } from "redux";

// ...

const mapDispatchToProps = dispatch => {
  return bindActionCreators(
    {
      incrementValue,
      decrementValue
    },
    dispatch
  );
};
با استفاده از تابع bindActionCreators کتابخانه‌ی redux، می‌توان تمام action creators واقع در فایل src\actions\index.js را به صورت یک شیء به آن ارسال کرد و پارامتر دوم آن‌را نیز به store.dispatch یا در اینجا به همان dispatch دریافتی توسط پارامتر dispatch متد mapDispatchToProps، تنظیم کرد. البته در این حالت props دریافتی در کامپوننت شمارشگر به صورت زیر تغییر می‌کنند:


به همین جهت نیاز است در متد رندر کامپوننت src\components\counter.jsx، نام‌هایی را که به متدهای action creator اشاره می‌کنند، به صورت زیر تغییر داد:
const {
      counterReducer: { count },
      incrementValue,
      decrementValue
    } = this.props;
و همچنین نام‌های منتسب به onClickها را نیز بر این اساس، اصلاح کرد.

روش دوم: در نگارش‌های اخیر react-redux می‌توان متد mapDispatchToProps را به صورت زیر نیز خلاصه و تعریف کرد که بسیار ساده‌تر است:
const mapDispatchToProps = {
  incrementValue,
  decrementValue
};
البته در این حالت نیز مابقی آن که شامل تغییر نام‌ها می‌شود، یکسان است.

همچنین بجای بازگشت کل state در متد mapStateToProps، می‌توان تنها خواص مدنظر را بازگشت داد:
const mapStateToProps = state => {
  //return state;
  return {
    count: state.counterReducer.count
  };
};
در این حالت props ارسالی به کامپوننت یک چنین شکلی را پیدا می‌کنند:


بنابراین باید در متد رندر کامپوننت شمارشگر، خاصیت count را به صورت معمولی دریافت کرد:
const {
      //counterReducer: { count },
      count,
      incrementValue,
      decrementValue
    } = this.props;

کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: state-management-redux-mobx-part03.zip
مطالب
SignalR - قسمت دوم
در قسمت اول بحث‌های مقدماتی درباره وب زمان واقعی (real time web) و معرفی کتابخونه SignalR به همراه یک مثال ساده رو با هم دیدیم. در ادامه به جزئیات ریزی از کتابخونه SignalR که توسط آقای David Fowler توسعه داده میشه میپردازم.

همونطور که قبلا هم اشاره شد قلب این کتابخونه در سمت سرور دو کلاس پایه PersistentConnection و Hub هستن که اولی سطحی پایینتر داره یعنی به تنظیمات و کدنویسی (بسیار) بیشتری برای پیاده‌سازی نیاز داره اما در عوض امکانات سطح پایینتری هم در اختیار برنامه نویس قرار میده که در برخی موارد موردنیاز هستن. در مورد بخشهای مختلف این دو کلاس و نحوه پیاده‌سازی هر دو کلاس فوق، تو آدرسی که قبلا اشاره کردم (آدرس پروژه متن باز این کتابخونه در github) راهنمایی‌های نسبتا مفصلی ارائه شده و نیازی به تکرار این مطالب در اینجا نیست. پیشنهاد میکنم که این مطالب رو با دقت مطالعه کنین.

SignalR به صورت توکار از 4 روشی که در قسمت قبل به اون اشاره شد برای برقراری ارتباط استفاده میکنه. WebSocket که به بسترهای جدیدی نیاز داره. Server-sent Events که تنها در مرورگرهایی که پشتیبانی کاملی از html5 دارند قابل استفاده است (بنابراین ie9 نمیتونه از این روش استفاده کنه). forever frame داده‌ها رو به‌صورت chunked (بخشی از استاندارد http 1.1) دریافت میکنه و روش آخر که Long-polling نام داره از هموش روش قدیمی اِیجکس (Ajax) بهره میبره و با استفاده از آبجکت معروف XmlHttpRequest کار ارتباط و تبادل داده‌ها رو انجام میده.

در اینجا برای بررسی این ارتباطات ابتدا برنامه چت ساده قسمت قبل رو در اینترنت اکسپلورر اجرا کنین و با استفاده developer tool (کلید F12) به درخواستهای مختلف ارسال شده به سرور نگاه کنین. پس از اجرای برنامه و قبل ارسال هرگونه داده به سرور در تب Network این ابزار چیزی شبیه به شکل زیر مشاهده خواهید کرد (البته باید قبل از ورود به صفحه برنامه چت روی دکمه Start capturing کلیک کنین):

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

حالا برنامه رو در یه مرورگر دیگه که از html5 پشتیبانی میکنه اجرا کنین. مثلا نتیجه در گوگل کروم و ابزار توسعه اون به شکل زیره:

همونطور که میبینین در اینجا روش استفاده شده Server Sent Events هست.

در فایرفاکس هم با استفاده از ابزار محبوب firebug نتیجه مشابه کروم بدست میاد:

البته اگر علاقه زیادی به کندوکاو در جزئیات این درخواستها دارین (مثل خود من) چیزی بهتر از fiddler2 پیدا نمیشه. میتونین پس از ارسال یک متن دوباره این درخواستها رو مورد بررسی قرار بدین و ببینین که چیجوری کانالهای ارتباط پس از ارسال و دریافت دیتا قطع و برقرار میشه.

این نکته رو هم باید یادآور بشم که هرچند که این کتابخونه بهترین روش رو میتونه انتخاب کنه اما به برنامه نویس امکان تعیین صریح روش ارتباط رو هم میده. اگر به راهنماهای این کتابخونه سر بزنین میبینین که امکانات زیادی بهش اضافه شده و امکانات زیادی هم در آینده به اون اضافه میشه. امکاناتی از قبیل ارسال داده‌ها به یک کلاینت خاص و یا به گروهی خاص از کلاینتها، خصوصی‌سازی آدرس سرور و همچنین پشتیبانی از Cross Domain در آخرین نسخه، امکان استفاده از Reactive Extension (بلاگ)، بحث Self Hosting که امکان خیلی جالبیه و میتونه خیلی جاها یه عنوان یک راه‌حل سبک و سریع به کار بیاد، قابلیت فوق العاده در بایندینگ داده‌ها در سمت سرور و مخصوصا کلاینت، امکان تشخیص برقراری یا قطع ارتباط کلاینتها در سمت سرور، استفاده از امکانات این کتابخونه برای برقراری ارتباط با کلاینتها در خارج از فضای کلاسهای مشتق شده از دو کلاس پایه (Hub و PersistentConnection) و چند مورد دیگه تا نسخه جاری اضافه شدند.

درحال حاضر دارم روی یه برنامه چت با امکانات بیشتر کار میکنم که پس از آماده شدن ارائه میدمش. یکی از پروژه‌های متن بازی که با استفاده از این کتابخونه توسعه داده شده jabbr.net است. یه اتاق گفتگوی کامل با امکانات جالبه که میتونین به اون هم یه سری بزنین.

در آخر هم یه لینک جالب برای مطالعه معرفی میکنم: Highest voted Signalr Questions - stackoverflow

بازخوردهای دوره
مدیریت نگاشت ConnectionIdها در SignalR به کاربران واقعی سیستم
- کلاینت سمت کاربر SiganlR که درون مرورگر اجرا می‌شود، اساسا جاوا اسکریپتی است. (البته برای جاوا یا دات نت و امثال آن هم کلاینت مخصوص دارد؛ ولی بحث مرورگر آن مشخص است)
+ این متد خاص هاب سمت سرور، در آخرین نگارش SiganlR به این نحو تغییر کرده‌است:
public override Task OnDisconnected(bool stopCalled)
{
    if (stopCalled)
    {
        // We know that Stop() was called on the client,
        // and the connection shut down gracefully.
    }
    else
    {
        // This server hasn't heard from the client in the last ~35 seconds.
        // If SignalR is behind a load balancer with scaleout configured, 
        // the client may still be connected to another SignalR server.
    }

    return base.OnDisconnected(stopCalled);
}
اگر پارامتر stopCalled با مقدار true فراخوانی شد، یعنی سمت کلاینت، با استفاده از کدهای جاوا اسکریپتی SignalR (فراخوانی شده به صورت خودکار در حین بستن یک تب یا مرورگر یا به صورت دستی به نحوی که عنوان شد)، درخواست بسته شدن صفحه را داده‌است. اگر مقدار آن false بود، یعنی سرور تشخیص داده‌است که در طی 35 ثانیه‌ی قبل کاربر فعالیتی نداشته‌است.
مطالب
داستانی از Unicode
یکی از مباحثی که به نظرم هر دانشجوی رشته کامپیوتر، فناوری اطلاعات و علاقمند به این حوزه باید بداند بحث کاراکترهاست؛ جدا از اینکه همه ما در مورد وجود ascii یا UTF-8 و ... و توضیحات مختصر آن اطلاع داریم ولی عده‌ای از دوستان مثل من هنوز اطلاعات پایه‌ای‌تر و جامع‌تری در این باره نداریم؛ در این مقاله که برداشتی از وب سایت smashing magazine  و W3 است به این مبحث می‌پردازیم.
کامپیوترها تنها با اعداد سر و کار دارند نه با حروف؛ پس این بسیار مهم هست که همه کامپیوترها بر روی یک سری اعداد مشخص به عنوان نماینده‌ای از حروف به توافق برسند. این توافق یکسان بین همه کامپیوترها بسیار مهم هست و باید طبق یک استاندارد مشترک استفاده شود تا در همه سیستم‌ها قابل استفاده و انتقال باشد؛ برای همین در سال 1960 اتحادیه استاندارهای آمریکا، یک سیستم رمزگذاری 7 بیتی را ایجاد کرد؛ به نام American Standard Code for Information Interchange یا کد استاندارد سازی شده آمریکایی برای تبادل اطلاعات یا همان ASCII. این هفت بیت به ما اجازه می‌داد تا 128 حرف را کدگذاری کنیم. این مقدار برای حروف کوچک و بزرگ انگلیسی و هم چنین حروف لاتین، همراه با کدگذاری ارقام و یک سری علائم نگارشی و کاراکترهایی از قبیل space ، tab و موارد مشابه و نهایتا کلیدهای کنترلی کافی بود. در سال 1968 این استاندارد توسط رییس جمهور وقت آمریکا لیندون جانسون به رسمیت شناخته شده و همه سیستم‌های کامپیوتری ملزم به رعایت و استفاده از این استاندارد شدند.
برای لیست کردن و دیدن این کدها و نمادهای حرفیشان می‌توان با یک زبان برنامه نویسی یا اسکریپتی آن‌ها را لیست کرد. کد زیر نمونه‌ای از کد نوشته شده در جاوااسکریپت است.
 <html> 

<body>
 <style type="text/css">p {float: left; padding: 0 15px; margin: 0;}</style> 

<script type="text/javascript">
 for (var i=0; i<128; i++) document.writeln ((i%32?'':'<p>') + i + ': ' + String.fromCharCode (i) + '<br>'); 

</script>
</body>
 </html>
در سال‌های بعدی، با قوی‌تر شدن پردازش‌گرها و 8 بیت شدن یک بایت به جای ذخیره 128 عدد توانستند 256 عدد را ذخیره کنند ولی استاندارد اسکی تا 128 کد ایجاد شده بود و مابقی را به عنوان ذخیره نگاه داشتند. در ابتدا کامپیوترهای IBM از آن‌ها برای ایجاد نمادهای اضافه‌تر و همچنین اشکال استفاده می‌کرد؛ مثلا کد 200 شکل  ╚ بود که احتمالا برنامه نویسان زمان داس، این شکل را به خوبی به خاطر میاورند یا مثلا حروف یونانی را اضافه کردند که با کد 224 شکل آلفا  α بود و بعد‌ها به عنوان  code page 437  نامگذاری شد. هر چند که هرگز مانند اسکی به یک استاندارد تبدیل نشد و بسیاری از کشورها از این فضای اضافی برای استانداردسازی حروف خودشان استفاده می‌کردند و در کشورها کدپیج‌های مختلفی ایجاد شد. برای مثال در روسیه کد پیچ 885 از کد 224 برای نمایش Я بهره می‌برد و در کد پیچ یونانی 737 برای نمایش حرف کوچک امگا ω استفاده می‌شد. این کار ادامه داشت تا زمانیکه مایکروسافت در سال 1980 کد پیچ Windows-1251 الفبای سریلیک را ارئه کرد. این تلاش تا سال 1990 ادامه پیدا کرد و تا آن زمان 15 کدپیج مختلف استاندارسازی شده برای الفبایی چون سیریلیک، عربی، عبری و ... ایجاد شد که این استانداردها از ISO-8859-1 شروع و تا  ISO-8859-16 ادامه داشت و موقعی که فرستنده پیامی را ارسال می‌کرد، گیرنده باید از کدپیج مورد نظر مطلع می‌بود تا بتواند پیام را صحیح بخواند.
بیایید با یک برنامه علائم را در این 15 استاندارد بررسی کنیم. تکه کدی که من در اینجا نوشتم یک لیست را که در آن اعداد یک تا 16 لیست شده است، نشان میدهد که با انتخاب هر کدام، کدها را از 0 تا 255 بر اساس هر استاندارد به ترتیب نمایش می‌دهد. این کار توسط تعیین استاندارد در تگ متا رخ میدهد.
در زمان بارگذاری، استانداردها با کد زیر به لیست اضافه می‌شوند.در مرحله بعد لیستی که  postback را در آن فعال کرده‌ایم، کد زیر را اجرا می‌کند. در این کد ابتدا charset انتخاب شده ایجاد شده و سپس یکی یکی کدها را به کاراکتر تبدیل می‌کنیم و رشته نهایی را درج می‌کنیم: ( دانلود فایل‌های زیر )
 private String ISO = "ISO-8859-";
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                for (int i = 1; i < 16; i++)
                {
                    ListItem item = new ListItem();
                    item.Text = ISO + i.ToString();
                    item.Value = i.ToString();
                    DropDownList1.Items.Add(item);
                }
                ShowCodes(1);
            }
           
        }

     
        protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (DropDownList1.SelectedItem != null)
            {
                int value = int.Parse(DropDownList1.SelectedValue);
                ShowCodes(value);
            }
            
        }

        private void ShowCodes(int value)
        {
            Response.Charset = ISO + value;
            string s = "";
            for (int i = 0; i < 256; i++)
            {
                char ch = (char)i;
                s += i + "-" + ch;
                s += "<br/>";//br tag
            }
            Label1.Text = s;
        }

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

یونیکد نجات بخش
اواخر سال 1980 بود که پیشنهاد یک استاندارد جدید داده شد و در آن به هر حرف و یا نماد در هر زبانی یک عدد یکتا نسبت داده میشد و باید بیشتر از 256 عدد می‌بود که آن را یونیکد نامیدند. در حال حاضر یونیکد نسخه 601 شامل 110 هزار کد می شود. 128 تای آن همانند اسکی است. از 128 تا 255 مربوط به علائم و علامت‌هاست که بیشتر آن‌ها از استاندارد ISO-8859-1 وام گرفته شده‌اند. از 256 به بعد هم بسیاری از علائم تلفظی و ... وجود دارد و از کد 880 زبان یونایی آغاز شده و پس از آن زبان‌های سیریلیک، عبری، عربی و الی آخر ادامه می‌یابند. برای نشان دادن یک کد یونیکد به شکل هگزادسیمال U+0048 نوشته می‌شود و برای تبدیل آن به دسیمال 4*16+8=72 استفاده می‌شود. به هر کد یونیکد، کد پوینت code point گفته میشود.
در ویکی پدیای فارسی، یونیکد اینگونه توضیح داده شده است: "نقش یونیکد در پردازش متن این است که به جای یک تصویر برای هر نویسه یک کد منحصر به فرد ارایه می‌کند. به عبارت دیگر، یونیکد یک نویسه را به صورت مجازی ارایه می‌کند و کار ساخت تصویر (شامل اندازه، شکل، قلم، یا سبک) نویسه را به عهده نرم‌افزار دیگری مانند مرورگر وب یا واژه‌پرداز می‌گذارد. "
یونیکد از 8 بیت یا 16 بیت استفاده نمی‌کند و با توجه به اینکه دقیقا 110 ،116 کد را حمایت می‌کند به 21 بیت نیاز دارد. هر چند که کامپیوترها امروزه از معمار‌های 32 بیتی و 64 بیتی استفاده می‌کنند، این سوال پیش می‌آید که ما چرا نمی‌توانیم کاراکترها را بر اساس این 32 بیت و 64 بیت قرار بدهیم؟ پاسخ این سوال این‌است که چنین کاری امکان پذیر است و بسیاری از نرم افزارهای نوشته شده در زبان سی و سی ++ از wide character حمایت می‌کنند. این مورد یک کاراکتر 32 بیتی به نام wchar_t است که نوعی داده char توسعه یافته هشت بیتی است و بسیاری از مرورگرهای امروزی از آن بهره مند هستند و تا 4 بیلیون کاراکتر را حمایت می‌کنند.
شکل زیر دسته بندی از انواع زبان‌های تحت حمایت خود را در نسخه 5.1 یونیکد نشان می‌دهد:


کد زیر در جاوااسکریپت کاراکترهای یونیکد را در مرز معینی که برایش مشخص کرده‌ایم نشان می‌دهد:
 <html> 

<body>
 <style type="text/css">p {float: left; padding: 0 15px; margin: 0;}</style> 

<script type="text/javascript">
for (var i=0; i<2096; i++)
   document.writeln ((i%256?'':'<p>') + i + ': ' + String.fromCharCode (i) + '<br>'); 

</script>
</body>
 </html>

CSS & Unicode
یکی از جذاب‌ترین خصوصیات در css، خصوصیت Unicode-range است. شما میتوانید برای هر کاراکتر یا حتی رنج خاصی از کاراکترها، فونت خاصی را اعمال کنید. به دو نمونه زیر دقت کنید:
/* cyrillic */
@font-face {
  font-style: normal;
  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/mErvLBYg_cXG3rLvUsKT_fesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-style: normal;
  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/-2n2p-_Y08sg57CNWQfKNvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-style: normal;
  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/u0TOpm082MNkS5K0Q4rhqvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
  unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
  font-style: normal;
  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/NdF9MtnOpLzo-noMoG0miPesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
  unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB;
}
/* latin-ext */
@font-face {
  font-style: normal;
  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
در صورتی که در Unicode-range، تنها یک کد مانند U+20AD نوشته شود، فونت مورد نظر فقط بر روی کاراکتری با همین کد اعمال می‌شود. ولی اگر بین دو کد از علامت - استفاده شود، فونت مورد نظر بر روی کاراکترهایی که بین این رنج هستند اعمال می‌شود U+0025-00FF و حتی می‌توان اینگونه نوشت ??U+4 روی کاراکترهایی در رنج U+400 تا U+4FF اعمال می‌شوند. برای اطلاعات بیش‌تر به اینجا و اینجا  مراجعه کنید.
به 65536 کد اول یونیکد Basic Multilingual Plan یا به اختصار BMP می‌گویند و شامل همه کاراکترهای رایجی است که مورد استفاده قرار می‌گیرند. همچنین یونیکد شامل یک فضای بسیار بزرگ خالی است که به شما اجازه توسعه دادن آن را تا میلیون‌ها کد می‌دهد. به کاراکترهایی که در این موقعیت قرار می‌گیرند supplementary characters یا کاراکترهای مکمل گویند. برای اطلاعات بیشتر می‌توانید به سایت رسمی یونیکد مراجعه کنید. در اینجا هم مباحث آموزشی خوبی برای یونیکد دارد، هر چند کامل‌تر آن در سایت رسمی برای نسخه‌های مختلف یونیکد وجود دارد.


UTF-8 نجات بخش می‌شود
بسیاری از مشکلات ما حل شد. همه حروف را داریم و مرورگر‌ها نیز همه حروف را میشناسند؛ ولی برای ما دو مشکل ایجاد کرده است:
  • بسیاری از نرم افزارها و پروتکل‌ها هنوز 8 بیتی کار می‌کنند.
  • اگر یک متن انگلیسی ارسال کنید، 8 بیت هم کافی است ولی در این حالت 32 بیت جابجا می‌شود؛ یعنی 4 برابر و در ارسال و دریافت و پهنای باند برایمان مشکل ایجاد می‌کند.
برای حل این مشکل استاندارهای زیادی چون USC-2 یا UTF-16 ایجاد شدند ولی در سال‌های اخیر برنده رقابت، UTF-8 بود که مخفف عبارت Universal Character Set Transformation Format 8 bit می‌باشد. این کدگذاری بسیار هوشمندانه عمل می‌کند. موقعی که شما کاراکتری را وارد می‌کنید که کدش بین 0 تا 255 است، 8 بیت به آن اختصاص می‌دهد و اگر در محدوده‌ای است که بتوان دو بایت را به آن اختصاص داد، دوبایت و اگر بیشتر بود، سه بایت و اگر باز بیشتر بود 4 بایت به آن اختصاص میدهد. پس با توجه به محدوده کد، تعداد بایت‌ها مشخص می‌شوند. بنابراین یک متن نوشته شده انگلیسی که مثلا از کدهای بین 0تا 128 استفاده می‌کند و فرمت ذخیره آن UTF-8 باشد به ازای هر کارکتر یک بایت ذخیره می‌کند.

مقایسه‌ای بین نسخه‌های مختلف :

همانطور که می‌بینید UTF-8 برای کاراکترهای اسکی، از یک بایت و برای دیگر حروف از دوبایت و برای بقیه BMP‌ها از سه بایت استفاده میکند و در صورتی که کاراکتری در ناحیه مکمل supplementary باشد، از چهار بایت استفاده خواهد کرد. UTF-16 از دو بایت برای نمایش کاراکترهای BMP و از 4 بایت برای نمایش کاراکترهای مکمل استفاده می‌کند و در UTF-32 از 4 بایت برای همه کاراکترها یا کد پوینت‌ها استفاده می‌شود.

نظرات اشتراک‌ها
تبدیلگر ایران سیستم به یونیکد
مرسی با این کد کاملا صحیح کاراکترها تبدیل میشه. اما همچنان زمان آپلود فایل خطا میداد که مقدار فیلد شماره لیست در فایل بیمه شدگان صحیح نیست.
یک راه دیگه رو امتحان کردم که بالاخره جواب داد! در فولدر DataBase برنامه تامین اجتماعی دو فایل DSKKAR00  و DSKWOR00 دیدم که فیلدها در آن‌ها تعریف شده بود ولی دیتا براشون ثبت نشده، حدس زدم که برنامه تامین اجتماعی هم از همین فایل‌ها استفاده میکنه.
من در برنامه خودم کد مربوط به ایجاد فایل رو حذف و آدرس این فایل‌ها رو دادم که دیتا در این فایل‌ها Insert بشه و مشکل حل شد و سایت فایل‌های من رو قبول کرد !
فایل راهنمای تامین اجتماعی که ساختار جدول‌های این فایل‌ها رو نوشته بود اشتباه بوده و نوع فیلدهایی که در اون فایل گفته بود در برنامه خودشون استفاده نشده و در سایت تامین اجتماعی هم قبول نمی‌کرد! 
مطالب
آشنایی با CLR: قسمت چهاردهم
در ادامه قسمت قبلی روش‌های زیادی جهت اضافه شدن یک ماژول به یک اسمبلی وجود دارند. اگر شما از کامپایلر سی‌شارپ برای ساخت یک فایل PE با جدول مانیفست استفاده می‌کنید، می‌توانید از سوئیچ AddModule/ استفاده کنید. برای اینکه بدانیم چگونه می‌توان یک اسمبلی چند فایله ساخت بیاید فرض کنیم که دو فایل سورس کد با مشخصات زیر داریم:
RUT.cs: این سورس شامل کدهایی است که به ندرت در برنامه استفاده می‌شود.
FUT.cs: این سورس شامل کدهایی است که به طور مکرر مورد استفاده قرار می‌گیرد.

ابتدا به صورت زیر کد سورسی را که به ندرت استفاده می‌شود، به عنوان یک ماژول جداگانه کامپایل می‌کنم:
csc /t:module RUT.cs
اجرای این خط سبب ایجاد یک فایل به نام RUT.netmodule می‌گردد که یک DLL استاندارد است؛ ولی CLR به تنهایی توانایی بارگیری آن را ندارد. دفعه‌ی بعد سورس کدی را که مکرر استفاده می‌شود، به صورت یک ماژول کامپایل می‌کنیم و از آنجائیکه این ماژول استفاده‌ی زیادی دارد، آن را نگهدارنده‌ی جدول مانیفست معرفی می‌کنیم و به این دلیل که این ماژول نماینده‌ی کل اسمبلی است، نام خروجی آن را به جای FUT.dll به MultiFileLibrary.dll تغییر می‌دهیم:
csc /out:MultiFileLibrary.dll /t:library /addmodule:RUT.netmodule FUT.cs
خط بالا به علت سوئیچ t:library\ فایل MultiFileLibrary.dll را ایجاد می‌کند. این فایل شامل جدول متادیتای مانیفست می‌شود و سوئیچ به آن می‌گوید که باید ماژول RUT.netmodule را جزئی از اسمبلی بداند. این سوئیچ به کامپایلر اعلام می‌کند که ارجاع این فایل در جدول FileDef  و ExportedTypesDef ثبت شود.
بعد از اتمام عملیات کامپایل، مطابق شکل زیر دو فایل ایجاد می‌شود که فایل سمت راست شامل جدول مانیفست است. فایل RUT.netmodule شامل کد IL و جداول متادیتاهای مربوط به خواص و رویدادها و مواردی از این قبیل است که در این ماژول یافت می‌شود. فایل بعدی MultiFileLibrary.dll هست که شامل کد IL کد FUT.CS می‌شوذ بعلاوه جداول متادیتا مثل ماژول قبلی و جدول متادیتای مانیفست که باعث می‌شود به عنوان یک اسمبلی شناخته شود.



البته توجه داشته باشید که جدول مانیفست ارجاعی به نوع‌های عمومی استخراج شده داخل فایل خودش ندارد، زیرا که در جداول اختصاصی خودش موجود است و در ذخیره سازی صرفه جویی می‌گردد.
بعد از اینکه MultiFileLibrary.dll ساخته شد، به منظور آزمایش کردن جداول متادیتا می‌توانید از ابزار ILDasm.exe استفاده کنید تا ارجاع به فایل RUT.netmodule به شما ثابت شود. آنچه در زیر می‌بینید نمایی از جداول FileDef و ExportedTypesDef است:
File #1 (26000001)
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
Token: 0x26000001
Name : RUT.netmodule
HashValue Blob : e6 e6 df 62 2c a1 2c 59 97 65 0f 21 44 10 15 96 f2 7e db c2
Flags : [ContainsMetaData] (00000000)


ExportedType #1 (27000001)
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
Token: 0x27000001
Name: ARarelyUsedType
Implementation token: 0x26000001
TypeDef token: 0x02000002
Flags : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass]
[BeforeFieldInit](00100101)
 
همانطور که در بالا می‌بینید فایل RUT.netmodule با شناسه‌ی (توکن) 0x26000001 به عنوان بخشی از اسمبلی شناخته می‌شود و به نوع کد IL آن اشاره می‌کند.

قابل توجه افراد کنجکاو: توکن‌های جداول متا، مقادیر 4 بایتی است که بایت پر ارزش آن اشاره می‌کند که برای یافتن آن باید به چه جدولی ارجاع کرد. مقادیر زیر این نکته را روشن می‌کند که هر کد ابتدایی به چه جدولی اشاره می‌کند:
 0x01
 TypeRef
 0x02
 TypeDef
 0x23
 AssemblyRef
 0x26
 File
file definition

 0x27
 ExportedType
برای دیدن لیست کاملی از این کدها فایل Corhdr.h را که به همراه فریم ورک دات نت نصب می‌شود، مطالعه فرمایید. سه بایت باقیمانده هم بر اساس جدولی که به آن ارجاع شده است مشخص می‌گردد؛ مثلا در مثال بالا کد 0x26000001  به اولین سطر جدول File اشاره می‌کند. برای اکثر جدول‌ها شماره گذاری سطرها از عدد 1 آغاز می‌شود نه صفر یا برای برای جداول TypeDef عموما از عدد 2 آغاز می‌شود. 

برای اجرای اسمبلی، کامپایلر نیاز دارد که همه‌ی فایل‌های اسمبلی، نصب شده و قابل دسترس باشند و در صورتیکه شما فایل RUT.netmodule را حذف کنید کامپایلر سی شارپ خطای زیر را صادر می‌کند:
fatal error CS0009: Metadata file 'C:\ MultiFileLibrary.dll' could not be opened—'Error importing module 'RUT.netmodule' of assembly 'C:\ MultiFileLibrary.dll'—The system cannot find the file specified'

و این خطا بدین معنی است که برای ساخت اسمبلی باید تمامی فایل‌ها حاضر و مهیا باشند. هر کد کلاینتی که اجرا می‌شود آن متد را صدا می‌زنند. موقعی که یک متد برای اولین بار فراخوانی می‌شود، CLR عملیات شناسایی جهت شناسایی ارجاعات آن در پارامترها، نوع خروجی متد و متغیرهای محلی آن اجرا می‌کند. سپس تلاش می‌کند تا فایل اسمبلی ارجاع شده را که شامل مانیفست هست، بار کند. اگر نوعی که لازم داریم در همین فایل متد وجود داشته باشد، اجرای عملیات را به سمت آن آغاز می‌کند ولی اگر جدول مانیفست ارجاع را به فایل دیگری بدهد، آن فایل در حافظه بار شده و سپس آن نوع را در دسترس قرار می‌دهد. 
خطوط بالا این نکته را روشن می‌کند که فایل‌های اسمبلی را تنها موقعی در حافظه بار میکند که ارجاعی از نوع موجود در آن صدا زده شده باشد؛ یعنی اینکه در زمان اجرای برنامه، لازم نیست که همه‌ی فایل‌ها حاضر و مهیا باشند.
مطالب
نحوه‌ی فعال سازی library caching زمانیکه یک Silverlight library را تولید کرده‌ایم

در مورد کاهش حجم فایل‌های XAP سیلورلایت زمانیکه از اسمبلی‌های کتابخانه‌های دیگر مانند Silverlight toolkit استفاده می‌شود، در این فصل بحث شده است و راه حل، استفاده از گزینه‌ی reduce XAP size by using application library caching است. به این صورت کاربران دیگر به ازای هر بار مشاهده‌ی سایت نیازی نخواهند داشت تا یک سری کتابخانه‌ی کمکی را که هیچ تغییری در آن‌ها حاصل نخواهد شد، دریافت کنند و اطلاعات آن‌ها از cache مرورگر خوانده می‌شود. این مورد با کتابخانه‌ها و ابزارهای کمکی تولید شده توسط مایکروسافت کار می‌کند. اما اگر خودتان یک Silverlight library را تولید کنید، چنین اتفاقی رخ نخواهد داد و باز هم فایل اسمبلی کتابخانه‌ی شما درون فایل XAP اصلی برنامه قرار گرفته و خبری از caching مجزای آن نیست. چرا اینطور است؟ چکار باید کرد؟!
علت آن بر می‌گردد به نحوه‌ی پیاده سازی library caching در VS.NET و Silverlight . برای این منظور چند مرحله باید طی شود تا این قابلیت برای کتابخانه‌های ساخت خودمان نیز فعال گردد:
الف) به کتابخانه‌ی خود باید امضای دیجیتال اضافه کنید:
اینکار با استفاده از امکانات خود VS.NET بسیار ساده است. به خواص پروژه مراجعه کنید. سپس برگه‌ی Signing را باز کرده و گزینه‌ی Sign the assembly را انتخاب کنید (شکل زیر). در قسمت choose a strong name key file ، گزینه‌ی new را انتخاب کرده و پس از وارد کردن یک نام دلخواه و گذر واژه‌ای، فایل pfx امضای دیجیتال اسمبلی شما تولید خواهد شد. اکنون تنها کافی است یکبار دیگر برنامه را کامپایل کنید.


ب) به یک فایل extMap.xml هم نیاز است:
هنگام پیاده سازی قابلیت library caching ، VS.NET به دنبال فایلی به نام AssemblyFileName.extmap.xml دقیقا در کنار فایل اسمبلی مورد نظر می‌گردد. ساختار عمومی این فایل XML به صورت زیر است:

<?xml version="1.0"?>
<manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<assembly>
<name>SLHelper</name>
<version>1.0.0.0</version>
<publickeytoken>f265933def965412</publickeytoken>
<relpath>SLHelper.dll</relpath>
<extension downloadUri="SLHelper.zip" />
</assembly>
</manifest>

نام، شماره نگارش، مسیر قرارگیری فایل اسمبلی مورد نظر و همچنین نام نهایی آن حین جدا سازی آن از XAP برنامه باید مشخص گردد. گزینه‌ی publickeytoken مهم‌ترین تنظیم این فایل است و قسمت الف را به همین منظور نیاز داشتیم. این عدد را به سادگی با استفاده از برنامه‌ی reflector می‌توان بدست آورد (شکل زیر).



جهت ساده سازی قسمت (ب)، برنامه‌ی کمکی را از آدرس ذیل می‌توانید دریافت کنید:
Utility: Extmap Maker

برای مطالعه بیشتر
Silverlight 3: Cached Assemblies and you can to

مطالب
Performance در AngularJS قدم چهارم
امیدوارم از مقالات قبلی لذت برده باشید. در این مقاله می‌خواهم در مورد $watch صحبت کنم.

سوال اول: $watch چیست و چه کاربردی دارد؟
$watch همان عملکرد Watching در AngularJS را انجام می‌دهد؛ ولی کاربردهای جالبی دارد. به کد زیر دقت کنید.
var errorChat=false;
$scope.$watch(function () {
     return errorChat;
}, function (newValue, oldValue) {
     if(newValue ==true){
          alert('قسمت محاوره سامانه با مشکل روبرو شده است لطفا با مدیریت تماس بگیرید')
     }                        
});
فرض کنید سناریوی پروژه به این صورت است که ما قسمت‌های مختلفی را در صفحه داریم و یکی از این قسمت‌ها، چت روم می‌باشد. می‌خواهیم در صورتیکه خطای اتصال و یا هر نوع خطایی در این قسمت بود، با پیغامی به کاربر گزارش داده شود. ما از متغیر errorChat برای این کار استفاده کرده‌ایم. با فرض اینکه در توابع دیگر در صورتیکه خطایی وجود داشته باشد، مقدار این متغیر را true می‌کند و ما بر حسب رصد این متغیر پیغام خطا را نمایش می‌دهیم. 

سوال دوم: این کد به نحوه احسن کار می‌کند؛ پس مشکل کجاست؟
مشکل اینجاست بعد از اینکه متغیر errorChat مقدار true گرفت و ما هشدار را نمایش دادیم، باز این متغییر رصد می‌شود که لزومی به این کار نیست (فرض ما بر این است که بعد از بروز خطا دیگر قسمت چت روم کار نخواهد کرد) و متوقف کردن این متغیر باعث می‌شود Performance در نهایت بهتر شود. خوب برای اینکه رصد این متغییر را متوقف کنیم از کد زیر استفاده می‌کنیم. به کد زیر توجه کنید:
var errorChat=false;
var stop=$scope.$watch(function () {
     return errorChat;
}, function (newValue, oldValue) {
     if(newValue ==true){
          stop();
          alert('قسمت محاوره سامانه با مشکل روبرو شده است لطفا با مدیریت تماس بگیرید')
     }                        
});
خوب؛ فکر کنم تفاوت این دو کد با هم روشن است. ما یک متغییر stop به کد اضافه کردیم. این متغیر با تابع متوقف کننده $watch مربوط به errorChat پر می‌شود و در نهایت با اجرای این تابع، عمل watching متغییر errorChat متوقف می‌شود. اگر با setInterval یا setTimeout در Javascript کار کرده باشید، شباهت این موردها را متوجه خواهید شد.