Quantcast
Channel: ‫فید مطالب .NET Tips
Viewing all 1980 articles
Browse latest View live

‫مبانی TypeScript؛ تهیه فایل‌های تعاریف نوع‌ها

$
0
0
فایل‌های تعاریف نوع‌ها (Type Definitions) امکان استفاده‌ی ساده‌تر از انواع و اقسام کتابخانه‌های جاوا اسکریپتی موجود را فراهم می‌کنند. این فایل‌ها حاوی تعاریف نوع‌های استفاده شده‌ی در کتابخانه‌های جاوا اسکریپتی هستند که بر اساس TypeScript تهیه نشده‌اند. حاوی هیچ نوع پیاده سازی نیستند و تنها از اینترفیس‌هایی تشکیل می‌شوند که راهنمای کامپایلر TypeScript جهت بررسی نوع‌ها هستند و همچنین به عنوان راهنمای ادیتورهای TypeScript جهت ارائه‌ی Intellisense کاملتر و دقیق‌تری نیز می‌توانند بکار روند. به آن‌ها TypeScript wrapper for JavaScript libraries هم می‌گویند. این فایل‌ها دارای پسوند d.ts. هستند.


منابع یافتن فایل‌های تعاریف نوع‌ها

- بزرگترین مخزن کد فایل‌های تعاریف نوع‌های TypeScript، در سایت Github و در مخزن کد DefinitelyTyped قابل مشاهده است:
https://github.com/DefinitelyTyped/DefinitelyTyped
- همچنین ابزار دیگری به نام «Typings type definition manager» نیز می‌تواند برای این منظور بکار رود.
- علاوه بر این‌ها، بسته‌های npm نیز می‌توانند به همراه تعاریف فایل‌های .d.ts باشند.


مفهوم Ambient Modules

پروژه‌های TypeScript عموما به همراه تعداد زیادی ماژول هستند. به این ترتیب هر ماژول نیاز به d.ts. فایل مخصوص خودش خواهد داشت که نگهداری آن‌ها مشکل خواهد بود. به همین جهت یک Solution متشکل از تعدادی ماژول، می‌تواند تمام تعاریف نوع‌ها را در یک تک فایل d.ts. نگهداری کند که به آن Ambient Module نیز می‌گویند. برای نمونه فایل d.ts. ذیل را درنظر بگیرید:
 // cardCatalog.d.ts
declare module "CardCatalog"{
   export function printCard(callNumber: string): void;
}
در اینجا نحوه‌ی تعریف یک module از نوع ambient را مشاهده می‌کنید که تنها حاوی تعاریف export شده‌است؛ بدون به همراه داشتن پیاده سازی آن‌ها.
سپس برای استفاده‌ی از این فایل d.ts. خواهیم داشت:
 // app.ts
/// <reference path="cardCatalog.d.ts" />
import * as catalog from "CardCatalog";
چون فایل‌های d.ts. دارای پیاده سازی‌های مرتبط نیستند، کار import آن‌ها همانند سایر ماژول‌ها نخواهد بود. ابتدا نیاز است با استفاده از Triple-Slash Directives به ابتدای ماژول فعلی الحاق شوند (مانند مثال فوق). سپس سطر import آن مانند قبل است؛ با این تفاوت که مسیر فایل ماژول را به همراه ندارد و بجای آن نام ماژولی که در فایل d.ts. ذکر شده‌است، تعریف می‌شود.


بررسی مخرن DefinitelyTyped

DefinitelyTyped مخزن کد عظیمی از فایل‌های تعاریف نوع‌های TypeScript است. هرچند دریافت این فایل‌ها از مخزن کد Github آن مانند سایر فایل‌های متداول آن سایت، اما چندین روش دیگر نیز برای کار با این مخزن کد وجود دارد:
- استفاده از NuGet. تقریبا تمام فایل‌های d.ts. آن به صورت یک بسته‌ی نیوگت مجزا نیز وجود دارند.
- استفاده از برنامه‌ی tsd. این برنامه یا type definition manager، به صورت اختصاصی برای کار با این نوع فایل‌ها طراحی شده‌است.
- استفاده از برنامه‌ی typings. این برنامه نیز یک type definition manager دیگر است. مزیت آن کار با چندین منبع مجزای ارائه‌ی فایل‌های d.ts. است که DefinitelyTyped تنها یکی از آن‌ها است.


یک مثال: دریافت مستقیم و افزودن فایل d.ts. مربوط به کتابخانه‌ی جاوا اسکریپتی lodash از مخزن کد DefinitelyTyped

در ادامه قصد داریم فایل تعاریف نوع‌های کتابخانه‌ی معروف lodashرا به پروژه‌ی جدیدی در VSCode اضافه کنیم. قدم اول، نصب خود کتابخانه است؛ از این جهت که فایل‌های d.ts.، فاقد هرگونه پیاده سازی هستند.
در مطلب «چرا TypeScript» نحوه‌ی کار با npm را جهت به روز رسانی کامپایلر TypeScript پیش فرض VSCode ملاحظه کردید. در اینجا نیز از npm برای نصب lodash استفاده می‌کنیم:
ابتدا خط فرمان را گشوده و سپس به پوشه‌ی پروژه‌ی خود وارد شوید. سپس دو دستور ذیل را صادر کنید:
npm init -f
npm install lodash --save


در ادامه به مخزن کد DefinitelyTyped وارد شده و پوشه‌ی مربوط به lodash را با جستجو پیدا کنید:
https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/lodash
در این پوشه تنها به فایل lodash.d.ts آن نیاز است. روی لینک این فایل کلیک کرده و سپس در صفحه‌ی باز شده، بر روی دکمه‌ی raw کلیک نمائید. این فایل نهایی را در ریشه‌ی پروژه‌ی جاری ذخیره کنید.
https://github.com/DefinitelyTyped/DefinitelyTyped/raw/master/lodash/lodash.d.ts

اگر به انتهای فایل lodash.d.ts دقت کنید، تعریف ambient module آن چنین شکلی را دارد و export آن lo dash است:
declare module "lodash" {
   export = _;
}
در ادامه برای استفاده‌ی از آن در فایل test.ts، به ابتدای فایل، با استفاده از Triple-Slash Directive، تعریف فایل d.ts. را اضافه کنید:
 /// <reference path="lodash.d.ts" />
سپس جهت دریافت یکجای تمام امکانات این کتابخانه خواهیم داشت:
 import * as _ from "lodash";
و اکنون بلافاصله intellisense به همراه مشخص بودن نوع پارامترهای یک متد فراهم است:
 let snakeCaseTitle = _.snakeCase("test this");
console.log(snakeCaseTitle);


برای گرفتن خروجی از این مثال همانند قبل، ابتدا Ctrl+Shift+P را فشرده و سپس انتخاب tasks:Run build task< و در ادامه فشردن F5 برای اجرا برنامه، نیاز است صورت گیرند:



مدیریت فایل‌های تعاریف نوع‌ها با استفاده از tsd

tsd یک برنامه‌ی خط فرمان است که کار یافتن و دریافت فایل‌های d.ts. را ساده می‌کند. این برنامه منحصرا با مخزن کد DefinitelyTyped کار می‌کند و پس از دریافت هر فایل d.ts.، ارجاعی به آن‌را در فایل tsd.json در ریشه‌ی پروژه ذخیره می‌کند. همچنین یک تک فایل tsd.d.ts حاوی تعاریف Triple-Slash Directive‌ها را نیز تولید می‌کند که در ادامه می‌توان تنها این فایل را به فایل‌های مدنظر الحاق کرد.
البته باید دقت داشت که این برنامه در ابتدای سال 2016 منسوخ شده اعلام گردید و با برنامه‌ی typings جایگزین شده‌است؛ هرچند هنوز هم مفید است و قابل استفاده.
روش دریافت tsd را در سایت definitelytyped.org می‌توانید مشاهده کنید:
http://definitelytyped.org/tsd
نصب آن نیز به صورت یک بسته‌ی npm است:
 npm install tsd -g
توضیحات بیشتر در مورد نحوه‌ی استفاده‌ی از tsd را در مخزن کد آن می‌توانید مشاهده کنید:
https://github.com/Definitelytyped/tsd#readme

برای مثال برای نصب فایل تعاریف نوع‌های lodash، ابتدا به پوشه‌ی پروژه از طریق خط فرمان وارد شده و سپس دستور ذیل را صادر کنید:
 D:\Prog\1395\VSCodeTypeScript>tsd install lodash --save
البته اگر موفق به اجرای این دستور نشدید؛ با خطای ذیل
 [ERR!] Error: connect ECONNREFUSED 10.10.34.36:443
به این معنا است که آدرس فایل‌های raw در github در ایران فیلتر شده‌است و قابل دسترسی نیست (آدرس IP فوق رنج خصوصی است).
اگر موفق به اجرای این دستور شدید، پوشه‌ی جدید typings در ریشه‌ی پروژه ایجاد خواهد شد. داخل آن فایل tsd.d.ts را نیز می‌توان مشاهده کرد که حاوی تعاریف فایل‌های نوع‌های دریافت شده‌است. از این پس در ابتدای فایل‌های ts، بجای تعریف جداگانه‌ی این فایل‌ها، تنها می‌توان نوشت:
 /// <reference path="./typings/tsd.d.ts" />
این تک فایل، reference pathهای تک تک فایل‌های نصب شده‌ی توسط tsd را به همراه دارد.


مدیریت فایل‌های تعاریف نوع‌ها با استفاده از typings

برنامه‌ی typings نیز بسیار شبیه به برنامه‌ی tsd است؛ با این تفاوت که منابع آن منحصر به مخزن کد definitelytyped نیست.
مخزن کد این برنامه در گیت‌هاب قرار دارد: https://github.com/typings/typings
و نصب آن با استفاده از دستور ذیل است:
 npm install typings --global
و اینبار دستور tsd قسمت قبل به نحو ذیل تغییر می‌کند:
 typings install lodash --ambient --save
این مورد نیز قابل استفاده نیست؛ چون به نظر تنها مرجع lodash در حال حاضر github است و آدرس https://raw.githubusercontent.com در ایران فیلتر شده‌است:
 typings ERR! caused by Unable to connect to "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/299b5caa22876ef27dc8e9a5b7fd7bf93457b6f4/lodash/lodash-3.10.d.ts"
typings ERR! caused by connect ECONNREFUSED 10.10.34.36:443
اگر موفق به نصب این بسته شدید، اکنون پوشه‌ی جدیدی به نام typings در ریشه‌ی سایت ایجاد شده‌است. داخل این پوشه علاوه بر فایل‌های دریافت شده، دو فایل browser.d.ts و main.d.ts را نیز می‌توان مشاهده کرد. فایل browser آن مخصوص برنامه‌های سمت کلاینت است و فایل main آن جهت برنامه‌های NodeJS طراحی شده‌است (که البته در مثال ما هر دو فایل حاوی یک محتوا هستند). این فایل‌ها حاوی تعاریف reference pathهای به فایل‌های نوع‌های نصب شده هستند. بنابراین ابتدای هر فایل ts می‌توان نوشت:
 /// <reference path="./typings/main.d.ts" />

‫مبانی TypeScript؛ فضاهای نام

$
0
0

همانطور که عنوان شد به ماژول‌های داخلی، فضاهای نام (namespace) گفته می‌شود. بنابراین از namespaceها نیز جهت مدیریت و ساماندهی پروژه‌های بزرگ با تعداد فایل‌های زیاد استفاده می‌شود. در واقع یک namespace حالت ساده‌تری از یک ماژول است؛ یعنی برای بارگذاری آن در مرورگر نیاز به loader خاصی نیست. بنابراین در پروژه‌هایی با مقیاس کوچکتر استفاده از namespaceها، انتخاب بهتری است. همچنین یکی از مزیت‌های مهم namespace جلوگیری از شلوغ شدن global scope است.  


نحوه‌ی تعریف namespace در TypeScript 

برای ایجاد یک namespace کافی است از کلمه کلیدی namespace استفاده کنیم: 

namespace Membership {
    export function AddMember(name: string) {
        // add a new member
    }
}

در کد فوق یک namespace را با نام Membership تعریف کرده‌ایم. درون این namespace می‌توانیم کدهای خود را بنویسیم. به عنوان مثال در کد فوق یک تابع را با نام AddMember درون آن تعریف کرده‌ایم و با استفاده از کلمه‌ی کلیدی export این تابع را در معرض دید مصرف کننده قرار داده‌ایم. لازم به ذکر است که یک namespace می‌تواند به صورت nested (تودرتو) نیز استفاده شود. اما باید توجه داشته باشید که برای داشتن چنین قابلیتی باید از export نیز قبل از کلمه‌ی کلیدی namespace استفاده شود: 

namespace Membership {
    export function AddMember(name: string) {
        // add a new member
    }
    export namespace Cards {
        export function IssueCard(memberNumber: number) {
            // issue new card
        }
    }
}

همانطور که مشاهده می‌کنید، درون یک nested namespace نیز می‌توانیم اشیاء را توسط کلمه‌ی کلیدی export در معرض دید مصرف کننده قرار دهیم.


نحوه‌ی استفاده از namespaceها در TypeScript 

برای استفاده از کدهای مثال قبل، در یک فایل دیگر می‌توانیم به صورت زیر یک ارجاع را به namespace مربوطه داشته باشیم:

/// <reference path="membership.ts" />

اکنون درون ادیتور، به صورت کامل به اشیاء export شده‌ی توسط فضای نام Membership دسترسی خواهید داشت. لازم به ذکر است به این نوع ارجاع، Triple-Slash References نیز گفته می‌شود. مزیت دیگر این روش این است که کامپایلر TypeScript ارجاعات را نیز کامپایل می‌کند.

برای استفاده از اشیاء مربوط به namespace ارجاع داده شده نیز باید از سینتکس نقطه استفاده کرد:

Membership.AddMember('Garrett');
Membership.Cards.IssueCard(500);

نکته‌ایی که در اینجا وجود دارد این است که کامپایلر TypeScript به صورت پیش‌فرض فایل‌های ارجاع داده شده را به فایل‌های جداگانه‌ایی کامپایل خواهد کرد و فرض را بر این خواهد گذاشت که هر کدام از فایل‌های کامپایل شده قرار است به ترتیب استفاده از آنها درون صفحه استفاده شوند. برای تولید یک فایل واحد می‌توانید از فلگ outFile استفاده کنید. با کمک این فلگ تمامی ارجاعات، بر اساس ترتیب استفاده از آنها، به یک فایل واحد تبدیل خواهند شد. جهت درک بهتر موضوع، یک فایل را با نام utilityFunctions.ts با محتویات زیر ایجاد کنید:

namespace Utility {
    export namespace Fees {
        export function CalculateLateFee(daysLate: number): number {
            return daysLate * .25;
        }
    }
    export function MaxBooksAllowed(age: number): number {
        if (age < 12) {
            return 3;
        } else {
            return 10;
        }
    }
    function privateFunc(): void {
        console.log('This is private....');
    }
}

در کد فوق یک namespace با نام Utility ایجاد کرده‌ایم سپس یک namespace دیگر و همچنین یک تابع را درون آن export کرده‌ایم. اکنون می‌خواهیم از این فضای نام درون یک فایل دیگر با نام app.ts استفاده کنیم. برای اینکار دورن فایل app.ts با استفاده از Triple-Slash یک ارجاع را به فضای نام عنوان شده اضافه کنید:

/// <reference path="utilityFunctions.ts" />

اکنون می‌توانیم از Intellisense مربوط به VS Code جهت دسترسی به اعضای فضای نام Utility استفاده کنیم:

لازم به ذکر است برای namespace مربوطه می‌توانیم alias نیز جهت دسترسی سریع‌تر، تعریف کنیم:

/// <reference path="utilityFunctions.ts" />
import util = Utility.Fees;
let fee = util.CalculateLateFee(10);
console.log(`Fee: ${fee}`);

توسط فرمان زیر نیز می‌توانیم فایل فوق را به راحتی کامپایل کنیم:

tsc --target ES5 app.ts

با فرمان فوق، فایل app.js به همراه تمامی فایل‌های ارجاع داده شده‌ی درون آن نیز کامپایل خواهند شد.

اکنون اگر بخواهیم توسط node فایل app.js را اجرا کنیم، با خطای ReferenceError: Utility is not defined مواجه خواهیم شد. دلیل آن نیز این است که node سعی در لود کردن فایل به صورت یک ماژول دارد و از آنجائیکه ما از namespace استفاده کرده‌ایم، فضای نام Utility توسط node قابل شناسنایی نمی‌باشد. اما درون یک صفحه‌ی HTML با قرار دادن فایل utilityFunctions.js و سپس app.js مشکلی نخواهیم داشت. برای حل این مورد در node کافی است از نکته‌ی یکی کردن فایل‌ها استفاده کنیم:

tsc --target ES5 app.ts --outFile out.js

در این‌حالت فایل‌ها به ترتیب استفاده، با هم ادغام شده و تبدیل به یک فایل با نام out.js خواهند شد.

‫مبانی TypeScript؛ تنظیمات کامپایلر

$
0
0
برای کامپایل کدهای TypeScript به جاوا اسکریپت، علاوه بر پارامترهای کامپایلر، از تنظیمات فایل مخصوصی به نام tsconfig.json نیز استفاده می‌شود که این موارد را در قسمت جاری بررسی خواهیم کرد.


نحوه‌ی اعمال تنظیمات کامپایلر TypeScript

روش‌های متفاوتی جهت اعمال تنظیمات کامپایلر TypeScript وجود دارند:
الف) ذکر پارامترها و سوئیچ‌های کامپایلر خط فرمان tsc به صورت مستقیم.
ب) بعضی از ادیتور‌ها و IDEها این پارامترها را به صورت گزینه‌ها و دیالوگ‌هایی ارائه می‌دهند.
ج) استفاده از یک Build task، همانند روشی که در تنظیمات VSCode در مطلب «چرا TypeScript» مشاهده کردید.
د) ذکر تنظیمات کامپایلر، در فایل مخصوصی به نام tsconfig.json.


گزینه‌های متداول کامپایلر TypeScript

گزینه‌های کامپایلر TypeScript نسبتا قابل توجه هستند و لیست کامل و به روز آن‌ها را در هندبوک تایپ‌اسکریپت می‌توانید مشاهده کنید. در اینجا تعدادی از مهم‌ترین‌ها را بررسی خواهیم کرد:
- سوئیچ module-- جهت مشخص سازی فرمت خروجی ماژول‌های TypeScript بکار می‌رود. در مطلب بررسی ماژول‌هاعنوان شد که TypeScript قادر است ماژول‌های تعریف شده را با سایر فرمت‌های متداول جاوا اسکریپت مانند common.js و amd نیز تولید کند. سوئیچ module-- جهت تنظیم این گزینه درنظر گرفته شده‌است. خلاصه‌ای این سوئیچ نیز m-- است. این سوئیچ یکی از مقادیر commonjs, amd, system, es2015 را می‌پذیرد. اگر es2015 را مشخص کردید، نیاز است target را نیز به ES6 تنظیم کنید.
- سوئیچ moduleResolution-- نحوه‌ی یافتن ارجاعات به ماژول‌ها را مشخص می‌کند. در اینجا روش‌های node.js و کلاسیک را می‌توان قید کرد.
- سوئیچ target-- برای تعیین نگارش خروجی جاوا اسکریپت تولیدی بکار می‌رود. حالت پیش فرض آن ES3 است و ES5 و ES6 را نیز پشتیبانی می‌کند.
- گزینه‌ی watch-- کامپایلر را در حالت watch نگه می‌دارد. در این حالت تغییرات آخرین تاریخ نوشته شدن در فایل‌های ts بررسی شده و در صورت یافتن تغییری، بلافاصله خروجی js آن‌ها تهیه می‌شود.
- سوئیچ outDir-- برای مشخص کردن پوشه‌ی فایل‌های تولیدی نهایی بکار می‌رود.
- گزینه‌ی noImplicitAny-- برای ممنوع کردن نوع‌های Any متغیرها به صورت پیش فرض است و در این حالت خطای کامپایلری را مشاهده خواهید کرد. استفاده از این گزینه به این معنا نیست که دیگر نمی‌توان از نوع Any استفاده کرد؛ بلکه به این معنا است که در صورت نیاز باید آن‌را به صورت صریح قید کنید.

یک مثال:
در VSCode و در پوشه‌ی vscode. آن، در تنظیمات فایل tasks.json، چنین گزینه‌هایی را می‌توان برای کامپایلر tsc، ذکر کرد:
"args": ["--target", "ES5",
            "--outDir", "js",
            "--module", "commonjs",
            "--sourceMap",
            "--watch",
             "app.ts"],
به این ترتیب خروجی جاوا اسکریپت آن با فرمت ES 5 بوده و فایل‌های نهایی آن در پوشه‌ی js، در ریشه‌ی پروژه نوشته خواهند شد. همچنین فرمت ماژول‌های خروجی آن نیز به commonjs تنظیم شده‌است. این کامپایلر sourceMapها را جهت امکان دیباگ بهتر کدها تولید کرده و در حالت watch قرار دارد.


بررسی کاربرد فایل tsconfig.json

فایل ویژه‌ی tsconfig.json در نگارش 1.5 تایپ‌اسکریپت معرفی گردید. هدف از این فایل، ساده کردن تعریف پارامترهای کامپایلر است؛ البته الزامی به استفاده‌ی از آن وجود ندارد.
این فایل مزایای ذیل را به همراه دارد:
الف) محل قرارگیری آن، ریشه‌ی پروژه‌ی TypeScript را مشخص می‌کند.
ب) تنظیمات ذکر شده‌ی در این فایل، به تمام فایل‌های موجود در پوشه و زیر پوشه‌های محل قرارگیری آن به صورت پیش فرض اعمال می‌شوند.
هنگامیکه در تنظیمات کامپایلر tsc، نام فایل یا فایل‌های ts ایی را ذکر نمی‌کنید، این کامپایلر در ابتدا به دنبال فایل tsconfig.json می‌گردد و بر این اساس فایل‌های ts را پردازش خواهد کرد. اگر مانند مثال فوق، در انتهای پارامترها، نام فایلی را ذکر کنید، از فایل tsconfig.json صرفنظر خواهد شد.
یک نکته: برای ذکر صریح محل فایل tsconfig از پارامتر project استفاده کنید:
 tsc --project ./lib
ج) امکان ذکر گزینه‌های کامپایلر را فراهم می‌کند.
در این حالت می‌توان کامپایلر tsc را بدون پارامتری اجرا کرد و این برنامه اطلاعات مورد نیاز خود را از فایل tsconfig.json دریافت خواهد کرد. باید دقت داشت، هر سوئیچی که در خط فرمان ذکر شود، پارامترهای معادل ذکر شده‌ی در فایل tsconfig.json را بازنویسی می‌کند. بنابراین در صورت وجود این فایل، می‌توان خاصیت args مثال قبل را به یک آرایه‌ی خالی تنظیم کرد.
د) امکان مشخص سازی الحاق و عدم الحاق فایل‌های ts را به همراه دارد.
 {
     "compilerOptions": {
                      "target": "es5",
                      "outDir": "js",
                      "module":"commonjs",
                      "sourceMap":true
     },
     "files": [
             "app.ts",
             "classes.ts"
     ]
}
نمونه‌ای از محتوای این فایل JSON را در مثال فوق مشاهده می‌کنید. در خاصیت compilerOptions آن، امکان تعریف پارامترهای کامپایلر وجود دارند؛ مانند تعیین نوع جاوا اسکریپت خروجی و پوشه‌ی نهایی آن. خاصیت آرایه‌ی files آن، برای ذکر لیست فایل‌هایی است که باید به کامپایلر ارسال شوند.
کار خاصیت files الحاق و include است. اگر می‌خواهید از پوشه‌ها و یا فایل‌هایی صرفنظر شود، از خاصیت exclude استفاده کنید:
{
      "compilerOptions": {
                     "target": "es5",
                     "outDir": "js"
      },
      "exclude": [
               "node_modules",
               "lib"
      ]
}
باید دقت داشت که در اینجا تنها یکی از خواص files و یا exclude را می‌توان ذکر کرد. اگر هر دو را با هم ذکر کنید، تنها از خاصیت files استفاده می‌شود.

یک نکته
در VSCode داخل فایل tsconfig.json با فشردن ctrl+space، به یک intellisense حاوی گزینه‌های تکمیل کننده‌ی آن خواهید رسید.


ساده سازی الحاق فایل‌های تعاریف نوع‌ها

در مطلب «مبانی TypeScript؛ تهیه فایل‌های تعاریف نوع‌ها» با فایل‌های ویژه‌ی d.ts. آشنا شدیم. استفاده‌ی از این فایل‌ها به همراه ذکر اجباری reference path مرتبط در ابتدای هر فایل ts است. این‌کار اضافی را با استفاده از فایل tsconfig.json می‌توان حذف کرد:
{
         "compilerOptions": {
                  "target": "es5",
                  "outDir": "js",
                  "module": "commonjs",
                  "sourceMap": true,
                  "watch": true
         },
         "files": [
                "app.ts",
                "typings/main.d.ts"
         ]
}
در اینجا با ذکر typings/main.d.ts در قسمت files، اطلاعات موجود در این فایل d.ts. به صورت سراسری به تمام فایل‌های ts موجود در پروژه‌ی جاری اعمال می‌شود و دیگر نیازی به ذکر صریح reference path آن نیست.

‫مبانی TypeScript؛ جنریک‌ها

$
0
0
بخش عمده‌ای از مهندسی نرم افزار، مربوط به ساخت کامپوننت‌هایی است که نه تنها به خوبی و مستحکم توسعه داده شده‌اند، بلکه قابلیت استفاده دوباره را نیز دارند.
کامپوننت‌هایی که قادر هستند بر روی داده‌های فعلی و همچنین داده‌های آینده، کار کنند، قابلیت‌های انعطاف پذیری را برای ساخت سیستم‌های نرم افزاری بزرگ در اختیار شما قرار خواهند داد.
در زبان هایی نظیر جاوا و سی شارپ، یکی از ابزارهای اصلی برای ساخت کامپوننت‌هایی با قابلیت استفاده مجدد، "جنریک‌ها" میباشد که امکان ساخت کامپوننت‌هایی را می‌دهند که با انواع داده‌های متنوعی به جای یک نوع داده، کار میکنند.
برای شروع به تابع زیر توجه کنید:
function identity(arg: number): number {
    return arg;
}
تابع identity هر آنچه را که به عنوان پارامتر به آرگومان آن ارسال کنیم، بازگشت خواهد داد. میتوانید آن را به مانند دستور "echo" در نظر بگیرید.
بدون استفاده از جنریک ها، باید برای هر نوع داده، یک تابع جدید و یا تابعی را به صورت کلی زیر در نظر بگیریم:
function identity(arg: any): any {
    return arg;
}
در تابع بالا از نوع any استفاده شده است. با استفاده از any، قطعا تابع بالا به صورت عمومی خواهد بود و تمام نوع داده‌ها را به عنوان آرگومان خواهد پذیرفت. ولی در واقع ما اطلاعات مربوط به اینکه نوع داده بازگشتی توسط تابع چه چیزی است را از دست خواهیم داد.
برای مثال اگر یک عدد را به آن ارسال کنیم، تنها متوجه خواهیم شد که نوع آن any میباشد؛ بنابراین به روشی نیاز داریم تا بتوانیم نوع داده آرگومان‌های تابع مورد نظر را کنترل کنیم.
در پیاده سازی زیر، ما از یک type variable خاصی استفاده خواهیم کرد که به جای مقادیر برای انوع داده‌ها مورد استفاده قرار می‌گیرد.
function identity<T>(arg: T): T {    return arg;
}
در تابع بالا با از T به عنوان یک type variable استفاده کرده‌ایم که امکان گرفتن انواع داده‌هایی را (برای مثال number) که توسط کاربر مهیا میشود، به ما خواهد داد.
این پیاده سازی از تابع identity، تحت عنوان تابع جنریک مطرح می‌شود که برای دامنه‌ی عظیمی از انواع داده‌ها می‌تواند مورد استفاده قرار گیرد و بر خلاف پیاده سازی قبل که از any استفاده کرده‌ایم، در این حالت دیگر اطلاعات نوع داده را از دست نخواهیم داد.
برای استفاده از تابع فوق ما دو روش را پیش رو خواهیم داشت:
  • ارسال تمام آرگومان‌ها که شامل آرگومان نوع داده هم میباشد
let output = identity<string>("myString");  // type of output will be 'string'
در کد بالا ما به صراحت T را با نوع داده string با استفاده از < > مقدار دهی کرده‌ایم.
  • روش دوم که شاید استفاده رایج از توابع جنریک هم هست، استفاده از امکان type argument inference میباشد.
let output = identity("myString");  // type of output will be 'string'
در کد بالا اینبار به صورت صریح نوع T را مشخص نکرده‌ایم و کامپایلر باتوجه به "myString"، نوع T را تعیین خواهد کرد. درحالیکه استفاده از امکان type argument inference خیلی مفید میباشد و کد را خیلی کم حجم و خوانا در اختیار ما قرار میدهد، ولی در مثال‌های پیچیده، امکان این وجود دارد که کامپایلر در تشخیص نوع داده، با خطا مواجه شود. در این صورت استفاده از روش اول مفید خواهد بود.
در ادامه اگر قصد لاگ کردن Length مربوط به آرگومان arg را در هر بار فراخوانی تابع داشته باشیم، می‌بایستی به شکل زیر عمل کنیم:
function loggingIdentity<T>(arg: T): T {    console.log(arg.length);  // Error: T doesn't have .length    return arg;
}
همانطور که انتظار داشتیم، کامپایلر خطایی مبنی بر نداشتن عضوی تحت عنوان length برای آرگومان arg را نمایش خواهد داد. همانطور که قبلا نیز اشاره کردیم، T جانشینی برای تمام نوع داده‌ها خواهد بود؛ بنابراین در اینجا میتوانیم یک داده‌ی از نوع number را که عضوی بنام length ندارد، هم به این تابع  پاس دهیم.
حال بیایید بگوییم که ما قصد داریم این تابع، با آرایه ای از T کار کند. در این صورت اگر با آرایه‌ها کار کنیم، عضوی به نام length را خواهیم داشت. به پیاده سازی زیر توجه کنید:
function loggingIdentity<T>(arg: T[]): T[] {    console.log(arg.length);  // Array has a .length, so no more error    return arg;
}
کد بالا را میتوانیم به این شکل تفسیر کنیم: تابع جنریک loggingIdentity یک type parameter را تحت عنوان T و یک آرگومان را تحت عنوان arg که آرایه ای از T هست، گرفته و آرایه‌ای از T را بازگشت خواهد داد. اگر ما آرایه‌ای از number را به آن پاس دهیم، آرایه‌ای از number‌ها را بازگشت خواهد داد.
در این حالت استفاده از T به عنوان type variable که بخشی از نوع داده‌هایی است که ما با آنها کار میکنیم، به جای پشتیبانی از تمام نوع داده‌ها، انعطاف پذیری بالایی را به ما خواهد داد.
حتی میتوانیم این مثال را به شکل زیر نیز پیاده سازی کنیم:
function loggingIdentity<T>(arg: Array<T>): Array<T> {    console.log(arg.length);  // Array has a .length, so no more error    return arg;
}
پیاده سازی بالا خیلی شبیه به پیاده سازی در سایر زبان‌ها هم میباشد.

Generic Types
در این قسمت ما به دنبال یافتن نوع خود توابع بوده و سعی خواهیم کرد اینترفیس‌های جنریک را هم پیاده سازی کنیم. نوع توابع جنریک هم بمانند توابع غیر جنریک میباشند؛ به طوری که می‌توان لیستی از type parameters هایی را که در حالت function declarations موجود هستند، در ابتدا بنویسیم.
function identity<T>(arg: T): T {    return arg;
}

let myIdentity: <T>(arg: T) => T = identity;
حتی می‌توانیم نام متفاوتی را هم برای type parameter در نظر بگیرم:
function identity<T>(arg: T): T {    return arg;
}

let myIdentity: <U>(arg: U) => U = identity;
یا حتی می‌توانیم به مانند امضای یک object literal هم کد بالا را بازنویسی کنیم:
function identity<T>(arg: T): T {    return arg;
}

let myIdentity: {<T>(arg: T): T} = identity;
حال میتوانیم این object literal را به یک اینترفیس منتقل کنیم:
interface GenericIdentityFn {    <T>(arg: T): T;
}

function identity<T>(arg: T): T {    return arg;
}

let myIdentity: GenericIdentityFn = identity;
کد بالا خوانایی بالاتری را نسبت به حالت قبل دارد و با تعریف یک اینترفیس به نام GenericIdentityFn و انتقال object literal به داخل آن، میتوانیم از نام اینترفیس به جای استفاده مستقیم از object literal، بهره ببریم.
حتی میتوانیم type parameter تابع جنریک خود را هم به اینترفیس منتقل کنیم. 
interface GenericIdentityFn<T> {    (arg: T): T;
}

function identity<T>(arg: T): T {    return arg;
}

let myIdentity: GenericIdentityFn<number> = identity;
باید توجه داشت که پیاده سازی ما کمی متفاوت‌تر از قبل شده است.الان type parameter ما برای کل اعضای اینترفیس قابل رویت میباشد.فهم این مورد که چه زمانی type parameter را در امضای نامیدن داخل اینترفیس یا بر روی خود اینترفیس استفاده کنیم، خود میتوانید برای شرح اینکه کدام وجه‌های یک نوع داده جنریک هستند، مفید باشد.
نکته : امکان تعریف enum‌ها و namespace‌های جنریک وجود ندارد.
 
Generic Classes
تعریف کلاس‌های جنریک هم به مانند اینترفیس‌های جنریک میباشد. به مثال زیر توجه کنید:
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
در کد بالا، استفاده‌ای واقعی از کلاس GenericNumber قابل مشاهده است. شاید متوجه شده باشید که هیچ محدودیتی برای استفاده‌ی نوع‌ها برای مثال تنها از نوع number در آن نیست و میتوانید از نوع string هم به شکل زیر استفاده کنید:
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };

alert(stringNumeric.add(stringNumeric.zeroValue, "test"));
نکته : برای اعضای استاتیک کلاس نمیتوانید از type parameter کلاس استفاده کنید.
 
Generic Constraints
اگر مثال اخیر را به یاد داشته باشید، شاید بعضی اوقات لازم باشد که یک تابع جنریک را تعریف کنیم تا تنها با مجموعه‌ای از نوع داده‌ها کار کند که اتفاقا از امکانات این مجموعه، آگاهی داریم. در همان مثال loggingIdentity، ما نیاز داشتیم تا به خصوصیت length آرگومان arg دسترسی داشته باشیم و کامپایلر در همان ابتدا، به دلیل اینکه همه نوع داده‌ها از این خصوصیت برخوردار نیستند، خطایی را به ما نشان میدهد.
در ادامه تابعی را پیاده سازی میکنیم که جوابگوی تمام نوع داده‌ها بوده، به شرطی که حداقل خصوصیت length را داشته باشند. لذا باید نیاز خود را در قالب یک محدودیت بر آنچه که T میتواند انجام دهد، فهرست کنیم.
interface Lengthwise {    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {    console.log(arg.length);  // Now we know it has a .length property, so no more error    return arg;
}
در کد بالا برای توصیف محدودیت خود از یک اینترفیس به نام Lengthwise استفاده کرده‌ایم که فقط یه خصوصیت length را دارد و با استفاده از آن و کلمه‌ی کلیدی extends، محدودیت خود را اعمال کرده ایم.
استفاده از تابع بالا:
loggingIdentity(3);  // Error, number doesn't have a .length property
چون تابع جنریک ما الان محدود میباشد و با تمام نوع داده‌ها کار نخواهد کرد، با خطای بالا روبرو خواهیم شد.
loggingIdentity({length: 10, value: 3});
در عوض مثال بالا، محدودیت ما را به همراه دارد (داشتن خصوصیت length) و بدون هیچ خطایی جواب خواهیم گرفت.

استفاده از Type Parameter‌ها در تعریف محدودیت
در برخی از سناریو‌ها شاید نیاز باشد که یکی از type parameter‌ها توسط دیگری محدود شده باشد. به مثال زیر توجه کنید:
function find<T, U extends Findable<T>>(n: T, s: U) {   // errors because type parameter used in constraint  // ...
}
find (giraffe, myAnimals);
همانطور که مشخص است، کامپایلر ما را با نشان دادن خطایی متوقف خواهد کرد. چون اجازه‌ی استفاده از type parameter را در اعمال محدودیت، نداریم.در عوض میشود به شکل زیر عمل کرد:
function find<T>(n: T, s: Findable<T>) {  // ...
}
find(giraffe, myAnimals);
این بار آرگومان s ما باید از نوع <Findable<T باشد که باز هم توانسته‌ایم محدودیت خود را توسط یک type parameter بر آن یکی اعمال کنیم.
نکته : دو پیاده سازی بالا اصلا یکسان نیستند؛ نوع بازگشی در تابع اول میبایستی از نوع U می‌بود، ولی در پیاده سازی دوم اینگونه نیست.(در صورت نبودن خطا)
 
استفاده از کلاس‌ها در جنریک‌ها
زمانی که قصد دارید با استفاده از جنریک‌ها، factory‌ها را پیاده سازی کنید، باید با استفاده از سازنده‌ی کلاس‌ها، به آنها اشاره کنید. به مثال زیر توجه کنید:
function create<T>(c: {new(): T; }): T {    return new c();
}
تابع بالا به عنوان یک object factory می‌تواند مورد استفاده قرار بگیرد و نکته آن در تعریف نوع آرگومان c میباشد که باز هم به صورت object literal معرفی شده است. اگر در قسمت‌های بالا به یاد داشته باشید، می‌توان این مورد را هم داخل یک اینترفیس گنجاند.
به عنوان یک مثال پیشرفته‌تر هم میتوان به استفاده از prototype property برای استنتاج type parameter‌ها و تحمیل کردن ارتباط بین تابع سازنده و وهله کلاس‌ها، اشاره کرد. به مثال زیر توجه کنید:
class BeeKeeper {    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

function findKeeper<A extends Animal, K> (a: {new(): A;    prototype: {keeper: K}}): K {    return a.prototype.keeper;
}
در کد بالا از دو کلاس BeeKeeper و ZooKeeper برای نوع بازگشتی متد‌های موجود در کلاس‌هایBee و Lion استفاده شده‌است.کلاس Animal به عنوان کلاس پایه دو کلاس Bee و Lion که یک خصوصیت numLegs دارد، تعریف شده‌است.از تابع جنریک findKeeper برای مشخص کردن نگهبان مرتبط با Animal ای که به عنوان type parameter توسط A مشخص میشود، استفاده می‌گردد. محدودیتی که بر روی A اعمال شده است نشان دهنده‌ی این است که نوع داده‌ی مورد نظر باید حتما یک Animal باشد و همچنین با اعمال محدودیتی که در قالب object literal مشخص است، تعیین شده است که نوع مورد نظر باید یک کلاس باشد و در نهایت با استفاده ازprototype مشخص کرده‌ایم که متدی به نام Keeper آن کلاس، باید نوع برگشتی از نوع K را که به عنوان type parameter مطرح شده‌ی در امضای تابع است، دارا باشد. K نشان دهنده نوع داده بازگشتی این تابع جنریک نیز میباشد.
استفاده از تابع بالا:
findKeeper(Lion).nametag;  // typechecks!
بله همانطور که مشخص است، type parameter‌های مورد نظر به اصطلاح infer شده‌اند و خصوصیت nametag نشان از این دارد که ZooKeeper به صورت خودکار به عنوان نوع داده K تشخیص داده شده است.

‫مبانی TypeScript؛ پیمایشگرها

$
0
0
همانطور که پیشتر در این مطلبنیز توضیح داده شد symbol یک primitive data type مانند number و string است. حین کار کردن با سمبل‌ها باید این نکات را در نظر بگیرید:
  • منحصربفرد و immutable (غیرقابل تغییر) هستند. 
  • همانند رشته‌ها می‌توان از آن‌ها به عنوان کلیدی برای پراپرتی‌ها یک شیء استفاده کرد.
بنابراین از سمبل‌ها بیشتر جهت توکن‌های منحصر به فرد برای استفاده و به عنوان کلید در پراپرتی‌های اشیاء استفاده خواهد شد. در اینجا می‌توانید لیستی از سمبل‌های رایج را مشاهده کنید.

Iterators and Generators 
یک شیء زمانی قابلیت پیمایش را خواهد داشت که یک پیاده‌سازی از Symbol.iterator را داشته باشد:
var myIterable = {}
myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};
در اینحالت می‌توان شیء myIterable را توسط حلقه‌ی for..of پیمایش کرد:
for (let item of myIterable) {
  console.log(item);
}
در واقع کار حلقه‌ی for..of حرکت درون یک قابل پیمایش (iterable) است و در هر بار اجرای حلقه پراپرتی Symbol.iterator شیء را فراخوانی خواهد کرد.

تفاوت حلقه‌ی for..of با حلقه‌ی for..in
هر دوی این حلقه‌ها یک لیست را پیمایش می‌کنند. با این تفاوت که حلقه‌ی for..in کلید هر آیتم را بر می‌گرداند اما for..of مقدار هر آیتم را بر می‌گرداند:
let list = [4, 5, 6];

for (let i in list) {
   console.log(i); // "0", "1", "2",
}

for (let i of list) {
   console.log(i); // "4", "5", "6"
نکته‌ی دیگر این است که for..in برای هر شیء‌ی قابل استفاده است یعنی از آن جهت پیمایش پراپرتی‌های یک شیء استفاده خواهد شد. اما for..of برای اشیایی که قابلیت پیمایش را داشته باشند استفاده خواهد شد؛ همانند Map و Set که پراپرتی Symbol.iterator را پیاده‌سازی کرده‌اند.
به عنوان مثال کد زیر را در نظر بگیرید:
let numbers = [1, 2, 3];
for (let num of numbers) {
    console.log(num);
}
اگر target را به ES5 و یا ES6 تنظیم کرده باشید، کد تولید شده‌ی یک حلقه‌ی for را به اینصورت برایتان تولید خواهد کرد:
var numbers = [1, 2, 3];
for (var _i = 0, numbers_1 = numbers; _i < numbers_1.length; _i++) {
    var num = numbers_1[_i];
    console.log(num);
}
//# sourceMappingURL=app.js.map

‫مبانی TypeScript؛ Decorators

$
0
0
Decorators  یا تزئین کننده‌ها و ReflectDecorators، یکی از پیشنهادهای نگارش بعدی جاوا اسکریپت هستند (ECMAScript 2016) که هم اکنون قابلیت استفاده‌ی از آن‌ها در TypeScript وجود دارد.
جهت افزودن قابلیت‌های meta-programming به زبان‌های جاوا اسکریپت و TypeScript و همچنین تعریف annotations بر روی کلاس‌ها و اعضای کلاس، می‌توان از Decorators استفاده کرد. یک decorator، تعریف ویژه‌ای است که می‌تواند به تعریف یک کلاس، متد، خاصیت و یا پارامتر «متصل» شود و به صورت expression@ تعریف می‌گردد. این expression باید قابلیت فراخوانی به صورت یک متد را داشته باشد که در زمان اجرا فراخوانی خواهد شد. از Decorators در طراحی AngularJS 2 زیاد استفاده شده‌است.


نحوه‌ی فعال سازی Decorators با ES 5

این قابلیت فعلا در مرحله‌ی آزمایش به سر می‌برد؛ بنابراین برای فعال سازی آن نیاز است پارامترهای experimentalDecorators و emitDecoratorMetadata را به کامپایلر خط فرمان tsc و یا به خواص کامپایلر در فایل tsconfig.json اضافه کنید:
Command Line:
tsc --target ES5 --experimentalDecorators --emitDecoratorMetadata

tsconfig.json:
{
    "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
    }
}


بررسی انواع Decorators

در اینجا یک مثال ساده از decoratorها را مشاهده می‌کنید:
 // A simple decorator
@decoratorExpression
class MyClass { }
و ساده‌ترین فرم پیاده سازی این decoratorExpression به صورت زیر است:
function decoratorExpression(target) {
   // Add a property on target
   target.annotated = true;
}
همانطور که ملاحظه می‌کنید، یک decorator در اصل یک function است که دسترسی به target ایی را که قرار است تزئین شود، میسر می‌کند. این قابلیت بسیار شبیه است به مفهومی به نام attributes و annotations در زبان #C.
در ادامه انواع و اقسام decoratorهای ممکن را با مثال‌هایی بررسی خواهیم کرد.


Class Decorator

یک class Decorator، پیش از تعریف یک کلاس اضافه می‌شود و هدف آن، اعمال تعاریفی به سازنده‌ی کلاس است و از آن می‌توان جهت تحت نظر قرار دادن تعاریف کلاس و یا تغییر یا حتی تعویض کلی تعاریف آن استفاده کرد. یک class Decorator را نمی‌توان در سایر فایل‌های تعاریف، مانند d.ts. قرار داد.
تنها آرگومانی که به یک class Decorator ارسال می‌شود، سازنده‌ی کلاسی است که به آن اعمال شده‌است. اگر متد class Decorator مقداری را برگرداند، سبب تعویض و جایگزینی تعاریف کلاس با مقدار باگشت داده شده، می‌شود.
در ادامه دو مثال را از class Decoratorها مشاهده می‌کنید:

مثال بدون پارامتر
 function ClassDecorator(
target: Function // The class the decorator is declared on
) {
    console.log("ClassDecorator called on: ", target);
}

@ClassDecorator
class ClassDecoratorExample {
}


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

مثال با پارامتر
 function ClassDecoratorParams(param1: number, param2: string) {
return function(
  target: Function // The class the decorator is declared on
  ) {
   console.log("ClassDecoratorParams(" + param1 + ", '" + param2 + "') called on: ", target);
}
}

@ClassDecoratorParams(1, "a")
@ClassDecoratorParams(2, "b")
class ClassDecoratorParamsExample {
}
با خروجی
 ClassDecoratorParams(2, 'b') called on: function ClassDecoratorParamsExample() {
}
ClassDecoratorParams(1, 'a') called on: function ClassDecoratorParamsExample() {
}
همانطور که در این مثال مشاهده می‌کنید، چندین decorator را نیز می‌توان به تعاریف یک کلاس اعمال کرد که به آن Decorator Composition نیز می‌گویند.


Property Decorator

یک Property Decorator دقیقا پیش از تعریف یک خاصیت اضافه می‌شود و نباید در سایر فایل‌های تعاریف جانبی قرار گیرد. زمانیکه متد expression آن در runtime فراخوانی می‌شود، دو پارامتر را دریافت خواهد کرد:
الف) برای static member ها، متد سازنده‌ی کلاس و برای  instance memberها، prototype کلاس را دریافت می‌کند.
ب) نام عضو.

اگر متد expression تعریف شده، مقداری را برگرداند، به عنوان Property Descriptor آن عضو بکارگرفته می‌شود.

در مثال ذیل که متد PropertyDecorator به خاصیت name اعمال شده‌است، دو پارامتر ارسالی به آن‌را بهتر می‌توان مشاهده کرد:
 function PropertyDecorator(
target: Object, // The prototype of the class
propertyKey: string | symbol // The name of the property
) {
   console.log("PropertyDecorator called on: ", target, propertyKey);
}

class PropertyDecoratorExample {
@PropertyDecorator
name: string;
}
با خروجی
 PropertyDecorator called on: PropertyDecoratorExample {} name


Method Decorator

یک Method Decorator باید درست پیش از تعریف یک متد، قرارگیرد و نباید در سایر کلاس‌ها تعاریف نوع‌های جانبی افزوده شود. هدف از آن می‌تواند بررسی، مشاهده و تغییر رفتار یک متد باشد. به متد expression آن، سه پارامتر ارسال می‌شوند:
الف) برای static member ها، متد سازنده‌ی کلاس و برای  instance memberها، prototype کلاس را دریافت می‌کند.
ب) نام عضو
ج) Property Descriptor عضو

اگر متد تعریف شده، خروجی را برگرداند به عنوان Property Descriptor آن متد استفاده خواهد شد.

در مثال ذیل، متد تزئین کننده‌ای به نام MethodDecorator تعریف شده‌است که سه پارامتر یاد شده را در زمان اجرا دریافت می‌کند:
 function MethodDecorator(
target: Object, // The prototype of the class
propertyKey: string, // The name of the method
descriptor: TypedPropertyDescriptor<any>
) {
console.log("MethodDecorator called on: ", target, propertyKey, descriptor);
}

class MethodDecoratorExample {
   @MethodDecorator
   method() {
   }
}
با خروجی
 MethodDecorator called on: MethodDecoratorExample { method: [Function] } method { value: [Function],
writable: true,
enumerable: true,
configurable: true }

و مثالی جهت محدود ساختن آن به یک سری متد خاص که یک پارامتر عددی را دریافت می‌کنند و یک خروجی عددی نیز دارند:
 function TypeRestrictedMethodDecorator(
target: Object, // The prototype of the class
propertyKey: string, // The name of the method
descriptor: TypedPropertyDescriptor<(num: number) => number>
) {
   console.log("TypeRestrictedMethodDecorator called on: ", target, propertyKey, descriptor);
}

class TypeRestrictedMethodDecoratorExample {
   @TypeRestrictedMethodDecorator
   method(num: number): number {
      return 0;
   }
}
با خروجی
 TypeRestrictedMethodDecorator called on: TypeRestrictedMethodDecoratorExample { method: [Function] } method { value: [Function],
writable: true,
enumerable: true,
configurable: true }

و مثالی از تزئین کننده‌های متدهای استاتیک یک کلاس که در این حالت، پارامتر target از نوع متد سازنده‌ی کلاس خواهد بود و نه prototype آن:
 function StaticMethodDecorator(
target: Function, // the function itself and not the prototype
propertyKey: string | symbol, // The name of the static method
descriptor: TypedPropertyDescriptor<any>
) {
   console.log("StaticMethodDecorator called on: ", target, propertyKey, descriptor);
}

class StaticMethodDecoratorExample {
   @StaticMethodDecorator
   static staticMethod() {
   }
}
با خروجی
 StaticMethodDecorator called on: function StaticMethodDecoratorExample() {
} staticMethod { value: [Function],
writable: true,
enumerable: true,
configurable: true }


Parameter Decorator

یک Parameter Decorator باید درست پیش از تعریف یک آرگومان متد، قرارگیرد و نباید در سایر کلاس‌ها تعاریف نوع‌های جانبی افزوده شود. به متد expression آن سه پارامتر ارسال می‌شوند:
الف) برای static member ها، متد سازنده‌ی کلاس و برای  instance memberها، prototype کلاس را دریافت می‌کند.
ب) نام عضو
ج) شماره ایندکس پارامتر مدنظر در متد

از خروجی متد تزئین کننده در اینجا صرفنظر می‌شود.

در ادامه مثالی را از نحوه‌ی تعریف یک تزئین کننده‌ی پارامترها را با سه آرگومان ویژه‌ی آن، مشاهده می‌کنید:
 function ParameterDecorator(
target: Function, // The prototype of the class
propertyKey: string | symbol, // The name of the method
parameterIndex: number // The index of parameter in the list of the function's parameters
) {
   console.log("ParameterDecorator called on: ", target, propertyKey, parameterIndex);
}

class ParameterDecoratorExample {
   method(@ParameterDecorator param1: string, @ParameterDecorator param2: number) {
   }
}
با خروجی
 ParameterDecorator called on: ParameterDecoratorExample { method: [Function] } method 1
ParameterDecorator called on: ParameterDecoratorExample { method: [Function] } method 0

یک سری مثال تکمیلی

‫مبانی TypeScript؛ Mixins

$
0
0
یکی از راه‌های محبوب دیگر برای ساخت کلاس‌ها با استفاده از اجزایی با قابلیت استفاده مجدد، ساخت آنها با ترکیب partial class‌های ساده، می‌باشد. mixins در زبان‌های برنامه نویسی مانند ++C و Lisp، کلاس‌هایی هستند که یکسری توابع را از طریق ارث بری در اختیار SubClass‌ها قرار می‌دهند.شاید با ایده استفاده از mixins، یا traits در زبانی مانند Scala و الگوی mixins که بین جامعه جاوااسکریپت هم تا حدودی محبوب شده است، آشنا هستید.
در جاوااسکریپت، ارث بری کردن از mixins، شبیه به افزایش عملکرد خود از طریق extension‌ها می‌باشد؛ چرا که این mixin‌ها این امکان را در اختیار اشیاء می‌گذارند که با کمترین پیچیدگی، بتوانند عملکردی (functionality) را از آنها به امانت بگیرند. Mixins در جاوااسکریپت به عنوان الگویی است که با object prototype‌ها کار می‌کند و امکان ارث بری چندگانه را هم به ما خواهد داد.

یک مثال از استفاده از Mixins در جاوااسکریپت
var myMixins = {   moveUp: function(){    console.log( "move up" );  },   moveDown: function(){    console.log( "move down" );  },   stop: function(){    console.log( "stop! in the name of love!" );  } 
};
کد بالا نشان دهنده یک object literal با 3 متد می‌باشد و نشان دهنده mixins ما نیز هست.
برای توسعه prototype مربوط به اشیاء، به منظور استفاده از رفتارهای تعریف شده در mixins بالا، می‌توان از هلپرهایی به مانند ()extend._ موجود در Underscore.js استفاده کرد.
// A skeleton carAnimator constructor
function CarAnimator(){  this.moveLeft = function(){    console.log( "move left" );  };
} 
// A skeleton personAnimator constructor
function PersonAnimator(){  this.moveRandomly = function(){ /*..*/ };
} 
// Extend both constructors with our Mixin
_.extend( CarAnimator.prototype, myMixins );
_.extend( PersonAnimator.prototype, myMixins ); 
// Create a new instance of carAnimator
var myAnimator = new CarAnimator();
myAnimator.moveLeft();
myAnimator.moveDown();
myAnimator.stop(); 
// Outputs:
// move left
// move down
// stop! in the name of love!
در کد بالا دو شیء جدید را تعریف کرده‌ایم که برای prototype هر کدام از آنها هم یک متد در نظر گرفته‌ایم. با استفاده از هلپر مذکور توانستیم عملیات مربوط به myMixins را به prototype هرکدام از اشیاء تعریف شده‌ی در بالا نسبت دهیم. استفاده‌ی از متدهای stop یا moveDown بر روی نمونه‌ای از CarAnimator نشان دهنده‌ی این ادعا می‌باشد.

پیاده سازی این الگو در TypeScript
// Disposable Mixin
class Disposable {    isDisposed: boolean;    dispose() {        this.isDisposed = true;    }

}

// Activatable Mixin
class Activatable {
    isActive: boolean;    activate() {        this.isActive = true;    }    deactivate() {        this.isActive = false;    }
}
از دو کلاس بالا به عنوان mixin‌های خودمان استفاده خواهیم کردم. همانطور که مشخص است هر کدام از آنها بر روی فعالیت خاصی متمرکز شده‌اند.
class SmartObject implements Disposable, Activatable {    constructor() {        setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);    }    interact() {        this.activate();    }    // Disposable    isDisposed: boolean = false;    dispose: () => void;    // Activatable    isActive: boolean = false;    activate: () => void;    deactivate: () => void;
}
گام بعدی تعریف کلاسی برای ترکیب شدن با دو mixins تعریف شده، میباشد. اگر توجه کنید در کد بالا به جای استفاده از کلمه کلیدی extend، از implements استفاده شده است. در واقع mixin‌ها شبیه بهاینترفیس هایی با امکان پیاده سازی درون خود، هستند. کلاس‌های استفاده کننده فقط باید تعریف این متدها و خصوصیت‌ها را به منظور تشخیص این خصوصیات در زمان اجرا، در خود تعریف کرده باشند.
applyMixins(SmartObject, [Disposable, Activatable]);
در نهایت با استفاده از هلپر applyMixins که در ادامه کد آن را مشاهده خواهید کرد، عملیات میکس را انجام دادیم.
در مثال زیر یک نمونه از کلاس SmartObject تهیه کرده‌ایم که به راحتی به دلیل پیاده سازی Activatable، دسترسی به استفاده از متد activate را داشته و آن را درون متد interact خود فراخوانی کرده است. اجرای خط دوم کد زیر، مبنی بر درستی ادعای ما میباشد.
let smartObj = new SmartObject();
setTimeout(() => smartObj.interact(), 1000);

پیاده سازی هلپر applyMixins
function applyMixins(derivedCtor: any, baseCtors: any[]) {    baseCtors.forEach(baseCtor => {        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {            derivedCtor.prototype[name] = baseCtor.prototype[name];        });    });
}
کد بالا تمام خصوصیات موجود در baseCtors‌ها را که همان mixin‌های ما هستند، واکشی کرده و در خصوصیات موجود در کلاس پیاده سازی کننده‌ی آن Mixin، پر میکند. 

‫الگوی مشاهده‌گر Observer Pattern

$
0
0
الگوی مشاهده‌گر یکی از محبوبترین و معروفترین الگوهای برنامه نویسی است که پیاده سازی آن در بسیاری از زبان‌ها رواج یافته است. برای نمونه پیاده سازی این الگو را می‌توانید در بسیاری از کتابخانه‌ها (به خصوص GUI) مانند این مطالب (+++ ) مشاهده کنید. برای اینکه بتوانیم این الگو را خودمان برای اشیاء برنامه خودمان پیاده کنیم، بهتر است که بیشتر با خود این الگو آشنا شویم. برای شروع بهتر است که با یک مثال به تعریف این الگو بپردازیم. مثال زیر نقل قولی از یکی از مطالباین سایت است که به خوبی کارکرد این الگو را برای شما نشان میدهد.

یک لامپ و سوئیچ برق را درنظر بگیرید. زمانیکه لامپ مشاهده می‌کند سوئیچ برق در حالت روشن قرار گرفته‌است، روشن خواهد شد و برعکس. در اینجا به سوئیچ، subject و به لامپ، observer گفته می‌شود. هر زمان که حالت سوئیچ تغییر می‌کند، از طریق یک callback، وضعیت خود را به observer اعلام خواهد کرد. علت استفاده از callbackها، ارائه راه‌حل‌های عمومی است تا بتواند با انواع و اقسام اشیاء کار کند. به این ترتیب هر بار که شیء observer از نوع متفاوتی تعریف می‌شود (مثلا بجای لامپ یک خودرو قرار گیرد)، نیازی نخواهد بود تا subject را تغییر داد.

عموما به شیءایی که قرار است وضعیت را مشاهده یا رصد کند، Observer گفته می‌شود و به شیءایی که قرار است وضعیت آن رصد شود Observable یا Subject گفته می‌شود.
 بد نیست بدانید این الگو یکی از کلیدی‌ترین بخش‌های معماری لایه بندی MVC نیز می‌باشد.
 همچنین این نکته حائز اهمیت است که این الگو ممکن است باعث نشتی حافظههم شود و به این مشکل Lapsed Listener Problemمی‌گویند. یعنی یک listener وجود دارد که تاریخ آن منقضی شده، ولی هنوز در حافظه جا خوش کرده‌است. این مشکل برای زبان‌های شیءگرایی که با سیستمی مشابه GC پیاده سازی می‌شوند، رخ میدهد. برای جلوگیری از این حالت، برنامه نویس باید  این مشکل را با رجیستر کردن‌ها و عدم رجیستر یک شنوده، در مواقع لزوم حل کند. در غیر این صورت این شنونده بی جهت، یک ارتباط را زنده نگه داشته و حافظه‌ی منبع را به هدر میدهد.

مثال:ما یک کلید داریم که سه کلاس  RedLED،GreenLED و BlueLED قرار است آن را مشاهده و وضعیت کلید را رصد کنند.
برای پیاده سازی این الگو، ابتدا یک کلاس انتزاعی را با نام Observer که دارای متدی به نام Update است، ایجاد می‌کنیم. متغیر از نوع کلاس Observable را بعدا ایجاد می‌کنیم:
public abstract class Observer
    {
        protected Observable Observable;
        public abstract void Update();
    }
سپس یک کلاس پدر را به نام Observable می‌سازیم تا آن را به شیء سوئیچ نسبت دهیم:
 public  class Observable
    {
        private readonly List<Observer> _observers = new List<Observer>();

        public void Attach(Observer observer)
        {
            _observers.Add(observer);
        }
        public void Dettach(Observer observer)
        {
            _observers.Remove(observer);
        }

        public void NotifyAllObservers()
        {
            foreach (var observer in _observers)
            {
                observer.Update();
            }
        }
    }
در کلاس بالا یک لیست از نوع Observer‌ها داریم که در آن، کلید با تغییر وضعیت خود، لیست رصد کنندگانش را مطلع می‌سازد و دیگر چراغ‌های LED نیازی نیست تا مرتب وضعیت کلید را چک کنند. متدهای attach و Detach در واقع همان رجیستر‌ها هستند که باید مدیریت خوبی روی آن‌ها داشته باشید تا نشتی حافظه پیش نیاید. در نهایت متد NotifyAllObservers هم متدی است که با مرور لیست رصدکنندگانش، رویداد Update آن‌ها را صدا میزند تا تغییر وضعیت کلید به آن‌ها گزارش داده شود.

حال کلاس Switch را با ارث بری از کلاس Observable می‌نویسیم:
 public  class Switch:Observable
    {
        private bool _state;

        public bool ChangeState
        {
            set
            {
                _state = value;
                NotifyAllObservers();
            }
            get { return _state; }
        }
    }
در کلاس بالا هرجایی که وضعیت کلید تغییر می‌یابد، متد NotifyAllObservers صدا زده میشود.
برای هر سه چراغ، رنگی هم داریم:
   public class RedLED:Observer
    {
        private bool _on = false;
        public override void Update()
        {
            _on = !_on;
            Console.WriteLine($"Red LED is {((_on) ? "On" : "Off")}");
        }
    }

  public class GreenLED:Observer
    {
        private bool _on = false;
        public override void Update()
        {
            _on = !_on;
            Console.WriteLine($"Green LED is {((_on) ? "On" : "Off")}");
        }
    }

  public  class BlueLED:Observer
    {
        private bool _on = false;
        public override void Update()
        {
            _on = !_on;
            Console.WriteLine($"Blue LED is {((_on) ? "On" : "Off")}");
        }
    }
سپس در Main اینگونه می‌نویسیم:
var greenLed=new GreenLED();
            var redLed=new RedLED();
            var blueLed=new BlueLED();

            var switchKey=new Switch();
            switchKey.Attach(greenLed);
            switchKey.Attach(redLed);
            switchKey.Attach(blueLed);

            switchKey.ChangeState = true;
            switchKey.ChangeState = false;
به طور خلاصه هر سه چراغ به شیء کلید attach شده و با هر بار عوض شدن وضعیت کلید، متدهای Update هر سه چراغ صدا زده خواهند شد. نتیجه‌ی کد بالا به شکل زیر در کنسول نمایش می‌یابد:
Green LED is On
Red LED is On
Blue LED is On
Green LED is Off
Red LED is Off
Blue LED is Off

‫مبانی TypeScript؛ تنظیمات TypeScript در ویژوال استودیو

$
0
0
تا اینجا «نحوه‌ی نصب و راه اندازی TypeScript را در VSCode» به همراه «تنظیمات کامپایلر TypeScript» و «دریافت فایل‌های d.ts. را توسط بسته‌های NodeJS» بررسی کردیم. در ادامه قصد داریم این تنظیمات را به نگارش کامل VS.NET نیز اعمال کنیم.


به روز رسانی وابستگی‌های VS.NET

برای دریافت آخرین نگارش TypeScript نیاز است افزونه‌های آن‌را از سایت رسمی زبان TypeScript دریافت و نصب کرد:


به علاوه نصب افزونه‌ی Web Essentialsنیز جهت تکمیل امکانات کار با TypeScript مانند امکان مشاهده‌ی خروجی جاوا اسکریپت تولیدی، در حین کار با فایل TypeScript فعلی توصیه می‌شود. همچنین TSLintرا نیز نصب می‌کند.


افزودن فایل تنظیمات tslint

افزونه‌ی Web Essentials که Web Analyzerنیز اکنون جزئی از آن است، به همراه TSLintهم هست که کار آن ارائه راهنماهایی جهت تولید کدهای با کیفیت TypeScript است. گزینه‌های آن‌را در منوی Tools -> Options می‌توانید مشاهده کنید:


برای بازنویسی تنظیمات آن (در صورت نیاز) فایل جدیدی را به نام tslint.json به ریشه‌ی پروژه (کنار فایل web.config) اضافه کنید. فایل پیش فرض آن چنین شکلی را دارد:
settings-defaults/tslint.json
و یک نمونه‌ی اصلاح شده‌ی آن به صورت ذیل است که می‌تواند به ریشه‌ی پروژه کپی شود:
tslint.json


تنظیمات کامپایلر TypeScript در VS.NET

هرچند قالب افزودن یک پروژه‌ی جدید TypeScript نیز به همراه نصب بسته‌های TypeScript به لیست پروژه‌های موجود اضافه می‌شود، اما عموما نیاز است تا فایل‌های ts. را به یک پروژه‌ی وب موجود اضافه کرد. بنابراین، یک پوشه‌ی جدید را به برای مثال به نام TypeScript ایجاد کرده و بر روی آن کلیک راست کنید. سپس گزینه‌ی Add->new item را انتخاب کرده و در اینجا TypeScript را جستجو کنید:


پس از اضافه شدن اولین فایل ts. به پروژه، دیالوگ زیر نیز ظاهر خواهد شد:


در اینجا جستجوی فایل‌های d.ts. را پیشنهاد می‌دهد. فعلا بر روی No کلیک کنید. این‌کار را در ادامه انجام خواهیم داد.
پس از افزودن اولین فایل ts. به پروژه، اگر به خواص پروژه‌ی جاری مراجعه کنید، برگه‌ی جدید تنظیمات کامپایلر TypeScript را مشاهده خواهید کرد:


با این تنظیمات در مطلب «تنظیمات کامپایلر TypeScript» پیشتر آشنا شده‌اید. برای مثال فرمت خروجی جاوا اسکریپت آن ES 5 باشد و یا در اینجا نوع‌های any که به صورت صریح any تعریف نشده‌اند، ممنوع شده‌است (تیک پیش فرض آن‌را بردارید). نوع ماژول‌های تولیدی نیز به commonjs تنظیم شده‌است.
همچنین در اینجا می‌توانید گزینه‌ی redirect JavaScript output to directory را هم مثلا به پوشه‌ی Scripts واقع در ریشه‌ی پروژه تنظیم کنید تا فایل‌های js. نهایی را در آن‌جا قرار دهد.

پس از این تنظیمات اولیه، به منوی tools->options مراجعه کرده و گزینه‌ی کامپایل فایل‌های ts. ایی را که به solution explorer اضافه نشده‌اند، نیز فعال کنید:


اعمال این تنظیمات نیاز به یکبار بستن و گشودن مجدد پروژه را دارد.


فعال سازی کامپایل خودکار فایل‌های ts. پس از ذخیره‌ی آن‌ها

پس از اعمال تغییرات فوق، اگر فایل ts. ایی را تغییر داده و ذخیره کردید و بلافاصله خروجی js. آن‌را مشاهده نکردید (این فایل‌ها در پوشه‌ی TypeScriptOutDir تنظیمات ذیل ذخیره می‌شوند و برای مشاهده‌ی آن‌ها باید گزینه‌ی show all files را در solution explorer فعال کنید)، فایل csproj پروژه‌ی جاری را در یک ادیتور متنی باز کرده و مداخل تنظیمات تنظیم شده‌ی در قسمت قبل را پیدا کنید. در اینجا نیاز است مدخل جدید TypeScriptCompileOnSaveEnabled را به صورت دستی اضافه کنید:
<PropertyGroup Condition="'$(Configuration)' == 'Debug'"><TypeScriptModuleKind>commonjs</TypeScriptModuleKind><TypeScriptCompileOnSaveEnabled>True</TypeScriptCompileOnSaveEnabled><TypeScriptOutDir>.\Scripts</TypeScriptOutDir><TypeScriptNoImplicitAny>True</TypeScriptNoImplicitAny>  <TypeScriptTarget>ES5</TypeScriptTarget>  <TypeScriptRemoveComments>false</TypeScriptRemoveComments><TypeScriptOutFile></TypeScriptOutFile><TypeScriptGeneratesDeclarations>false</TypeScriptGeneratesDeclarations><TypeScriptSourceMap>true</TypeScriptSourceMap><TypeScriptMapRoot></TypeScriptMapRoot><TypeScriptSourceRoot></TypeScriptSourceRoot><TypeScriptNoEmitOnError>true</TypeScriptNoEmitOnError>  </PropertyGroup>
پس از این تغییرات بدیهی است یکبار باید پروژه را بسته و مجددا بارگذاری نمائید.


رفع مشکل عدم کامپایل پروژه

زمانیکه افزونه‌های TypeScript را نصب کنید و تنظیمات فوق را اعمال نمائید، در دو حالت ذخیره‌ی یک فایل ts و یا کامپایل کل پروژه، فایل‌های js تولید خواهند شد. اما ممکن است نگارش نصب شده‌ی بر روی سیستم شما ناقص باشد و چنین خطایی را در حین کامپایل پروژه دریافت کنید:
 Your project file uses a different version of the TypeScript compiler and tools than is currently installed on this machine.  
No compiler was found at C:\Program Files (x86)\Microsoft SDKs\TypeScript\1.8\tsc.exe.
You may be able to fix this problem by changing the <TypeScriptToolsVersion> element in your project file.
اگر این خطا را دریافت کردید، سریع‌ترین راه رفع آن به صورت زیر است:
الف) ابتدا به تمام مسیرهای ذیل (در صورت وجود) مراجعه کرده و پوشه‌ی TypeScript را تغییر نام دهید (یا کلا آن‌را حذف کنید):
 C:\Program Files (x86)\Microsoft SDKs
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\
ب) سپس نصاب افزونه‌ی TypeScript را مجددا اجرا کنید. اینبار گزینه‌ی repair ظاهر می‌شود. با ترمیم صورت گرفته، مشکل فوق برطرف خواهد شد. این گزینه‌ی repair را در کنترل‌پنل و قسمت add/remove programs هم می‌توانید پیدا کنید (اگر فایل نصاب افزونه را حذف کرده‌اید).


اصلاح شماره نگارش کامپایلر TypeScript خط فرمان ویژوال استودیو

در فایل C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\VsDevCmd.bat که مربوط به خط فرمان VS.NET است، شماره نگارش TypeScript به 1.5 تنظیم شده‌است که نیاز به اصلاح دستی دارد؛ برای مثال تنظیم آن به نگارش 1.8 به صورت زیر است:
 @rem Add path to TypeScript Compiler
@if exist "%ProgramFiles%\Microsoft SDKs\TypeScript\1.8" set PATH=%ProgramFiles%\Microsoft SDKs\TypeScript\1.8;%PATH%
@if exist "%ProgramFiles(x86)%\Microsoft SDKs\TypeScript\1.8" set PATH=%ProgramFiles(x86)%\Microsoft SDKs\TypeScript\1.8;%PATH%
اگر از VS 2013 استفاده می‌کنید، چنین تنظیمی در فایل C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\VsDevCmd.bat آن نیز وجود دارد که به نگارش 1 تنظیم شده‌است و این مورد هم باید اصلاح شود (تنظیمات آن دقیقا مانند تنظیم فوق است).


تداخل ReSharper با شماره نگارش TypeScript نصب شده

برای نمونه اگر بخواهیم از decoratorsاستفاده کنیم، یک چنین خطایی نمایش داده می‌شود:


هرچند در ابتدای بحث، آخرین نگارش TypeScript برای دریافت معرفی شده‌است، اما پس از نصب آن، ممکن است هنوز خطای استفاده از نگارش قدیمی 1.4 را مشاهده کنید. علت آن به نصب بودن ReSharper بر می‌گردد:
به منوی ReSharper و سپس گزینه‌ی Options آن مراجعه کنید.
 ReSharper -> Options -> Code Editing -> TypeScript -> Inspections -> Typescript language level

در اینجا می‌توان نگارش TypeScript مورد استفاده را تغییر داد. این شماره‌ها، نگارش‌هایی هستند که ReSharper از آن‌ها پشتیبانی می‌کند و نه شماره‌ای که نصب شده‌است.

و یا حتی می‌توان به صورت کامل فایل‌های ts را از کنترل ReSharper خارج کرد:
 Tools -> Options -> ReSharper Options -> Code Inspection -> Settings -> File Masks to Skip -> add *.ts
این مورد زمانی مفید خواهد بود که شماره نگارش فعلی TypeScript، از شماره نگارش پشتیبانی شده‌ی توسط ReSharper بالاتر باشد. در این حالت ممکن است syntaxهای جدید زبان TypeScript را ReSharper به صورت خطا اعلام کند که اشتباه است. بنابراین باید به ReSharper اعلام کرد که از این فایل‌ها صرفنظر کند. برای نمونه در زمان نگارش این مطلب، جهت کار با decorators، حتما نیاز است ReSharper را جهت حذف بررسی فایل‌های ts تنظیم کرد و گرنه ذیل کدهای مرتبط، خطوط قرمز نمایش خطا را مشاهده خواهید کرد که با توجه به کامپایلر جدید موجود، بی‌مورد است.


افزودن فایل tsconfig.json به پروژه

همانطور که در مطلب «تنظیمات کامپایلر TypeScript» نیز مطالعه کردید، روش دیگری نیز برای ذکر تنظیمات ویژه‌ی کامپایلر، خصوصا مواردی که در برگه‌ی خواص پروژه هنوز اضافه نشده‌اند، با استفاده از افزودن فایل ویژه‌ی tsconfig.json وجود دارد.
پشتیبانی کاملی از فایل‌های tsconfig.json در پروژه‌های VS 2015 با ASP.Core 1.0 وجود دارد و حتی گزینه‌ای در منوی add->new item برای آن درنظر گرفته شده‌است.
اگر گزینه‌ی فوق را در لیست موارد add->new item پیدا نمی‌کنید (تحت عنوان TypeScript JSON Configuration File)، مهم نیست. تنها کافی است فایل جدیدی را به نام tsconfig.json به ریشه‌ی پوشه‌ی فایل‌های ts خود اضافه کنید؛ با این محتوا:
 {
    "compilerOptions": {
         "target": "es5",
         "outDir": "../Scripts",
         "module": "commonjs",
         "sourceMap": true,
         //"watch": true, // JsErrorScriptException (0x30001)
         //"compileOnSave": true, // https://github.com/Microsoft/TypeScript/issues/7362#issuecomment-196586037
         "experimentalDecorators": true,
         "emitDecoratorMetadata": true
    }
}
حتی اگر از VS 2013 هم استفاده می‌کنید، این فایل توسط کامپایلر tsc شناسایی شده و استفاده می‌شود. برای آزمایش آن، گزینه‌ای غیرمتعارف را به گزینه‌های موجود اضافه کرده و سپس پروژه را کامپایل کنید. بلافاصله خطایی را در لیست خطاهای کامپایل پروژه دریافت خواهید کرد.
در اینجا نیازی به استفاده از گزینه‌ی watch نیست و ممکن است سبب بروز خطای JsErrorScriptException (0x30001) شود. قرار است این مشکل در نگارش‌های بعدی افزونه‌ی TypeScript مخصوص VS.NET برطرف شود.


افزودن فایل‌های d.ts. از طریق نیوگت

به ازای هر کتابخانه‌ی جاوا اسکریپتی معروف، یک بسته‌ی نیوگت تعاریف نوع‌های TypeScriptآن هم وجود دارد.
یک مثال: فرض کنید می‌خواهیم فایل d.ts. کتابخانه‌ی jQuery را اضافه کنیم. برای این منظور jquery.typescript را در بین بسته‌های نیوگت موجود، جستجو کنید:


برای سایر کتابخانه‌ها نیز به همین صورت است. نام کتابخانه را به همراه typescript جستجو کنید.

‫پیاده سازی یک سیستم دسترسی Role Based در Web API و AngularJs

$
0
0
در این مجموعه مقالات قصد دارم یک روش را برای پیاده سازی سیستم Role Based سفارشی شده، به صورت پروژه محور، در اختیار شما دوستان قرار دهم. این مجموعه مقالات در هر دو بخش سرور و کلاینت، در قالب یک پروژه‌ی واقعی ارائه خواهد شد. در سمت سرور از Web API و سیستم Identity 2.1 و در سوی کلاینت از تکنولوژی قدرتمند AngularJs کمک گرفته شده‌است. در این مجموعه قصد دارم تا مراحل کلی و اصلی این سیستم دسترسی را تشریح کنم.

مقدمه ای بر سیستم مبتنی بر نقش کاربران

شاید برای شما هم پیش آمده باشد که برای وب سایت یا وب اپلیکیشن خود، به دنبال یک سیستم دسترسی بوده‌اید که بتوانید در آن نقش‌های گوناگونی را تعریف کنید و هر نقش شامل یک سری دسترسی خاص باشد و همچنین با اضافه شدن هر کاربر به این سیستم، بتوانید آنها را به یک نقش خاص انتساب دهید. برای اجرای این امر، روش‌های گوناگونی وجود دارد. یکی از این روش‌ها در سایت CodeProject توسط آقای Stefan Wloch تشریح شده است. در این مقاله کلیت و هدف یک سیستم Role Based، ساختار نقش‌ها و کاربران و دسترسی‌های آنها به خوبی بیان شده است.
 
و اما کمی درباره روشهای موجود حول توسعه یک سیستم مبتنی بر نقش (Role Based) صحبت کنیم. در ابتدا لازم است یک تعریف کلی از یک سیستم Role Based داشته باشیم:
یک سیستم مدیریت کاربر مبتنی بر نقش، سیستمی است که در آن نقش‌های گوناگونی تعریف می‌گردد. به گونه‌ای که هر نقش شامل یک سری دسترسی هایی است که به کاربر اجازه می‌دهد تا به بخشی از سیستم دسترسی داشته باشد. هر کاربر در این سیستم می‌تواند یک و یا چند نقش متفاوت داشته باشد و بر اساس آن نقش‌ها و دسترسی‌های تعریف شده، درون هر نقش به قسمتی از سیستم دسترسی داشته باشد.
بر اساس این تعریف ما در نهایت سه موجودیت نقش (Role)، دسترسی (Permission) و کاربر (User) را خواهیم داشت. در این سیستم دسترسی را اینگونه تعریف میکنیم:
دسترسی در یک سیستم، مجموعه‌ای از حوزه‌ها و یا ناحیه‌هایی است که در سیستم تعریف می‌شود. این حوزه‌ها می‌توانند شامل یک متد یا مجموعه‌ای از متدهای نوشته شده و یا فراتر از آن، شامل مجموعه‌ای از کنترلرها باشد که کاربر اجازه‌ی فراخوانی آنها را دارد.

بدیهی است کاربر برای ما موجودیتی مشخص است. یک کاربر ویژگی‌هایی مانند نام، نام کاربری، سن، رمز عبور و ... خاص خود را داراست که به واسطه این ویژگی‌ها از کاربری دیگر تمییز داده میشود. کاربر در سیستم طی دو مرحله جداگانه Authentication و Authorization مجاز است تا از بخش‌هایی از سیستم استفاده کند. مرحله Authentication به طور خلاصه شامل مرحله‌ای است که هویت کاربر (به عنوان مثال نام کاربری و رمز عبور) تایید صلاحیت میشود. این مرحله در واقع تایید کننده کاربر است و اما بخش بعدی که ما قصد داریم تا در این مورد راهکاری را ارائه دهیم بخش Authorization است. در این بخش به کاربر بر اساس نقش وی دسترسی‌هایی اعطا می‌گردد و کاربر را به استفاده از بخش‌هایی از سیستم مجاز میدارد.

دیاگرام زیر نمود سه موجودیت کاربر، نقش و دسترسی میباشد.

برای شرح دیاگرام فوق این چنین میتوان گفت که هر کاربر مجاز است چندین نقش داشته باشد و هر نقش نیز میتواند به چندین کاربر انتساب شود. در مورد دسترسی‌ها نیز به همین صورت، هر دسترسی نیز میتواند به چندین نقش انتساب شود.

ارائه یک سیستم مبتنی بر نقش کاربران با استفاده از تکنولوژی Web API و AngularJs

حال که از محتوا و عملکرد یک سیستم مبتنی بر نقش (Role Based) دانش کافی را کسب کردیم، قصد داریم تا به پیاده سازی کامل این سیستم بپردازیم. شاید برای شما سوال باشد که چرا ما قصد داریم تا این معماری را در Web API و AngularJs پیاده سازی کنیم. در جواب به این سوال باید بگوییم که پیاده سازی این روشها در تکنولوژی‌هایی مانند ASP.NET MVC و تکنولوژی‌ةای مشابه آن که صفحه را در سمت سرور می‌سازند، در بسیاری از منابع و مقالات اینترنتی موجود است. ما قصد داریم تا آنچه را که در یک Single Page Application پیاده سازی کرده‌ایم، به اشتراک بگذاریم. بنابراین قصد داریم تا علاوه بر تشریح کامل این سیستم که قسمت اعظم آن در سمت سرور انجام میشود، به تشریح نحوه تبادل اطلاعات و مدیریت دسترسی‌ها در سمت کلاینت نیز بپردازیم.
همانطور که میدانید ما در یک سیستم بر پایه SPA، برای ساخت یک صفحه ممکن است چندین API را فراخوانی کنیم. بنابراین APIهای نوشته شده به تعداد زیاد ولی با عملکرد اتمیک خواهند بود. به عنوان مثال در یک سیستم فروشگاه اینترنتی، برای ویرایش یک محصول، ممکن است دو یا چند متد API (فراخوانی آن محصول برای قرار دادن در فیلدهای ویرایش، ارسال اطلاعات پس از ویرایش به سرور و ...) فراخوانی گردند. با این حساب ما در تعریف و ایجاد دسترسی‌ها دو راه را خواهیم داشت.
  1. تعریف هر یک از متدهای اتمیک به عنوان یک مجوز دسترسی: در این روش نام کنترلر و نام متد به عنوان یک دسترسی تعریف خواهد شد. در این روش به ازای هر متد ما یک آیتم جدید را باید به جدول Permissions، اضافه نماییم و در نهایت برای تعریف یک نقش و انتساب دسترسی‌ها به یک کاربر، بایستی یک مجموعه چک باکس را که در نهایت به یک متد API ختم میشود، فعال یا غیر فعال کنیم.
  2. تعریف ناحیه‌های مختلف و کنترل‌های قابل انجام در آن ناحیه: در این روش ما قصد داریم تا مجموعه‌ای از متد‌ها را که هدف مشترکی را انجام خواهند داد، در یک ناحیه و یک کنترل بگنجانیم و از به وجود آمدن تعداد زیادی مجوز دسترسی جلوگیری نماییم. به عنوان مثال فرض کنید میخواهیم یک سطح دسترسی را با نام ویرایش کاربران تعریف کنیم. همانگونه که گفتیم ممکن است برای یک عملیات، دو و یا چندین متد درون یک کنترلر تعریف شوند. حال ما این متد‌ها را درون یک ناحیه دسترسی قرار خواهیم داد و آن را در یک حوزه و یک کنترل (Area & Control) میگنجانیم.
در بخش بعدی سعی میکنیم تا این مبحث را بازتر کنیم و به صورت عملی مراحل توسعه این مدل را تشریح نماییم.

      ‫پیاده سازی یک سیستم دسترسی Role Based در Web API و AngularJs - بخش اول

      $
      0
      0
      در این مجموعه مقالات قصد دارم یک روش را برای پیاده سازی سیستم Role Based سفارشی شده، به صورت پروژه محور، در اختیار شما دوستان قرار دهم. این مجموعه مقالات در هر دو بخش سرور و کلاینت، در قالب یک پروژه‌ی واقعی ارائه خواهد شد. در سمت سرور از Web API و سیستم Identity 2.1 و در سوی کلاینت از تکنولوژی قدرتمند AngularJs کمک گرفته شده‌است. در این مجموعه قصد دارم تا مراحل کلی و اصلی این سیستم دسترسی را تشریح کنم.

      مقدمه ای بر سیستم مبتنی بر نقش کاربران

      شاید برای شما هم پیش آمده باشد که برای وب سایت یا وب اپلیکیشن خود، به دنبال یک سیستم دسترسی بوده‌اید که بتوانید در آن نقش‌های گوناگونی را تعریف کنید و هر نقش شامل یک سری دسترسی خاص باشد و همچنین با اضافه شدن هر کاربر به این سیستم، بتوانید آنها را به یک نقش خاص انتساب دهید. برای اجرای این امر، روش‌های گوناگونی وجود دارد. یکی از این روش‌ها در سایت CodeProject توسط آقای Stefan Wloch تشریح شده است. در این مقاله کلیت و هدف یک سیستم Role Based، ساختار نقش‌ها و کاربران و دسترسی‌های آنها به خوبی بیان شده است.
       
      و اما کمی درباره روشهای موجود حول توسعه یک سیستم مبتنی بر نقش (Role Based) صحبت کنیم. در ابتدا لازم است یک تعریف کلی از یک سیستم Role Based داشته باشیم:
      یک سیستم مدیریت کاربر مبتنی بر نقش، سیستمی است که در آن نقش‌های گوناگونی تعریف می‌گردد. به گونه‌ای که هر نقش شامل یک سری دسترسی هایی است که به کاربر اجازه می‌دهد تا به بخشی از سیستم دسترسی داشته باشد. هر کاربر در این سیستم می‌تواند یک و یا چند نقش متفاوت داشته باشد و بر اساس آن نقش‌ها و دسترسی‌های تعریف شده، درون هر نقش به قسمتی از سیستم دسترسی داشته باشد.
      بر اساس این تعریف ما در نهایت سه موجودیت نقش (Role)، دسترسی (Permission) و کاربر (User) را خواهیم داشت. در این سیستم دسترسی را اینگونه تعریف میکنیم:
      دسترسی در یک سیستم، مجموعه‌ای از حوزه‌ها و یا ناحیه‌هایی است که در سیستم تعریف می‌شود. این حوزه‌ها می‌توانند شامل یک متد یا مجموعه‌ای از متدهای نوشته شده و یا فراتر از آن، شامل مجموعه‌ای از کنترلرها باشد که کاربر اجازه‌ی فراخوانی آنها را دارد.

      بدیهی است کاربر برای ما موجودیتی مشخص است. یک کاربر ویژگی‌هایی مانند نام، نام کاربری، سن، رمز عبور و ... خاص خود را داراست که به واسطه این ویژگی‌ها از کاربری دیگر تمییز داده میشود. کاربر در سیستم طی دو مرحله جداگانه Authentication و Authorization مجاز است تا از بخش‌هایی از سیستم استفاده کند. مرحله Authentication به طور خلاصه شامل مرحله‌ای است که هویت کاربر (به عنوان مثال نام کاربری و رمز عبور) تایید صلاحیت میشود. این مرحله در واقع تایید کننده کاربر است و اما بخش بعدی که ما قصد داریم تا در این مورد راهکاری را ارائه دهیم بخش Authorization است. در این بخش به کاربر بر اساس نقش وی دسترسی‌هایی اعطا می‌گردد و کاربر را به استفاده از بخش‌هایی از سیستم مجاز میدارد.

      دیاگرام زیر نمود سه موجودیت کاربر، نقش و دسترسی میباشد.

      برای شرح دیاگرام فوق این چنین میتوان گفت که هر کاربر مجاز است چندین نقش داشته باشد و هر نقش نیز میتواند به چندین کاربر انتساب شود. در مورد دسترسی‌ها نیز به همین صورت، هر دسترسی نیز میتواند به چندین نقش انتساب شود.

      ارائه یک سیستم مبتنی بر نقش کاربران با استفاده از تکنولوژی Web API و AngularJs

      حال که از محتوا و عملکرد یک سیستم مبتنی بر نقش (Role Based) دانش کافی را کسب کردیم، قصد داریم تا به پیاده سازی کامل این سیستم بپردازیم. شاید برای شما سوال باشد که چرا ما قصد داریم تا این معماری را در Web API و AngularJs پیاده سازی کنیم. در جواب به این سوال باید بگوییم که پیاده سازی این روشها در تکنولوژی‌هایی مانند ASP.NET MVC و تکنولوژی‌ةای مشابه آن که صفحه را در سمت سرور می‌سازند، در بسیاری از منابع و مقالات اینترنتی موجود است. ما قصد داریم تا آنچه را که در یک Single Page Application پیاده سازی کرده‌ایم، به اشتراک بگذاریم. بنابراین قصد داریم تا علاوه بر تشریح کامل این سیستم که قسمت اعظم آن در سمت سرور انجام میشود، به تشریح نحوه تبادل اطلاعات و مدیریت دسترسی‌ها در سمت کلاینت نیز بپردازیم.
      همانطور که میدانید ما در یک سیستم بر پایه SPA، برای ساخت یک صفحه ممکن است چندین API را فراخوانی کنیم. بنابراین APIهای نوشته شده به تعداد زیاد ولی با عملکرد اتمیک خواهند بود. به عنوان مثال در یک سیستم فروشگاه اینترنتی، برای ویرایش یک محصول، ممکن است دو یا چند متد API (فراخوانی آن محصول برای قرار دادن در فیلدهای ویرایش، ارسال اطلاعات پس از ویرایش به سرور و ...) فراخوانی گردند. با این حساب ما در تعریف و ایجاد دسترسی‌ها دو راه را خواهیم داشت.
      1. تعریف هر یک از متدهای اتمیک به عنوان یک مجوز دسترسی: در این روش نام کنترلر و نام متد به عنوان یک دسترسی تعریف خواهد شد. در این روش به ازای هر متد ما یک آیتم جدید را باید به جدول Permissions، اضافه نماییم و در نهایت برای تعریف یک نقش و انتساب دسترسی‌ها به یک کاربر، بایستی یک مجموعه چک باکس را که در نهایت به یک متد API ختم میشود، فعال یا غیر فعال کنیم.
      2. تعریف ناحیه‌های مختلف و کنترل‌های قابل انجام در آن ناحیه: در این روش ما قصد داریم تا مجموعه‌ای از متد‌ها را که هدف مشترکی را انجام خواهند داد، در یک ناحیه و یک کنترل بگنجانیم و از به وجود آمدن تعداد زیادی مجوز دسترسی جلوگیری نماییم. به عنوان مثال فرض کنید میخواهیم یک سطح دسترسی را با نام ویرایش کاربران تعریف کنیم. همانگونه که گفتیم ممکن است برای یک عملیات، دو و یا چندین متد درون یک کنترلر تعریف شوند. حال ما این متد‌ها را درون یک ناحیه دسترسی قرار خواهیم داد و آن را در یک حوزه و یک کنترل (Area & Control) میگنجانیم.
      در بخش بعدی سعی میکنیم تا این مبحث را بازتر کنیم و به صورت عملی مراحل توسعه این مدل را تشریح نماییم.

          ‫رمزنگاری یک بانک اطلاعاتی SQLite

          $
          0
          0
          ممکن است برای شما هم پیش آمده باشد که بخواهید در برنامه‌تان از دیتابیس SQLite استفاده کنید و دسترسی به آن از طریق برنامه‌های SQLite Browser امکان پذیر نباشد؛ به زبان ساده‌، امنیت آن را تامین کنید.
          SQLite امکان تعیین کلمه‌ی عبور را به شما می‌دهد تا به وسیله‌ی آن بتوانید دیتابیس را رمزنگاری کنید تا امکان باز کردن آن به صورت عادی وجود نداشته باشد.

          برای ایجاد کلمه عبوردر دیتابیسی که از قبل رمزنگاری نشده است و یا تغییر کلمه عبوردیتابیسی که قبلا رمزنگاری شده است، به این صورت عمل کنید:
          // بازکردن دیتابیسی که از قبل رمزنگاری نشده است
          SQLiteConnection cnn = new SQLiteConnection("Data Source=c:\\test.db3");
          cnn.Open();
          
          // دیتابیس رمزنگاری شده. ارتباط با دیتابیس همچنان صحیح و برقرار باقی می‌ماند
          cnn.ChangePassword("mypassword");

          برای حذف کلمه عبوردر دیتابیسی که از قبل رمزنگاری شده است باید تابع ChangePassword را با مقدار null و یا "" فراخوانی کنید.
          // بازکردن دیتابیسی که از قبل رمزنگاری شده
          SQLiteConnection cnn = new SQLiteConnection("Data Source=c:\\test.db3;Password=mypassword");
          cnn.Open();
          
          // حذف رمزنگاری از دیتابیس رمزنگاری شده
          cnn.ChangePassword( null as byte[] );

          برای بازکردن دیتابیس رمزنگاری شدهو یاایجاد یک دیتابیس رمزنگاری شدهباید کلمه عبور در ConnectionString (همانند مثال بالا) قید شود و یا با فراخوانی تابع SetPassword قبل از فراخوانی تابع Open این کار را انجام داد. در صورتی که کلمه عبور در ConnectionString قید شود باید به صورت cleartext باشد. در حالیکه وقتی از تابع SetPassword استفاده می‌کنید می‌توانید کلمه عبور را به صورت آرایه ای از byte نیز تعریف کنید.
          // SetPassword بازکردن دیتابیس رمزنگاری شده با استفاده از فراخوانی تابع
          SQLiteConnection cnn = new SQLiteConnection("Data Source=c:\\test.db3");
          
          cnn.SetPassword(new byte[] { 0xFF, 0xEE, 0xDD, 0x10, 0x20, 0x30 });
          cnn.Open();

          در صورتیکه بخواهید ارتباطی را که وجود دارد، به یک فایل دیتابیس دیگر متصل کنید باید به صورت زیر عمل کنید:

          - در صورتیکه کلمه عبور به صورت cleartext  می‌باشد:
          SQLiteConnection cnn = new SQLiteConnection("Data Source=c:\\test.sqlite");
          cnn.Open();
          
          cmd = new SQLiteCommand("ATTACH DATABASE 'c:\\pwd.sqlite' AS [Protected] KEY 'mypassword'", cnn);
          cmd.ExecuteNonQuery();

          - در صورتیکه کلمه عبور به صورت باینری می‌باشد:
          SQLiteConnection cnn = new SQLiteConnection("Data Source=c:\\test.db3");
          cnn.Open();
          
          cmd = new SQLiteCommand("ATTACH DATABASE 'c:\\pwd.db3' AS [Protected] KEY X'FFEEDD102030'", cnn);
          cmd.ExecuteNonQuery();

          ‫پیاده سازی یک سیستم دسترسی Role Based در Web API و AngularJs - بخش دوم

          $
          0
          0
          در بخش پیشینمروری اجمالی را بر روی یک سیستم مبتنی بر نقش کاربر داشتیم. در این بخش تصمیم داریم تا به جزئیات بیشتری در مورد سیستم دسترسی ارائه شده بپردازیم.
          همانطور که گفتیم ما به دو صورت قادر هستیم تا دسترسی‌های (Permissions) یک سیستم را تعریف کنیم. روش اول این بود که هر متد از یک کنترلر، دقیقا به عنوان یک آیتم در جدول Permissions قرار گیرد و در نهایت برای تعیین نقش جدید، مدیر باید جزء به جزء برای هر نقش، دسترسی به هر متد را مشخص کند. در روش دوم مجموعه‌ای از API Methodها به یک دسترسی تبدیل شده است.
          مراحل توسعه این روش به صورت زیر خواهند بود:
          1. توسعه پایگاه داده سیستم دسترسی مبتنی بر نقش
          2. توسعه یک Customized Filter Attribute بر پایه Authorize Attribute
          3. توسعه سرویس‌های مورد استفاده در Authorize Attribute
          4. توسعه کنترلر Permissions: تمامی APIهایی که در جهت همگام سازی دسترسی‌ها بین کلاینت و سرور را بر عهده دارند در این کنترلر توسعه داده میشود.
          5. توسعه سرویس مدیریت دسترسی در کلاینت توسط AngularJS

          توسعه پایگاه داده

          در این مرحله پایگاه داده را به صورت Code First پیاده سازی مینماییم. مدل Permissions به صورت زیر میباشد:
              public class Permission
              {
                  [Key]
                  public string Id { get; set; }
                  public string Title { get; set; }
                  public string Description { get; set; }
                  public string Area { get; set; }
                  public string Control { get; set; }
                  public virtual ICollection<Role> Roles { get; set; }
              }
          در مدل فوق همانطور که مشاهده میکنید یک ارتباط چند به چند، به Roles وجود دارد که در EF به صورت توکار یک جدول اضافی Junction اضافه خواهد شد با نام RolesPermissions. Area و Control نیز طبق تعریف شامل محدوده مورد نظر و کنترل‌های روی ناحیه در نظر گرفته می‌شوند. به عنوان مثال برای یک سایت فروشگاهی، برای بخش محصولات می‌توان حوزه‌ها و کنترل‌ها را به صورت زیر تعریف نمود:
           ControlArea 
           view products
           add products
           edit products
           delete products

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

          مدل Roles را ما به صورت زیر توسعه داده‌ایم:

              public class Role
              {
                  [Key]
                  public string Id { get; set; }
                  public string Title { get; set; }
                  public string Description { get; set; }
                  public virtual ICollection<Permission> Permissions { get; set; }
                  public virtual ICollection<User> Users { get; set; }
              }

          در مدل فوق می‌بینید که دو رابطه چند به چند وجود دارد. رابطه اول که همان Permissions است و در مدل پیشین تشریح شد. رابطه‌ی دوم رابطه چند به چند بین کاربر و نقش است. چند کاربر قادرند یک نقش در سیستم داشته باشند و همینطور چندین نقش میتواند به یک کاربر انتساب داده شود.

          ما در این سیستم از ASP.NET Identity 2.1 استفاده و کلاس IdentityUser را override کرده‌ایم. در مدل override شده، برخی اطلاعات جدید کاربر، به جدول کاربر اضافه شده‌اند. این اطلاعات شامل نام، نام خانوادگی، شماره تماس و ... می‌باشد.

          public class ApplicationUser : IdentityUser
              {
                  [MaxLength(100)]
                  public string FirstName { get; set; }
                  [MaxLength(100)]
                  public string LastName { get; set; }
                  public bool IsSysAdmin { get; set; }
                  public DateTime JoinDate { get; set; }
          
                  public virtual ICollection<Role> Roles { get; set; }
              }

          در نهایت تمامی این مدل‌ها به وسیله EF Code First پایگاه داده سیستم ما را تشکیل خواهند داد.

          توسعه یک Customized Filter Attribute بر پایه Authorize Attribute 

          اگر در مورد Custom Filter Attributeها اطلاعات ندارید نگران نباشید! مقاله «فیلترها در MVC» تمامی آنچه را که باید در اینباره بدانید، به شما خواهد گفت. همچنین در  مقاله وب سایت  مایکروسافت به صورت عملی (ایجاد یک سیستم Logger) همه چیز را برای شما روشن خواهد کرد. حال بپردازیم به Filter Attribute نوشته شده که قرار است وظیفه پیش پردازش تمامی درخواست‌های کاربر را انجام دهد. در ابتدا کمی در اینباره بگوییم که این فیلتر قرار است چه کاری را دقیقا انجام دهد!
          این فیلتر قرار است پیش از پردازش هر API Method، درخواست کاربر را با استفاده از نقشی که او در سیستم دارد، بررسی نماید که آیا کاربر به API اجازه دسترسی دارد یا خیر. برای این کار باید ما در ابتدا نقش‌های کاربر را بررسی نماییم. پس از اینکه نقش‌های کاربر واکشی شدند، باید بررسی کنیم آیا نقشی که کاربر دارد، شامل این حوزه و کنترل بوده است یا خیر؟ Area و Control دو پارامتری هستند که در سیستم پیش از هر متد، Hard Code شده‌اند و در ادامه نمونه‌ای از آن را نمایش خواهیم داد.
              public class RBACAttribute : AuthorizeAttribute
              {
                  public string Area { get; set; }
                  public string Control { get; set; }
                  AccessControlService _AccessControl = new AccessControlService();
                  public override void OnAuthorization(HttpActionContext actionContext)
                  {
                      var userId = HttpContext.Current.User.Identity.GetCurrentUserId();
                      // If User Ticket is Not Expired
                      if (userId == null || !_AccessControl.HasPermission(userId, this.Area, this.Control))
                      {
                          actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
                      }
                  }
              }
          در خط پنجم، سرویس AccessControl را فراخوانی کرده‌ایم که در ادامه به پیاده سازی آن نیز خواهیم پرداخت. متد HasPermission از این سرویس دو پارامتر id کاربر و Area و Control را دریافت میکند و با استفاده از این سه پارامتر بررسی میکند که آیا کاربر جاری به این بخش دسترسی دارد یا خیر؟ در صورت منقضی شدن ticket کاربر و یا عدم دسترسی، سرور Unauthorized status code را به کاربر باز می‌گرداند.
          بلوک زیر استفاده از این فیلتر را نمایش می‌دهد:
          [HttpPost]
          [Route("ChangeProductStatus")]
          [RBAC(Area = "products", Control = "edit")]
          public async Task<HttpResponseMessage> ChangeProductStatus(StatusCodeBindingModel model)
          {
          // Method Body
          }
          همانطور که مشاهده می‌کنید کافیست RBAC را با دو پارامتر، پیش از متد نوشت. به صورت خودکار پیش از فراخوانی این متد که وظیفه تغییر وضعیت کالا را بر عهده دارد، فیلتر نوشته شده فراخوانی خواهد شد.
          در بخش بعدی به بیان ادامه جریان و توسعه سرویس Access Control خواهیم پرداخت.

          ‫الگوی سردر Facade Pattern

          $
          0
          0
          یکی از الگوهای ساختاری Gang Of Four، استفاده از الگوی Facade است که پیچیدگی‌های یک سیستم را مخفی می‌سازد و با ارائه یک پیاده سازی ساده‌تر، استفاده از آن و تست آن را راحت‌تر می‌سازد. این الگو یک کلاس یا یک سیستمی را با متدها و رویدادهایی ساده، در اختیار ما قرار می‌دهد و در یک لحظه، تنها با یک کلاس واحد سر و کله می‌زنیم. احتمالا بسیاری از شما از این الگو استفاده کرده‌اید، ولی شاید با اسم آن آشنا نبوده‌اید.

          کار این کلاس در واقع ترکیب کلاس‌ها و کتابخانه‌های کاری مشخص است که نیاز به ارتباط با یکدیگر را دارند. به عنوان مثال یک برنامه کتابخانه، برای وظیفه‌ای چون امانت یک کتاب نیاز است تا چندین کلاس مختلف را با یکدیگر به کار بگیرد که این وظایف شامل موارد زیر می‌باشند:
          1. بررسی وجود کتاب
          2. بررسی تعداد موجود یک کتاب در کتابخانه
          3. بررسی وضعیت امانی کتاب (آیا کتاب در دست کسی از قبل امانت است؟ یا کتاب برای امانت آزاد است؟)
          4. در صورتی که کتابی بیش از زمان مورد نظر در دست کسی امانت است، با یک پیامک از او بخواهیم که کتاب را بازگرداند.
          تمامی موارد بالا تنها قسمتی از انجام یک عمل ساده هستند که در یک گروه جای می‌گیرند؛ ولی در واقع از چندین کلاس جدا مثل کلاس کتاب، امانت، سیستم پیامکی و ... استفاده شده است . الگوی Facade به ما کمک می‌کند تا پیچیدگی و تعداد خطوط اجرا را در سطوح بالاتر مخفی سازیم و تنها با صدا زدن یک یا چند متد ساده، کار را به اتمام برسانیم. این کار باعث کاهش کد و خوانایی برنامه در سطوح بالاتر می‌شود.

          در کد زیر ما قصد داریم نمونه‌ای از اجرای این الگو را ببینیم. ابتدا سه کلاس اطلاعاتی زیر را ایجاد می‌کنیم:
          کلاس کتاب Book:
              class Book
              {
                  public int Id { get; set; }
                  public string Title { get; set; }
                  public int Quantity { get; set; }
                  public bool IsLoanable { get; set; }
              }

          کلاس کاربر User
           class User
              {
                  public int Id { get; set; }
                  public string Title { get; set; }
                  public  string CellPhoneNumber { get; set; }
              }

          کلاس امانت Loan
            class Loan
              {
                 public DateTime ExpiredDate { get; set; }
                  public User User { get; set; }
              }

          بعد از آن سه کلاس را برای مدیریت کتاب، مدیریت امانت و مدیریت پیامکی، ایجاد می‌کنیم:
          کلاس کتاب BookManager:
           class BookManager
              {
                  public Book GetBook(int id)
                  {
                          return new Book()
                          {
                              Id=id,
                              Title = "a book",
                              Quantity = 3,
                              IsLoanable = true
                          };
                  }
              }
          کلاس بالا یک کتاب را با موجودی سه عدد بازمی‌گرداند که خاصیت IsLoanable آن می‌گوید کتاب را برای بردن به خانه امانت می‌دهند؛ ولی اگر کتاب به فرض یک کتاب مرجع باشد باید False برگرداند تا از امانت آن خودداری شود.

          کلاس LoanManager برای مدیریت امانت‌ها
           public int IsLoan(int bookId)
                  {
                      return 2;
                  }
          
                  public List<Loan> GetLoans(int bookId)
                  {
                      return new List<Loan>()
                      {
                          new Loan()
                          {
                              ExpiredDate = DateTime.Now.AddDays(-15),
                              User = new User()
                          {
                              Id = 2,
                              Title = "User1",
                              CellPhoneNumber = "342342424"
                          }
                          },
                          new Loan()
                          {
                              ExpiredDate = DateTime.Now.AddDays(5),
                              User = new User()
                                  {
                                      Id = 56,
                                      Title = "User56",
                                      CellPhoneNumber = "324324324324"
                                  }
                          }
                      };
                  }
          این کلاس شامل دو متد است که اولین متد آن کد کتاب را دریافت می‌کند و تعداد افرادی را که در حال حاضر نسخه‌های مختلف آن را به امانت برده‌اند، برمی‌گرداند. در کد بالا عدد دو بازگردانده می‌شود که میگوید از نسخه‌های موجود این کتاب در کتابخانه، دوتای آن‌ها به امانت برده شده‌اند. در متد دوم، کد کتاب داده شده و امانت‌های فعلی آن کتاب که همان دو عدد بالا می‌باشد را برگشت می‌دهد.

          در نهایت سومی کلاس مدیریتی برای پیامک هاست:
             class SmsManager
              {
                  public void SendMessage(string number)
                  {
                      Console.WriteLine("please take back the book to the library : "+number);
                  }
              }

          و در کلاس Facade داریم
              class FacadeBookLoan
              {
                  private readonly BookManager _bookManager;
                  private readonly LoanManager _loanManager;
                  private readonly SmsManager _smsManager;
                  public FacadeBookLoan()
                  {
                      _bookManager = new BookManager();
                      _loanManager=new LoanManager();
                      _smsManager=new SmsManager();
                  }
          
                  public int IsLoanable(int bookId)
                  {
                      var book = _bookManager.GetBook(2);
                      if (book == null)
                          return -2;
                      if (!book.IsLoanable)
                          return -1;
                      var howManyBookIsLoaned = _loanManager.IsLoan(bookId);
          
                      if(howManyBookIsLoaned>0) ManageLoaners(bookId);
          
                      return book.Quantity - howManyBookIsLoaned;
                  }
          
                  private void ManageLoaners(int bookId)
                  {
                      var loans = _loanManager.GetLoans(bookId);
          
                      foreach (var loan in loans)
                      {
                          if (loan.ExpiredDate > DateTime.Now)
                          {
                              _smsManager.SendMessage(loan.User.CellPhoneNumber);
                          }
                      }
                  }
              }
          در این کلاس متد IsLoanable چک می‌کند که آیا کتاب قابل امانت هست یا خیر. در اینجا مرحله به مرحله، وجود کتاب و قابلیت امانت کتاب بررسی شده و در صورت نتیجه، کد وضعیت -1 یا -2 بازگردانده می‌شوند و در مرحله بعد تعداد نسخه‌های آن کتاب که در حال حاضر در دست امانت هستند، بررسی می‌شوند. اگر کتابی در درست امانت باشد، متد خصوصی صدا زده شده و به کاربرانی که بیش از مدت معینی یک نسخه از کتاب را داشته‌اند، پیامک می‌زند که کتاب را بازگردانید؛ چرا که نسخه‌ها در حال کاهش هستند و در مرحله بعد تعداد نسخه‌های موجود از آن کتاب را در کتابخانه باز می‌گرداند که در این مثال تنها یک نسخه از آن کتاب در کتابخانه موجود است و دو تای آن‌ها در دست امانت هستند که یکی از امانت دارها 15 روز است کتاب را در تاریخ معینی تحویل نداده است.

          کد بدنه اصلی برنامه:
          var myfacade=new FacadeBookLoan();
          var loansCount= myfacade.IsLoanable(2);
          Console.WriteLine(loansCount > 0 ? "you can loan the book" : "you can't loan the book");
          خروجی برنامه:
          please take back the book to the library : 324324324324
          you can loan the book

          ‫الگوی نماینده (پروکسی) Proxy Pattern

          $
          0
          0
          همه کاربران کامپیوتر در ایران به خوبی با کلمه پروکسی آشنا هستند. پروکسی به معنی نماینده یا واسط است و پروکسی واسطی است بین ما و شیء اصلی. پروکسی در شبکه به این معنی است که سیستم شما به یک سیستم واسط متصل شده است که از طریق پروکسی محدودیت‌های دسترسی برای آن تعریف شود. در اینجا هم پروکسی در واقع به همین منظور استفاده می‌شود. در تعدادی از کامنت‌های سایت خوانده بودم دوستان در مورد اصول SOLID  و Refactoring بحث می‌کردند که آیا انجام عمل اعتبارسنجی در خود اصل متد کار درستی است یا خیر. در واقع خودم هم نمی‌دانم که این حرکت چقدر به این اصول پایبند است یا خیر، ولی می‌دانم که الگوی پروکسی کل سؤالات بالا را حذف می‌کند و با این الگو دیگر این سؤال اهمیتی ندارد. به عنوان مثال فرض کنید که شما یک برنامه ساده کار با فایل را دارید. ولی اگر بخواهید اعتبارسنجی‌هایی را برای آن تعریف کنید، بهتر است اینکار را به یک پروکسی بسپارید تا شیء گرایی بهتری را داشته باشید.

          برای شروع ابتدا یک اینترفیس تعریف می‌کنیم:
             public interface IFilesService
              {
                  DirectoryInfo GetDirectoryInfo(string directoryPath);
                  void DeleteFile(string fileName);
                  void WritePersonInFile(string fileName,string name, string lastName, byte age);
              }
          این اینترفیس شامل سه متد نام نویسی، حذف فایل و دریافت اطلاعات یک دایرکتوری است. بعد از آن دو عدد کلاس را از آن مشتق می‌کنیم:
          کلاس اصلی:
              class FilesServices:IFilesService
              {
                  public DirectoryInfo GetDirectoryInfo(string directoryPath)
                  {
                      return new DirectoryInfo(directoryPath);
                  }
          
                  public void DeleteFile(string fileName)
                  {
                      File.Delete(fileName);
                      Console.WriteLine("the file has been deleted");
                  }
          
                  public void WritePersonInFile(string fileName, string name, string lastName, byte age)
                  {
                      var text = $"my name is {name} {lastName} with {age} years old from dotnettips.info";
                      File.WriteAllText(fileName,text);
                  }    
              }
          که تنها وظیفه اجرای فرامین را دارد و دیگری کلاس پروکسی است که وظیف تامین اعتبارسنجی و آماده سازی پیش شرط‌ها را دارد:
              class FilesServicesProxy:IFilesService
              {
                  private readonly IFilesService _filesService;
          
                  public FilesServicesProxy()
                  {
                      _filesService=new FilesServices();
                  }
          
                  public DirectoryInfo GetDirectoryInfo(string directoryPath)
                  {
                      var existing = Directory.Exists(directoryPath);
                      if (!existing)
                          Directory.CreateDirectory(directoryPath);
                      return _filesService.GetDirectoryInfo(directoryPath);
                  }
          
                  public void DeleteFile(string fileName)
                  {
                      if(!File.Exists(fileName))
                          Console.WriteLine("Please enter a valid path");
                      else
                          _filesService.DeleteFile(fileName);
                  }
          
                  public void WritePersonInFile(string fileName, string name, string lastName, byte age)
                  {
                      if (!Directory.Exists(fileName.Remove(fileName.LastIndexOf("\\"))))
                      {
                          Console.WriteLine("File Path is not valid");
                          return;
                      }
                      if (name.Trim().Length == 0)
                      {
                          Console.WriteLine("first name must enter");
                          return;
                      }
          
                      if (lastName.Trim().Length == 0)
                      {
                          Console.WriteLine("last name must enter");
                          return;
                      }
          
                      if (age<18)
                      {
                          Console.WriteLine("your age is illegal");
                          return;
                      }
          
          
                      if (name.Trim().Length < 3)
                      {
                          Console.WriteLine("first name must be more than 2 letters");
                          return;
                      }
          
                      if (lastName.Trim().Length <5)
                      {
                          Console.WriteLine("last name must be more than 4 letters");
                          return;
                      }
          
                      _filesService.WritePersonInFile(fileName,name,lastName,age);
                      Console.WriteLine("the file has been written");
                  }
              }
          کلاس پروکسی، همان کلاسی است که شما باید صدا بزنید. وظیفه کلاس پروکسی هم این است که در زمان معین و صحیح، کلاس اصلی شما را بعد از اعتبارسنجی‌ها و انجام پیش شرط‌ها صدا بزند. همانطور که می‌بیند، ما در سازنده کلاس اصلی را در حافظه قرار می‌دهیم. سپس در هر متد، اعتبارسنجی‌های لازم را انجام می‌دهیم. مثلا در متد GetDirectoryInfo باید اطلاعات دایرکتوری بازگشت داده شود؛ ولی اصل عمل در واقع در کلاس اصلی است و اینجا فقط شرط گذاشته‌ایم اگر مسیر داده شده معتبر نبود، همان مسیر را ایجاد کن و سپس متد اصلی را صدا بزن. یا اگر فایل موجود است جهت حذف آن اقدام کن و ...
          در نهایت در بدنه اصلی با تست چندین حالت مختلف، همه متد‌ها را داریم:
          static void Main(string[] args)
                  {
                      IFilesService filesService=new FilesServicesProxy();
                      filesService.WritePersonInFile("c:\\myfakepath\\a.txt","ali","yeganeh",26);
          
                      var directory = filesService.GetDirectoryInfo("d:\\myrightpath\\");
                      var fileName = Path.Combine(directory.FullName, "dotnettips.txt");
          
                      filesService.WritePersonInFile(fileName, "al", "yeganeh", 26);
                      filesService.WritePersonInFile(fileName, "ali", "yeganeh", 12);
                      filesService.WritePersonInFile(fileName, "ali", "yeganeh", 26);
          
                      filesService.DeleteFile("c:\\myfakefile.txt");
                      filesService.DeleteFile(fileName);
                  }
          و نتیجه خروجی:
          File Path is not valid
          first name must be more than 2 letters
          your age is illegal
          the file has been written
          Please enter a valid path
          the file has been deleted

          ‫الگوی وضعیت State Pattern

          $
          0
          0
          الگوی وضعیت، یکی از الگوهای رفتاری Gang Of Four است و بسیار شبیه به الگوی Strategy می‌باشد؛ ولی با کپسوله سازی بیشتر. در الگوی استراتژی تغییر وضعیت از بیرون کلاس اعمال می‌د ولی در الگوی وضعیت، بر اساس تغییر وضعیت درونی خودش صورت می‌گیرد.
          یکی از استفاده‌های این الگو برای مثال در پلیرهاست که وضعیت پخش را چون Play,Pause و ... در خود دارند. در اینجا هم از این مثالاستفاده می‌کنیم:
          ابتدا یک اینترفیس برای وضعیت خود بسازید که آرگومان ورودی متد آن را در مرحله بعد تعریف میکنیم:
          public interface IState
          {
              void PressPlay(MP3PlayerContext context);
          }
          سپس نوبت ایجاد کلاس اصلی یا همان دستگاه پخش که به آن Context می‌گوییم می‌رسد تا تغییر وضعیت الگو را به آن بسپاریم:
          public class MP3PlayerContext
          {
              public MP3PlayerContext()
              {
                  this.CurrentState = new StandbyState();
              }
              public MP3PlayerContext(IState state)
              {
                 this.CurrentState = state; 
              }
              public IState CurrentState { get; set; }
              public void Play()
              {
                  this.CurrentState.PressPlay(this);
              }
          }
          سپس کلاس‌های مختلف خود را بر اساس اینترفیس بالا می‌سازیم:
          public class StandbyState : IState
          {
              public void PressPlay(MP3PlayerContext context)
              {
                  context.CurrentState = new PlayingState();
              }
          }
          
          public class PlayingState : IState
          {
              public void PressPlay(MP3PlayerContext context)
              {
                  context.CurrentState = new StandbyState();
              }
          }
          در کدهای بالا، کلاس‌های Playing و StandBy در واقع شبیه سازی از عمل کلید پخش هستند که با هر بار فشردن آن، پخش به طور موقت توقف کرده و یا پخش خود را از سر می‌گیرد. کلاس Context نیز باید در ابتدا به طور پیش فرض با یکی از این مقادیر پر شود و برای دکمه پخش مشخص است که کلاس PlayingState می‌باشد.
          بدین ترتیب در اولین اجرای متد Play در کلاس Context، کلاس PlayingState اجرا می‌شود و وضعیت، به StandbyState تغییر می‌کند و هر بار که مجددا متد Play اجرا گردد، تعویض بین این دو کلاس صورت می‌گیرد.

          ‫الگوی بازدیدکننده Visitor Pattern

          $
          0
          0
          این الگو یکی دیگر از الگوهای رفتاری است که به قاعده OCP یا Open Closed Principle کمک بسیاری می‌کند. این الگو برای زمانی مناسب است که ما سعی بر این داریم تا یک سری الگوریتم‌های متفاوت را بر روی یک سری از اشیاء پیاده سازی کنیم. به عنوان مثال تصور کنید که ما در یک سازمان افراد مختلفی را از مدیریت اصلی گرفته، تا ساده‌ترین کارمندان، داریم و برای محاسبه حقوق و مالیات و ... نیاز است تا برای هر کدام دستور العمل‌هایی را اجرا کنیم  و ممکن است در آینده تعداد این دستور العمل‌ها بالاتر هم برود.
          در این مثال ما سه گروه Manager,Employee و Worker را داریم که می‌خواهیم با استفاده از این الگو برای هر کدام به طور جداگانه، حقوق و دستمزد و اضافه کاری را محاسبه کنیم. با توجه به اینکه فرمول هر یک جداست و این احتمال نیز وجود دارد که هر کدام خواص مخصوص به خود را داشته باشند که در دیگری وجود ندارد و در آینده این احتمال می‌رود که سمت جدید یا دستورالعمل‌های جدیدی اضافه شود، بهترین راه حل استفاده از الگوی Visitor است.

          الگوی visitor دو بخش مهم دارد؛ یکی Element که قرار است کار روی آن انجام شود. مثل سمت‌های مختلف و دیگری Visitor هست که همان دستورالعمل‌هایی چون محاسبه حقوق و دستمزد و ... است که روی المان‌ها صورت می‌گیرد.
          ابتدا برای هر کدام یک اینترفیس را با مشخصات زیر می‌سازیم:
           public interface IElement
              {
                  void Accept(IElementVisitor visitor);
              }

              public interface IElementVisitor
              {
                  void Visit(Manager manager);
                  void Visit(Employee manager);
                  void Visit(Worker manager);
              }
          همانطور که می‌بینید در کلاس Visitor سه متد هستند که سه کلاس مدیر، کارمند و کارگر را که مشتق شده از اینترفیس Element هستند، به صورت آرگومان می‌پذیرند. توصیف هر کلاس المان به شرح زیر است:
           public class  Manager: IElement
              {
                  public int WorkingHour = 8;
                  public int Wife = 1;
                  public int Children = 3;
                  public int OffDays = 6;
                  public int OverHours = 12;
          
                  public void Accept(IElementVisitor visitor)
                  {
                      visitor.Visit(this);
                  }
              }

          public class Employee: IElement
              {
                  public int WorkingHour = 8;
                  public int Wife = 1;
                  public int Children = 3;
                  public int OffDays = 6;
                  public int OverHours = 12;
          
                  public void Accept(IElementVisitor visitor)
                  {
                      visitor.Visit(this);
                  }
              }

          public class Worker:IElement
              {
                  public int WorkingHour = 8;
                  public int Wife = 1;
                  public int Children = 3;
                  public int OffDays = 6;
                  public int OverHours = 12;
          
                  public void Accept(IElementVisitor visitor)
                  {
                      visitor.Visit(this);
                  }
              }
          ما اطلاعات هر کلاس را در این مثال، مشابه گذاشته‌ایم تا نتیجه فرمول را ببینیم. ولی هیچ الزامی به رعایت آن نیست.
          حال وقت آن رسیده تا از روی کلاس Visitor، برای حقوق، دستمزد و اضافه کاری، کلاس‌های جدیدی را بسازیم:
           class SalaryCalculator:IElementVisitor
              {
                  public void Visit(Manager manager)
                  {
                      var salary = manager.WorkingHour*10000;
                      salary += manager.Wife*25000;
                      salary += manager.Children*20000;
                      salary -= manager.OffDays*5000;
                      Console.WriteLine("Manager's Salary is " + salary);
                  }
          
                  public void Visit(Employee employee)
                  {
                      var salary = employee.WorkingHour * 7000;
                      salary += employee.Wife * 15000;
                      salary += employee.Children * 10000;
                      salary -= employee.OffDays * 6000;
                      Console.WriteLine("Employee's Salary is " + salary);
                  }
          
                  public void Visit(Worker worker)
                  {
                      var salary = worker.WorkingHour * 6000;
                      salary += worker.Wife * 5000;
                      salary += worker.Children * 2000;
                      salary -= worker.OffDays * 7000;
                      Console.WriteLine("Worker's Salary is " + salary);
                  }
              }

              class WageCalculator:IElementVisitor
              {
                  public void Visit(Manager manager)
                  {
                      var wage = manager.OverHours*30000;
                      Console.WriteLine("Employee's wage is " + wage);
                  }
          
                  public void Visit(Employee employee)
                  {
                      var wage = employee.OverHours * 20000;
                      Console.WriteLine("Employee's wage is " + wage);
                  }
          
                  public void Visit(Worker worker)
                  {
                      var wage = worker.OverHours * 15000;
                      Console.WriteLine("Employee's wage is " + wage);
                  }
              }
          اکنون نیاز است تا ارتباط بین المان‌ها و بازدید کننده‌ها را طوری برقرار کنیم که برای تغییر آن‌ها در آینده، مشکلی نداشته باشیم. به همین جهت یک کلاس جدید به نام سیستم مالی ایجاد می‌کنیم:
          class FinancialSystem
              {
                  private readonly IList<IElement> _elements;
          
                  public FinanceSystem()
                  {
                      _elements=new List<IElement>();
                  }
          
                  public void Attach(IElement element)
                  {
                      _elements.Add(element);
                  }
          
                  public void Detach(IElement element)
                  {
                      _elements.Remove(element);
                  }
          
                  public void Accept(IElementVisitor visitor)
                  {
                      foreach (var element in _elements)
                      {
                          element.Accept(visitor);
                      }
                  }
              }
          در این روش تمام المان‌ها را داخل یک لیست قرار داده و سپس با استفاده از متد Accept، یکی از کلاس‌های مشتق شده از Visitor را به آن نسبت می‌دهیم که وظیفه آن صدا زدن متد Accept درون المان هاست. وقتی متد Accept المان‌ها صدا زده شد، شیء، المان را به متد Visit در Visitor داده و فرمول را روی آن اجرا می‌کند.
          بدنه اصلی:
          IElement manager=new Manager();
          IElement employee=new Employee();
          IElement worker=new Worker();
          
          var fine=new FinancialSystem();
          fine.Attach(manager);
          fine.Attach(employee);
          fine.Attach(worker);
          
          fine.Accept(new SalaryCalculator());
          fine.Accept(new WageCalculator());
          نتیجه خروجی:
          Manager's Salary is 135000
          Employee's Salary is 65000
          Worker's Salary is 17000
          Manager's wage is 360000
          Employee's wage is 240000
          Worker's wage is 180000

          ‫الگوی شیء نال Null Object Pattern

          $
          0
          0
          این الگو شاید به نظر ساده برسد، ولی در بعضی موارد می‌تواند در سطوح بالاتر، کدهای تمیزتر و خلوت‌تری را در اختیار شما بگذارد. در مورد این الگو، در کتاب توسعه چابکعمو باب نیز آمده است. بسیاری ممکن است نسبت به این الگو جبهه بگیرند و بگویند که بررسی نال بودن یک شیء بهتر است و یا حتی رخ دادن خطای Null Pointer Exception در برنامه باعث می‌شود بتوانیم باگ‌ها را پیدا کنیم. در جواب باید گفت که این الگو قرار نیست در همه جا مورد استفاده قرار گیرد. در مثال زیر می‌توانید تا حدی به جایگاه استفاده از این الگو برسید. اینکه چگونه و در کجا از یک الگو استفاده کنید، به عهده برنامه نویس است.
          کار این الگو در یک جمله این است که اگر متدی نتواند خروجی مناسبی را بدهد و به جای آن قرار باشد نال را برگشت دهد، به جای برگشت دادن نال، از یک شیء که هیچ رفتاری ندارد استفاده می‌کند و آن شیء را برمی‌گرداند تا در ادامه کد، بررسی نال بودن، یا خطای NPE رخ ندهد.

          به عنوان مثال فرض کنید قرار است یک کاربر با نام کاربری Ali به سیستم وارد شود؛ در اینجا سه حالت وجود دارد:
          1. این کاربر یافت شده و اجازه دسترسی دارد.
          2. این کاربر یافت شده و اجازه دسترسی ندارد.
          3. این کاربر یافت نمی‌شود.

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

          بدون استفاده از الگو
          در این مثال ابتدا کلاس یوزر را می‌سازیم:
           public class User
              {
                  public String Usernam { get; set; }
                  public bool Authenticated { get; set; }
              }
          در لایه سرویس هم خروجی را برمی‌گردانیم:
           public User GeUser(string uname)
                  {
                      if (uname == "Ali")
                      {
                          return new User()
                          {
                              Usernam = "Ali",
                              Authenticated = true
                          };
                      }
                      else if (uname == "Reza")
                      {
                          return new User()
                          {
                              Usernam = "Reza",
                              Authenticated = false
                          };
                      }
                      else
                      {
                          return null;
                      }
                  }
          در این حالت کد بعدی شما باید اینگونه باشد:
           var userServices=new UserServices();
                      var user = userServices.GeUser("Ali");
                      if (user != null && user.Authenticated)
                      {
                          Console.WriteLine("You are Authorized");
                      }
          همانطور که می‌بینید یک خط کد شرطیبه سیستم اضافه شد و در یک سیستم بزرگتر این بررسی‌ها بیشتر شده و حتی در بعضی نقاط ممکن است با یک عدم بررسی، وقوع خطای NPE را افزایش دهید. حالا همین مثال بالا را با همین الگو جلو می‌بریم.

          استفاده از الگو

          ابتدا یک کلاس جدید را با ارث بری از کلاس یوزر می‌سازیم:
          public class NullUser:User
              {
                  public NullUser()
                  {
                      Authenticated = false;
                  }
              }
          این کلاس همان شیء نالی است که قرار است به جای خود عبارت Null برگشت دهیم:
            public User GeUser(string uname)
                  {
                      if (uname == "Ali")
                      {
                          return new User()
                          {
                              Usernam = "Ali",
                              Authenticated = true
                          };
                      }
                      else if (uname == "Reza")
                      {
                          return new User()
                          {
                              Usernam = "Reza",
                              Authenticated = false
                          };
                      }
                          return new NullUser();
                  }
          بدین ترتیب در ادامه کد الزامی به بررسی نال نیست:
          var userServices=new UserServices();
                      var user = userServices.GeUser("xxx");
                      if (user.Authenticated)
                          Console.WriteLine("You are Authorized");

          یک نکته اضافه تراینکه، در صورتی که قصد دارید متدی را در کلاس پدر تحریف کنید، بهتر است یک اینترفیس یا کلاس انتزاعی را تعریف و هر دو کلاس را از آن ارث بری کنید که برای مثال بالا می‌شود اینترفیس IUser و  دو کلاس User و NullUser هم مشتقات آن.

          ‫آموزش Linq - بخش ششم: عملگرهای پرس و جو قسمت چهارم

          $
          0
          0
          عملگر‌های تولید  Generation Operator

          عملگر‌های تولید، برای ما توالی ایجاد می‌کنند و تفاوت‌های عمده‌ای با سایر عملگرهای پرس و جو دارند که در بخش زیر به آنها اشاره می‌کنیم:
           1- هیچ توالی ورودی را دریافت نمی‌کنند.
           2- این عملگر‌ها بصورت متد الحاقی پیاده سازی نشده‌اند و بصورت متد‌های استاتیک در کلاس Enumerable قرار گرفته‌اند.
          امضاء زیر مربوط به متد Empty  می‌باشد:
           public static IEnumerable<TResult> Empty<TResult>()

          Empty

          عملگر Empty یک توالی بدون عنصر (Empty) را بر اساس نوع مشخص شده، ایجاد می‌کند.
          در کد زیر نحوه ایجاد یک توالی خالی از نوع Ingredient نشان داده شده است.
          IEnumerable<Ingredient> ingredients = Enumerable.Empty<Ingredient>();
          Console.WriteLine(ingredients.Count());
          خروجی کد بالا :
           0
          پیاده سازی توسط عبارت‌های جستجو
          معادل عملگر Empty، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          Range
          عملگر پرس و جوی Range، یک توالی از مقادیر صحیح متوالی را برای ما ایجاد می‌کند. اولین پارامتر این عملگر عنصر آغاز کننده توالی است و دومین پارامتر این عملگر تعداد کل عناصر توالی تولید شده، با احتساب عنصر اول خواهد بود.
          مثال:
          IEnumerable<int> fiveToTen = Enumerable.Range(5,6);
          foreach (var num in fiveToTen)
          {
             Console.WriteLine(num);
          }
          خروجی مثال بالا:
          5
          6
          7
          8
          9
          10
          همانطور که ملاحظه کردید مجموعا 6 عنصر برای توالی تولید شدند و اولین عنصر، با عدد 5 آغاز شده است.

          پیاده سازی توسط عبارت‌های جستجو

          معادل عملگر Range، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          Repeat
          عملگر پرس و جوی Repeat یک عدد را به تعداد بار مشخصی در توالی خروجی تکرار می‌کند.
          مثال:
          IEnumerable<int> fiveToTen = Enumerable.Repeat(42, 6);
          foreach (var num in fiveToTen)
          {
              Console.WriteLine(num);
          }
          خروجی مثال بالا:
          42
          42
          42
          42
          42
          42
          پیاده سازی توسط عبارت‌های جستجو
          معادل عملگر Repeat، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.


          عملگرهای  کمی (Quantifier Operators)
          عملگرهای Quantifier یک توالی ورودی را گرفته، آن را ارزیابی کرده و یک مقدار منطقی را باز می‌گردانند.

          عملگر Contains
          عملگر Contains  عناصر یک توالی را ارزیابی می‌کند و در صورتیکه مقدار مورد نظر ما در توالی وجود داشته باشد، ارزش True باز می‌گرداند.
          مثال:
          int[] nums = {1, 2, 3};
          bool isTowThere = nums.Contains(2);
          bool isFiveThere = nums.Contains(5);
          
          Console.WriteLine(isTowThere);
          Console.WriteLine(isFiveThere);
          خروجی مثال بالا :
          True
          False

          پیاده سازی توسط عبارت‌های جستجو

          معادل عملگر Contains، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          عملگر Any
          عملگر Any دو امضاء مختلف را دارد:
           1- اولین امضاء: در صورتیکه توالی شامل حداقل یک عنصر باشد، ارزش True بازگردانده می‌شود.
           2- دومین امضاء: یک عبارت پیش بینی را قبول می‌کند. در صورتیکه حداقل یکی از عناصر توالی، عبارت پیش بینی را تامین کند، ارزش صحیح باز گردانده می‌شود.
          مثال: بررسی امضاء اول عملگر Any
          int[] nums = { 1, 2, 3 };
          IEnumerable<int> noNums = Enumerable.Empty<int>();
          
          Console.WriteLine(nums.Any());
          Console.WriteLine(noNums.Any());
          خروجی مثال بالا:
          True
          False
          مثال: بررسی امضاء دوم عملگر Any
          int[] nums = { 1, 2, 3 };
          bool areAnyEvenNumbers = nums.Any(x => x % 2 == 0);
          Console.WriteLine(areAnyEvenNumbers);
          خروجی مثال بالا:
           True
          در مثال بالا، عبارت پیش بینی مشخص می‌کند که اعداد زوج در توالی وجود داشته باشند یا خیر.

          پیاده سازی توسط عبارت‌های جستجو
          معادل عملگر Any، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          عملگر All
          عملگر پرس و جوی All، یک عبارت پیش بینی را دریافت می‌کند و عناصر توالی ورودی را بر مبنای آن ارزیابی می‌کند تا مشخص شود همه عناصر، شرط پیش بینی را تامین می‌کنند.
          در کد زیر بررسی می‌کنیم که آیا همه عناصر توالی مواد غذایی، جزء مواد غذایی کم چرب می‌باشند یا خیر .
          Ingredient[] ingredients =
          {
          new Ingredient { Name = "Sugar", Calories = 500 },
          new Ingredient { Name = "Egg", Calories = 100 },
          new Ingredient { Name = "Milk", Calories = 150 },
          new Ingredient { Name = "Flour", Calories = 50 },
          new Ingredient { Name = "Butter", Calories = 400 }
          };
          bool isLowFatRecipe = ingredients.All(x => x.Calories < 200);
          Console.WriteLine(isLowFatRecipe);
          خروحی کد بالا :
          False

          نکته : عملگر All به محض پیدا کردن عنصری که شرط مشخص شده را نقض کند، ارزش False را باز می‌گرداند و ادامه بررسی عناصر باقی مانده را متوقف می‌کند.

          پیاده سازی توسط عبارت‌های جستجو
          معادل عملگر Any، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          عملگر SequenceEqual
          عملگر SequenceEqual دو توالی را با هم مقایسه کرده و در صورتیکه عناصر هر دو توالی برابر و ترتیب قرار گیری آنها نیز یکسان باشند، ارزش True باز گردانده می‌شود.
          مثال:
          IEnumerable<int> sequence1 = new[] {1, 2, 3};
          IEnumerable<int> sequence2 = new[] { 1, 2, 3 };
          bool isSeqEqual = sequence1.SequenceEqual(sequence2);
          Console.WriteLine(isSeqEqual);
          خروجی مثال بالا:
           True

          در صورتی که دو توالی عناصر یکسانی داشته باشند، ولی ترتیب قرار گیری عناصر با هم یکسان نباشند، عملگر ارزش False را باز می‌گرداند.
          مثال :
          IEnumerable<int> sequence1 = new[] { 1, 2, 3 };
          IEnumerable<int> sequence2 = new[] { 3, 2, 1 };
          bool isSeqEqual = sequence1.SequenceEqual(sequence2);
          Console.WriteLine(isSeqEqual);
          خروجی مثال بالا:
          False
          پیاده سازی توسط عبارت‌های جستجو
          معادل عملگر SequenceEqual، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.
           
          عملگر‌های تجمیع/تجمعی Aggregate Operators
          عملگرهای Aggregate یک توالی ورودی را دریافت و یک مقدار عددی (Scalar Value) را باز می‌گردانند. مقدار بازگردانده شده، حاصل یک عملیات محاسباتی می‌باشد.
          لیستی از عملگر‌های تجمیع ( Aggregate Operators ):
           • Count
           • LongCount
           • Sum
           • Min
           • Max
           • Average
           • Aggregate

          عملگر Count
          عملگر Count، تعداد عناصر توالی ورودی را باز می‌گرداند. عملگر Count، دو امضاء مختلف دارد. یکی از این امضاء‌ها یک عبارت پیش بینی را می‌پذیرد.
          کد زیر، امضاء اول عملگر Count را نشان می‌دهد:
          int[] nums = { 1, 2, 3 };
          int numberOfElements = nums.Count();
          Console.WriteLine(numberOfElements);
          خروجی کد بالا:
           3

          وقتی عبارت پیش بینی بکار گرفته می‌شود، عملگر Count تنها عناصری را که شرط را تامین کنند، شمارش می‌کند.
          در کد زیر عملگر Count، همه عناصر زوج توالی ورودی را شمارش می‌کند:
          int[] nums = { 1, 2, 3 };
          int numberOfEvenElements = nums.Count(x => x % 2 == 0);
          Console.WriteLine(numberOfEvenElements);
          خروجی کد بالا :
          1

          پیاده سازی توسط عبارت‌های جستجو

          معادل عملگر Count ، کلمه‌ی کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          عملگر LongCount
          این عملگر مثل عملگر Count عمل می‌کند، اما با این تفاوت که خروجی آن به جای نوع int از نوع long می‌باشد. این عملگر برای شمارش توالی‌های  ورودی بسیار بزرگ مورد استفاده قرار می‌گیرد.

          پیاده سازی توسط عبارت‌های جستجو

          معادل عملگر LongCount، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          عملگر Sum

          این عملگر  مجموع تمامی عناصر یک توالی را باز می‌گرداند.
          در کد زیر جمع عناصر یک توالی از نوع int را مشاهده می‌کنید:
          int[] nums = { 1, 2, 3 };
          int total = nums.Sum();
          Console.WriteLine(total);
          خروجی کد بالا :
           6

          عملگر Sum می‌تواند بر روی توالی‌هایی از نوع <IEnumerable<T و بر روی اعضای عددی آنها اعمال شود.
          مثال:
          Ingredient[] ingredients =
          {
          new Ingredient { Name = "Sugar", Calories = 500 },
          new Ingredient { Name = "Egg", Calories = 100 },
          new Ingredient { Name = "Milk", Calories = 150 },
          new Ingredient { Name = "Flour", Calories = 50 },
          new Ingredient { Name = "Butter", Calories = 400 }
          };
          int totalCalories = ingredients.Sum(x => x.Calories);
          Console.WriteLine(totalCalories);
          خروحی مثال بالا :
           1200

          پیاده سازی توسط عبارت‌های جستجو

          معادل عملگر Sum، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          عملگر Average

          این عملگر میانگین عناصر توالی‌های عددی را محاسبه می‌کند.
          مثال:
          int[] nums = { 1, 2, 3 };
          var avg = nums.Average();
          Console.WriteLine(avg);
          خروجی مثلا بالا :
           2

          همانطور که در کد بالا مشاهده می‌کنید، نوع متغیر avg صراحتا مشخص نشده و از نوع var استفاده شده است. تابع average بر اساس توالی ورودی، انواع مختلفی از نوع داده‌های عددی را به خروجی ارسال می‌کند (double,float,decimal).
          همانند عملگر Sum، عملگر Average می‌تواند بر روی اعضای عددی توالی‌هایی که از نوع<IEnumarable<T هستند، اعمال شود.
          مثال:
          Ingredient[] ingredients =
          {
          new Ingredient { Name = "Sugar", Calories = 500 },
          new Ingredient { Name = "Egg", Calories = 100 },
          new Ingredient { Name = "Milk", Calories = 150 },
          new Ingredient { Name = "Flour", Calories = 50 },
          new Ingredient { Name = "Butter", Calories = 400 }
          };
          var avgCalories = ingredients.Average(x => x.Calories);
          Console.WriteLine(avgCalories);
          خروجی مثال بالا :
           240

          پیاده سازی توسط عبارت‌های جستجو

          معادل عملگر Average، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          عملگر Min

          عملگر Min کوچکترین عنصر توالی را باز می‌گرداند.
          مثال:
          int[] nums = { 3, 2, 1 };
          var smallest = nums.Min();
          Console.WriteLine(smallest);
          خروجی مثال بالا:
           1
          امضاء دیگر Min می‌تواند یک عبارت پیش بینی را بپذیرد:
          مثال:
          Ingredient[] ingredients =
          {
          new Ingredient { Name = "Sugar", Calories = 500 },
          new Ingredient { Name = "Egg", Calories = 100 },
          new Ingredient { Name = "Milk", Calories = 150 },
          new Ingredient { Name = "Flour", Calories = 50 },
          new Ingredient { Name = "Butter", Calories = 400 }
          };
          var smallestCalories = ingredients.Min(x => x.Calories);
          Console.WriteLine(smallestCalories);

          پیاده سازی توسط عبارت‌های جستجو
          معادل عملگر Min ، کلمه کلیدی در عبارت‌های جستجو وجود ندارد.ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          عملگر Max
          عملگر Max بزرگترین عنصر توالی را باز می‌گرداند.
          مثال:
          int[] nums = { 1 ,3, 2 };
          var largest = nums.Max();
          Console.WriteLine(largest);
          خروجی مثال بالا:
           3
          همچون عملگر Min، عملگر Max نیز یک امضاء دارد که می‌توان از طریق آن یک عبارت پیش بینی را مشخص کرد.

          پیاده سازی توسط عبارت‌های جستجو

          معادل عملگر Max، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          Aggregate
          عملگر‌های تجمعی که  تا اینجا معرفی شدند، تنها یک کار را انجام می‌دادند. اما عملگر Aggregate امکان تعریف یک پرس و جوی تجمیع سفارشی و پیشرفته‌تر را که بر روی توالی ورودی اعمال می‌شود نیز مهیا می‌کند.
          عملگر Aggregate  دو نسخه دارد:
           1- نسخه‌ای که اجازه استفاده از یک عدد را به عنوان مقدار Seed، به ما می‌دهد (مقدار آغازین یا Seed).
           2- نسخه‌ای که از عنصر ابتدایی توالی به عنوان مقدار Seed استفاده می‌کند.
          هر دو نسخه این عملگر به یک تابع  انباره (accumulator function) جهت نگهداری نتیجه نیاز دارند.
          کد زیر شبیه سازی عملگر Sum  توسط عملگر Aggregate می‌باشد:
          int[] nums = {1, 2, 3};
          var result = nums.Aggregate(0,
          (currentElement, runningTotal) => runningTotal + currentElement);
          Console.WriteLine(result);
          خروجی قطعه کد بالا:
           6
          در قطعه کد بالا، نسخه‌ای از عملگر aggregate استفاده شد که مقدار شروع آن با عدد صفر مقدار دهی اولیه شد‌ه‌است.
          کد زیر شبیه سازی عملیات فاکتوریل را با در نظر گرفتن عنصر اول توالی، به عنوان مقدار Seed نشان می‌دهد:
           int[] nums = { 1, 2, 3 ,4,5};
          var result = nums.Aggregate((runningProduct, nextfactor) => runningProduct * nextfactor);
          Console.WriteLine(result);
          خروجی کد بالا:
           120

          پیاده سازی توسط عبارت‌های جستجو

          معادل عملگر Aggregate، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.

          ‫تاثیر تعداد فونت‌های نصب شده در سیستم بر روی سرعت بارگذاری اولیه برنامه‌ها

          $
          0
          0
          مدتی بود که سرعت آغاز ویژوال استودیو و همچنین تمام برنامه‌های دات نتی موجود، به نحو عجیبی کاهش پیدا کرده بودند. آغاز ویژوال استودیو گاهی تا 3 دقیقه هم طول می‌کشید. تا اینکه آغاز یک برنامه ساده‌ی دات نتی را توسط برنامه‌ی معروف Procmonبررسی کردم:


          بله. همانطور که مشاهده می‌کنید، چون تعداد فونت‌های نصب شده‌ی بر روی سیستم من بیش از اندازه است (1800 فونت)، این مشکل رخ می‌دهد. هر بار آغاز برنامه‌های دات نت، به همراه بررسی تمام فونت‌های نصب شده‌ی بر روی سیستم هم هست و اگر تعداد آن‌ها زیاد باشد، شاید چند دقیقه‌ای این بررسی طول بکشد.

          راه حل‌ها

          الف) حذف فونت‌های اضافی سیستم
          این مورد به طور قطع بر روی سایر برنامه‌های غیردات نتی هم تاثیر مثبت خواهد گذاشت. برای نمونه، این مورد بارگذاری فونت‌ها، در مرورگرها هم صادق است. به علاوه مصرف RAM سیستم را هم کاهش خواهد داد.
          برای حذف فونت‌های اضافی:
          - ابتدا به مسیر C:\Windows\Fonts مراجعه کنید. در لیست فونت‌ها، ابتدا ctrl+a و سپس delete. بله! حذف تمام فونت‌ها، تا جایی که ممکن است.
          - در ادامه ویندوز به صورت توکار، قابلیت بازگشت به لیست ابتدایی سیستمی خود را دارد (جهت ترمیم مواردی که نباید حذف می‌شدند). برای این منظور باید مراحل ذیل را طی کنید:
           Start > Control Panel -> Appearance and Personalization -> Fonts -> Font Settings -> Restore Default Font Settings
          و یا مراجعه‌ی مستقیم به پوشه‌ی C:\Windows\Fonts نیز معادل طی مسیر فوق است:



          با کلیک بر روی دکمه‌ی «Restore Default Font Settings» قلم‌های اصلی ویندوز مجددا نصب خواهند شد و سیستم به حالت اول باز می‌گردد.


          ب) تنظیم سرویس Font Cache ویندوز
          سرویس ویژه‌ای به نام «Windows Presentation Foundation Font Cache 3.0.0.0» در ویندوزهایی که دات نت فریم ورک بر روی آن‌ها نصب است، وجود دارد:


          کار آن کش کردن و به اشتراک گذاشتن اطلاعات قلم‌های نصب شده‌ی بر روی سیستم، بین تمام برنامه‌های WPF در حال اجرا است.
          حالت آغاز این سرویس بر روی manual است. به این معنا که تا یک برنامه‌ی WPF ایی بر روی سیستم اجرا نشود، این سرویس فعال نخواهد شد. می‌توان این حالت آغاز را بر روی automatic قرار داد تا به تمام برنامه‌های WPF سیستم به صورت یکسانی، پیش از اجرای آن‌ها اعمال شود.
          این تغییر توسط مایکروسافت هم توصیه شده‌است: «12. Understand the PresentationFontCache service»


          نتیجه گیری
          اگر آغاز برنامه‌ی دات نتی شما آنچنان سریع نیست، الزاما مشکل از Entity framework نیست. چه تعدادی فونت را نصب کرده‌اید؟!
          Viewing all 1980 articles
          Browse latest View live


          <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>