مطالب
تزریق وابستگی‌ها فراتر از کلاس‌ها در برنامه‌های Angular
عموما تزریق وابستگی‌های کلاس‌ها، در برنامه‌های Angular صورت می‌گیرند. برای مثال در یک NgModule در قسمت providers آن نام کلاسی را معرفی می‌کنیم و سپس می‌توان این کلاس را به سازنده‌ی کامپوننت‌ها تزریق کرد و از امکانات آن استفاده کرد. اما سیستم تزریق وابستگی‌های Angular محدود به تزریق وهله‌های کلاس‌ها نیست و می‌توان قسمت providers را با یک سری شیء تعریف شده‌ی با {} نیز مقدار دهی کرد. در اینجا می‌توان یک token را به یک وابستگی انتساب داد.


انواع providers در Angular

سیستم تزریق وابستگی‌های Angular، تامین کننده‌های ذیل را نیز به همراه دارد:
 - تامین کننده‌ی مقادیر که با useValue مشخص می‌شود.
 - تامین کننده‌ی Factory‌ها که با useFactory تعریف خواهد شد.
 - تامین کننده‌ی کلاس‌ها که با useClass تعریف می‌شود.
 - تامین کننده‌ی کلاس‌هایی با نام‌های مستعار که توسط useExisting مشخص می‌شود.

یک تامین کننده مشخص می‌کند که سیستم تزریق کننده‌ی وابستگی‌ها، با درخواست توکن/کلیدی مشخص، چه وابستگی را باید وهله سازی کند.


تزریق وابستگی‌هایی از نوع ثوابت در برنامه‌های Angular

فرض کنید برنامه‌ی Angular شما در مسیر دیگری نسبت به Web API سمت سرور آن قرار دارد. به همین جهت در تمام سرویس‌های برنامه نیاز به تعریف مسیر پایه‌ی Web API مانند API_BASE_HREF را خواهید داشت. یک روش حل این مساله، تعریف این ثابت به صورت یک وابستگی و سپس تزریق آن به کلاس‌های سرویس‌ها و یا کامپوننت‌های برنامه است:
@NgModule({
  imports: [
    CommonModule,
    InjectionBeyondClassesRoutingModule
  ],
  declarations: [TestProvidersComponent],
  providers: [
    { provide: "API_BASE_HREF", useValue: "http://localhost:5000" },
    { provide: "APP_BASE_HREF", useValue: document.location.pathname },
    { provide: "IS_PROD", useValue: true },
    { provide: "APIKey", useValue: "XYZ1234ABC" },
    { provide: "Random", useValue: Math.random() },
    {
      provide: "emailApiConfig", useValue: Object.freeze({
        apiKey: "email-key",
        context: "registration"
      })
    },
    { provide: "languages", useValue: "en", multi: true },
    { provide: "languages", useValue: "fa", multi: true }
  ]
})
export class InjectionBeyondClassesModule { }
- در اینجا چندین مثال از تکمیل قسمت providers یک ماژول را با شیء‌های token دار provide مشاهده می‌کنید. هر provide یک token را مشخص می‌کند که از آن جهت دریافت مقدار وابستگی منتسب به آن استفاده خواهد شد.
- در این مثال، حالت‌های مختلفی از تامین کننده‌ی useValue را نیز مشاهده می‌کنید. انتساب یک رشته، یک مقدار boolean و یا یک مقدار که در زمان انتساب محاسبه خواهد شد مانند Math.random.
- همچنین در اینجا می‌توان در قسمت useValue مانند emailApiConfig، یک شیء را نیز تعریف کرد. علت استفاده‌ی از Object.freeze، تعریف این شیء به صورت read only است.
- در حین تعریف provideها اگر کلید توکن بکار رفته یکی باشد، آخرین مقدار، مابقی را بازنویسی می‌کند؛ مانند حالت languages که در اینجا دوبار تعریف شده‌است. اما با ذکر خاصیت multi، می‌توان به کلید languages به صورت یک آرایه دسترسی یافت و در این حالت مقادیر آن بازنویسی نمی‌شوند.

اکنون برای استفاده‌ی از این توکن‌های تعریف شده توسط سیستم تزریق وابستگی‌ها، می‌توان به صورت ذیل عمل کرد:
import { Component, OnInit, Inject } from "@angular/core";
import { inject } from "@angular/core/testing";

@Component({
  selector: "app-test-providers",
  templateUrl: "./test-providers.component.html",
  styleUrls: ["./test-providers.component.css"]
})
export class TestProvidersComponent implements OnInit {

  constructor(
    @Inject("API_BASE_HREF") public apiBaseHref: string,
    @Inject("APP_BASE_HREF") public appBaseHref: string,
    @Inject("IS_PROD") public isProd: boolean,
    @Inject("APIKey") public apiKey: string,
    @Inject("Random") public random: string,
    @Inject("emailApiConfig") public emailApiConfig: any,
    @Inject("languages") public languages: string[]
  ) { }

  ngOnInit() {
  }
}
در اینجا هر توکن توسط ویژگی Inject به سازنده‌ی کلاس تزریق شده‌است. از این جهت آن‌ها را public تعریف کرده‌ایم که بتوان در قالب این کامپوننت، به مقادیر تزریق شده، دسترسی یافت:
<h1>
  Injection Beyond Classes
</h1>
<div class="alert alert-info">
  <ul>
    <li>API_BASE_HREF: {{apiBaseHref}}</li>
    <li>APP_BASE_HREF: {{appBaseHref}}</li>
    <li>IS_PROD: {{isProd}}</li>
    <li>APIKey: {{apiKey}}</li>
    <li>Random-1: {{random}}</li>
    <li>Random-2: {{random}}</li>
    <li>emailApiConfig {{emailApiConfig | json}}</li>
    <li>languages: {{languages | json}}</li>
  </ul>
</div>
با این خروجی:


در اینجا همانطور که مشاهده می‌کنید، languages از نوع multi: true به یک آرایه تبدیل شده‌است و یا emailApiConfig نیز یک شیء است که توسط کلیدهای آن می‌توان به مقادیر متناظر آن دسترسی یافت. Random نیز تنها یکبار دریافت شده‌است و مهم نیست که چندبار صدا زده شود؛ همواره مقدار آن مساوی اولین مقداری است که در زمان انتساب دریافت می‌کند.


تزریق تنظیمات برنامه توسط تامین کننده‌ی مقادیر

یک نمونه از تزریق شیء emailApiConfig: any را در مثال فوق ملاحظه کردید. روش بهتر و نوع دار آن به صورت ذیل است. ابتدا یک فایل جدید thismodule.config.ts یا app.config.ts را ایجاد می‌کنیم:
import { InjectionToken } from "@angular/core";

export let APP_CONFIG = new InjectionToken<string>("this.module.config");

export interface IThisModuleConfig {
  apiEndpoint: string;
}

export const ThisModuleConfig: IThisModuleConfig = {
  apiEndpoint: "http://localhost:45043/api/"
};
تاکنون توکن‌های تعریف شده را توسط یک رشته‌ی ثابت مانند "API_BASE_HREF" تعریف کردیم. مشکل این روش، امکان تداخل آن‌ها در یک برنامه‌ی بزرگ است. به همین جهت روش توصیه شده، قرار دادن این کلید داخل یک InjectionToken است تا همواره بتوان به یک توکن منحصربفرد در طول عمر برنامه دست یافت که نمونه‌ی آن‌را در تعریف APP_CONFIG مشاهده می‌کنید. در برنامه اگر دو new InjectionToken، با یک سازنده‌ی یکسان تعریف شوند، با هم مساوی نخواهند بود و توکن نهایی آن منحصربفرد است:
import { InjectionToken } from '@angular/core';
export const EmailService1 = new InjectionToken<string>("EmailService");
export const EmailService2 = new InjectionToken<string>("EmailService");
console.log(EmailService1 === EmailService2); // false

سپس نوع تنظیمات را توسط اینترفیس IThisModuleConfig تعریف کرده‌ایم (که نسبت به استفاده‌ی از any یک پیشرفت محسوب می‌شود). در آخر وهله‌ای از این اینترفیس را به نحوی که مشاهده می‌کنید export کرده‌ایم.

اکنون نحوه‌ی تعریف تزریق وابستگی از نوع IThisModuleConfig در یک NgModule به صورت ذیل است:
import { ThisModuleConfig, APP_CONFIG } from "./thismodule.config";

@NgModule({
  providers: [
    { provide: APP_CONFIG, useValue: ThisModuleConfig }
  ]
})
export class InjectionBeyondClassesModule { }
اینبار توکن تعریف شده توسط InjectionToken مشخص شده‌است و مقدار آن توسط ThisModuleConfig تامین خواهد شد.

در آخر، تزریق آن به سازنده‌ی یک کامپوننت بر اساس توکن APP_CONFIG و از نوع مشخص اینترفیس آن خواهد بود:
import { IThisModuleConfig, APP_CONFIG } from "./../thismodule.config";
@Component()
export class TestProvidersComponent implements OnInit {

  constructor(
    @Inject(APP_CONFIG) public config: IThisModuleConfig
  ) { }

  ngOnInit() {
  }

}


تزریق وابستگی‌ها توسط تامین کننده‌ی Factory ها

تا اینجا useValue را بررسی کردیم. نوع دیگر تامین کننده‌های قابل تعریف، useFactory هستند:
@NgModule({
  providers: [
    // ------ useFactory
    { provide: "BASE_URL", useFactory: getBaseUrl },
    { provide: "RandomFactory", useFactory: randomFactory }
  ]
})
export class InjectionBeyondClassesModule { }

export function getBaseUrl() {
  return document.getElementsByTagName("base")[0].href;
}

export function randomFactory() {
  return Math.random();
}
در اینجا روش استفاده‌ی از useFactory را مشاهده می‌کنید. کار کرد آن با useValue دقیقا یکی است؛ یک توکن را مشخص می‌کنیم و سپس مقداری به آن نسبت داده می‌شود. اما در اینجا می‌توان یک متد را که بیانگر نحوه‌ی تامین این مقدار است نیز مشخص کرد و نسبت به حالت useValue که تنها یک مقدار ثابت و مشخص را دریافت می‌کند، انعطاف پذیری بیشتر دارد و می‌توان منطق سفارشی خاصی را نیز در اینجا پیاده سازی کرد.

روش استفاده‌ی از آن نیز همانند توکن‌های useValue است که توسط ویژگی Inject مشخص می‌شوند:
export class TestProvidersComponent implements OnInit {

  constructor(
    @Inject("BASE_URL") public baseUrl: string,
    @Inject("RandomFactory") public randomFactory: string
  ) { }

حالت useFactory علاوه بر امکان دریافت یک منطق سفارشی توسط یک function، امکان دریافت یک سری وابستگی را نیز دارد. فرض کنید کلاس سرویس خودرو به صورت زیر تعریف شده‌است که دارای وابستگی از نوع HttpClient تزریق شده‌ی در سازنده‌ی آن است:
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";

@Injectable()
export class CarService {

  constructor(private http: HttpClient) { }

}
در این حالت useFactory آن جهت تامین پارامتر سازنده‌ی  new CarService، به همراه متدی خواهد بود که پارامتری از نوع HttpClient را دریافت می‌کند:
import { CarService } from "./car.service";
import { HttpClient } from "@angular/common/http";

@NgModule({
  providers: [
    // ------ useFactory
    { provide: "Car_Service", useFactory: carServiceFactory, deps: [HttpClient] }
  ]
})
export class InjectionBeyondClassesModule { }

export function carServiceFactory(http: HttpClient) {
  return new CarService(http);
}
در اینجا برای تامین این پارامتر سازنده، خاصیت دیگری به نام deps قابل تعریف است که می‌تواند یک یا چند سرویس و وابستگی را تزریق و تامین کند. برای مثال سرویس HttpClient در اینجا توسط deps: [HttpClient] تزریق شده‌است.


تزریق وابستگی‌ها توسط تامین کننده‌ی کلاس‌ها

تا اینجا useValue و useFactory را بررسی کردیم. نوع دیگر تامین کننده‌های قابل تعریف، useClass هستند. در حالت استفاده‌ی useClass، نام یک نوع مشخص می‌شود و سپس Angular وهله‌ای از آن‌را تامین خواهد کرد. در این حالت اگر این وابستگی دارای پارامترهای تزریق شده‌ای در سازنده‌ی آن باشد، آن‌ها نیز به صورت خودکار وهله سازی می‌شوند.
import { CarService } from "./car.service";

@NgModule({
  providers: [
    // ------ useClass
    { provide: "Car_Service_Name1", useClass: CarService },
  ]
})
export class InjectionBeyondClassesModule { }
این حالت دقیقا معادل تعریف متداول سرویس ذیل است؛ با این تفاوت که توکن آن مساوی مقدار سفارشی Car_Service_Name1 است:
import { CarService } from "./car.service";

@NgModule({
  providers: [
        CarService
  ]
})
export class InjectionBeyondClassesModule { }


تزریق وابستگی‌ها توسط تامین کننده‌ی کلاس‌هایی با نام‌های مستعار

چگونه می‌توان دو تامین کننده را برای کلاسی مشابه، با توکن‌هایی متفاوت ایجاد کرد؟ در این حالت از useExisting استفاده می‌شود:
import { CarService } from "./car.service";

@NgModule({
  providers: [
    // ------ useClass
    { provide: "Car_Service_Name1", useClass: CarService },
    // ------ useExisting
    { provide: "Car_Service_Token2", useExisting: "Car_Service_Name1" },
  ]
})
export class InjectionBeyondClassesModule { }
در اینجا CarService توسط دو توکن مختلف در معرض دید قرار گرفته‌است. باید دقت داشت که درخواست "Car_Service_Token2" دقیقا همان وهله‌ی ایجاد شده‌ی توسط توکن "Car_Service_Name1" را بازگشت می‌دهد و وهله‌ی جدیدی در این حالت ایجاد نخواهد شد.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
نظرات مطالب
Repository ها روی UnitOfWork ایده خوبی نیستند
ممنونم جناب نصیری. دلیل اشاره من به عدم تست پذیری قابل قبول در حالت استفاده مستقیم از Context به خاطر وجود دستوراتی نظیر Entry of T یا موارد مربوط به ChangeTracking است که با تست درون حافظه ای نتیجه مطلوب حاصل نمی‌شود، در نتیجه بهتر است از Effort برای تست لایه دسترسی استفاده شود که عملیات را در قالب یک دیتابیس SqlCE تست می‌کند و نسخه Effort.Ef6  آن نیز از Entity Framework 6 به خوبی پشتیبانی می‌کند.
مطالب
یافتن خطاهای متداول کدهای جاوا اسکریپتی با غنی سازی تنظیمات کامپایلر TypeScript
یکی از اهداف مهم استفاده‌ی از TypeScript، یافتن خطاهای متداول کدهای جاواسکریپتی، پیش از اجرای آن‌ها در مرورگر است. برای مثال، قطعه کد زیر:
  defaultChecks() {
    const author = { firstName: "Vahid", lastName: "N" };
    console.log(author.lastname);
    author.lastName.trimStart();
    author.firstName.charCodeAt("1");
  }
دارای سه مشکل است که سریعا توسط TypeScript شناسایی می‌شود:


- خاصیت lastname در شیء author وجود خارجی ندارد.
- نوع رشته‌ای، به همراه متد trimStart نیست.
- متد charCodeAt یک عدد را به عنوان پارامتر قبول می‌کند.

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


نصب افزونه‌ی TSLint در VSCode

جهت مشاهده‌ی بهتر خطاهای کامپایلر TypeScript، پیش از کامپایل نهایی کدها، می‌توان از افزونه‌ی TSLint استفاده کرد. برای نصب آن، ابتدا باید بسته‌ی ذیل را نصب کرد:
  > npm install -g tslint typescript
سپس نیاز است افزونه‌ی آن‌را نیز نصب کنید: https://marketplace.visualstudio.com/items?itemName=eg2.tslint
کار TSLint انجام static code analysis است؛ چیزی شبیه به افزونه‌هایی مانند ری‌شارپر در ویژوال استودیو که راهنماهایی را در مورد بهتر کردن کیفیت کدهای نوشته شده ارائه می‌دهد.


فعالسازی بررسی نال و نوع‌های نال پذیر

 strictNullChecks یکی از مهم‌ترین پرچم‌های تنظیمات کامپایلر تایپ‌اسکریپت است. برای افزودن آن، به فایل tsconfig.json مراجعه کرده و پرچم آن‌را به true تنظیم کنید:
{
    "compilerOptions": {
        "strictNullChecks": true
    }
}
به این ترتیب کامپایلر تایپ‌اسکریپت، بین null ،undefined و سایر نوع‌ها، تفاوت قائل خواهد شد و اگر نوعی نال‌پذیر است، باید آن‌را به صورت صریح type | null تعریف کنید.
برای مثال، متد ذیل را در نظر بگیرید:
  getSessionItem(key: string): any {
    const data = window.sessionStorage.getItem(key);
    return JSON.parse(data);
  }
زمانیکه پرچم strictNullChecks فعال شود، قطعه کد فوق با خطای ذیل کامپایل نخواهد شد:
[ts]
Argument of type 'string | null' is not assignable to parameter of type 'string'.
Type 'null' is not assignable to type 'string'.
const data: string | null
از این جهت که خروجی متد getItem به صورت string | null تعریف شده‌است؛ اما پارامتر متد JSON.parse فقط string را قبول می‌کند. یعنی ممکن است data دریافتی نال باشد که متد JSON.parse قادر به پردازش آن نیست.
برای رفع این مشکل تنها کافی است بررسی کنیم که آیا data نال است یا خیر؟ و اگر خیر، آنگاه آن‌را به متد JSON.parse ارسال کنیم:
  getSessionItem(key: string): any {
    const data = window.sessionStorage.getItem(key);
    if (data) {
      return JSON.parse(data);
    } else {
      return null;
    }
  }
همانطور که ملاحظه می‌کنید، فعالسازی strictNullChecks می‌تواند از بروز بسیاری از خطاهای در زمان اجرای برنامه، جلوگیری کند.


گزارش return‌های فراموش شده

در متد ذیل، یک return فراموش شده وجود دارد و تمام شرط‌های برنامه به یک خروجی مشخص، منتهی نمی‌شوند:
  noImplicitReturns(a: number) {
    if (a > 10) {
      return a;
    }
    // No return in this branch
  }
برای تشخیص زود هنگام یک چنین مشکلاتی می‌توان پرچم noImplicitReturns را در فایل tsconfig.json به true تنظیم کرد:
{
    "compilerOptions": {
        "noImplicitReturns": true
    }
}
پس از این تنظیم، کامپایلر در مورد متد noImplicitReturns، خطای ذیل را صادر می‌کند:
 [ts] Not all code paths return a value.


تشخیص کدهای مرده

قطعه کدی که پس از یک return قرار بگیرد، یک کد مرده نامیده می‌شود. با تنظیم پرچم allowUnreachableCode در فایل tsconfig.json به false، می‌توان کامپایلر TypeScript را وادار کرد تا اینگونه موارد را به عنوان خطا گزارش کند:
{
    "compilerOptions": {
        "allowUnreachableCode": false
    }
}
پس از این فعالسازی، کامپایلر TypeScript دو خطای [ts] Unreachable code detected را در مورد قطعه کد ذیل صادر می‌کند:
    allowUnreachableCode() {
      if (false) {
        console.log("Unreachable code");
      }
      const a = 1;
      if (a > 0) {
        return 10; // reachable code
      }
      return 0;
      console.log("Unreachable code");
    }
مورد اول مربوط به بدنه‌ی if ایی است که شرط آن false است و این بدنه هیچگاه فراخوانی نمی‌شود و مورد دوم، به سطر پس از return آخر مرتبط است که آن مورد نیز یک کد مرده محسوب می‌شود.


تشخیص پارامترها و متغیرهای استفاده نشده

دو متد ذیل را درنظر بگیرید:
  unusedLocals() {
    const a = "foo"; // Error: 'a' is declared but its value is never read
    return "bar";
  }

  unusedParameters(n: number) {
    n = 0; // Never read
  }
در اولی متغیر a تعریف شده‌است، اما هیچگاه استفاده نشده‌است. در متد دوم، پارامتر n نیز هیچگاه خوانده نشده‌است و وجود آن بی‌مصرف است؛ حتی ممکن است نشان فراموش شدن استفاده‌ی از آن و مقدار دهی اشتباه آن باشد.
برای فعالسازی بررسی یک چنین مواردی باید دو پرچم ذیل را در فایل tsconfig.json به true تنظیم کرد:
{
    "compilerOptions": {
        "noUnusedLocals": true,
        "noUnusedParameters": true
    }
}
پس از آن، کامپایلر دو خطای ذیل را در مورد متدهای فوق، گزارش می‌کند:
[ts] 'a' is declared but its value is never read.
[ts] 'n' is declared but its value is never read.


یافتن خواصی که نباید در یک شیء وجود داشته باشند

در مثال ذیل، خاصیت baz در تعاریف اصلی نوع‌های x و y وجود ندارد:
  excessPropertyForObjectLiterals() {
    let x: { foo: number };
    x = { foo: 1, baz: 2 };  // Error, excess property 'baz'
    let y: { foo: number, bar?: number };
    y = { foo: 1, baz: 2 };  // Error, excess property 'baz'
  }
برای فعالسازی یافتن یک چنین مواردی که در بسیاری از حالات ممکن است ناشی از اشتباهات تایپی نیز باشند، می‌توان پرچم suppressExcessPropertyErrors را در فایل tsconfig.json به false تنظیم کرد:
{
    "compilerOptions": {
        "suppressExcessPropertyErrors": false
    }
}
پس از آن برای نمونه در مورد انتساب اول، یک چنین پیام خطایی از طرف کامپایلر TypeScript صادر خواهد شد:
[ts]
Type '{ foo: number; baz: number; }' is not assignable to type '{ foo: number; }'.
Object literal may only specify known properties, and 'baz' does not exist in type '{ foo: number; }'.
(property) baz: number


یافتن breakهای فراموش شده در عبارات switch

در مثال زیر، یک break فراموش شده‌است:
  fallthroughCasesInSwitchStatement(a: number) {
    switch (a) {
      case 0:
        break;

      case 1:
        a += 1;

      case 2:
        a += 2;
        break;
    }
  }
برای تشخیص آن توسط کامپایلر TypeScript باید پرچم noFallthroughCasesInSwitch را در فایل tsconfig.json به true تنظیم کرد:
{
    "compilerOptions": {
        "noFallthroughCasesInSwitch": true
    }
}
پس از آن کامپایلر TypeScript خطای «[ts] Fallthrough case in switch» را در مورد متد فوق صادر می‌کند.


یافتن ایندکس‌های تعریف نشده‌ی در اشیاء

در مثال زیر، شیء x دارای خاصیت b نیست؛ اما دقیقا با این ایندکس مورد استفاده قرار گرفته‌است:
  indexingObjectsLackingIndexSignatures() {
    const x = { a: 0 };
    x["a"] = 1; // ok
    x["b"] = 1; // Error, type '{ a: number; }' has no index signature.
  }
برای تشخیص یک چنین خطاهایی می‌توان پرچم suppressImplicitAnyIndexErrors را در فایل tsconfig.json به false تنظیم کرد:
{
    "compilerOptions": {
        "suppressImplicitAnyIndexErrors": false
    }
}
پس از آن کامپایلر TypeScript خطای ذیل را در مورد دسترسی به ایندکسی که با امضای شیء سازگاری ندارد، صادر می‌کند:
[ts] Element implicitly has an 'any' type because type '{ a: number; }' has no index signature.


اجبار به تعریف صریح نوع‌ها در TypeScript

عمده‌ی قابلیت TypeScript در یافتن خطاها به تعاریف نوع‌ها و راهنمایی کامپایلر آن در این زمینه بر می‌گردد. اما چون این زبان سازگاری کاملی را با JavaScript دارد، تعریف نوع‌ها در آن اجباری نیست و در این حالت اگر نوعی تعریف نشده باشد، به any تفسیر می‌شود. جهت اجبار به تعریف نوع‌ها در TypeScript می‌توان پرچم noImplicitAny را در فایل tsconfig.json به true تنظیم کرد:
{
    "compilerOptions": {
        "noImplicitAny": true
    }
}
در این حالت دیگر قطعه کد ذیل کامپایل نخواهد شد:
  noImplicitAny(args) { // Error: Parameter 'args' implicitly has an 'any' type.
    console.log(args);
  }
برای رفع این مشکل می‌توان نوع args را به صورت صریحی مشخص کرد:
  noImplicitAnyArgs(args: string[]) { // ok with the type information
    console.log(args);
  }


یک نکته‌ی تکمیلی
اگر از دستور ng build --watch برای ساخت برنامه‌های Angular استفاده می‌کنید، تغییرات فوق زمانی تاثیر داده خواهند شد که یکبار این برنامه را بسته و مجددا اجرا کنید.
اشتراک‌ها
معرفی یکی از بهترین سایتهای کار گروهی

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

معرفی یکی از بهترین سایتهای کار گروهی
نظرات مطالب
ASP.NET MVC #6
کسی که دانشی در مورد وب فرم‌ها نداره چطور می‌تونه مثلا ستون‌های یک GridView رو طراحی و بایند کنه؟ برای نمونه یک گزارش رو دربیاره؟ اینجا هم به همین ترتیب. یک ترکیب کلی صفحه وجود دارد که طراح می‌تونه صرفنظر از اینکه کار شما PHP است یا ASP.NET در بیاره. مثلا یک فایل PSD  به شما تحویل بده (روش مرسوم). یک قسمت‌هایی هم باقی خواهد ماند که باید برنامه نویس پرکنه زمانیکه این فایل PSD رو تبدیل به قالب سایت کرد. زمانیکه master page رو درست کرد. المان‌ها رو در جاهای مختلف جایگذاری کرد.
مطالب
جمع آوری آمار لینک‌های خروجی از سایت توسط Google analytics

چندی قبل مطلب کوتاهی را در مورد Google analytics نوشتم. در حین جستجو درباره‌ی jQuery در وب، به نحوه ردیابی لینک‌های خروجی از سایت توسط Google analytics برخوردم که نحوه پیاده سازی آن به صورت زیر است.
بدیهی است قبل از هر کاری باید اسکریپت مربوط به Google analytics را به انتهای صفحه و جایی که تگ body بسته می‌شود اضافه کنید (قابل دریافت درقسمت Add Website Profile . شماره این اسکریپت برای هر پروفایلی که ایجاد می‌کنید متفاوت است).
سپس:
الف) افزودن ارجاعی از کتابخانه jQuery به هدر صفحه که آن‌را در مطلب شمسی کردن تاریخ بلاگر ملاحظه کردید.
ب) افزودن چند سطر زیر به هدر صفحه
<script type="text/javascript">
$(document).ready(function() {
$("a").click(function() {
var $a = $(this);
var href = $a.attr("href");

// see if the link is external
if ( (href.match(/^http/)) && (! href.match(document.domain)) ) {

// if so, register an event
var category = "outgoing";
var event = "click";
var label = href;

pageTracker._trackPageview('/outgoing/' + href);
pageTracker._trackEvent(category, event, href);
}
});
});
</script>

البته اگر قبلا اسکریپت شمسی کردن تاریخ بلاگر را اضافه کرده بودید فقط محتویات تابع document.ready را باید اضافه کنید (جهت مشاهده نمونه اعمال شده، روی صفحه جاری کلیک راست کنید و سورس صفحه را مشاهده نمائید).

توضیحاتی در مورد کد فوق:
این اسکریپت به روال رخ داد گردان onclick هر لینکی که به خارج از سایت ختم می‌شود (مثلا لینک به یک فایل یا یک سایت خارجی (خارج از سایت))، به صورت خودکار تابع trackPageview مربوط به Google analytics را اضافه می‌کند. این کار تاثیری در عملکرد سایت ندارد و کاربر چیزی را متوجه نخواهد شد، اما به این طریق لینک‌های خروجی در آمار Google analytics ظاهر می‌شوند (مطابق تصاویر زیر).





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

امکانات بیشتری مانند event tracking نیز قرار است به Google analytics اضافه شود که هنوز در مرحله آزمایشی است و بر روی تمامی اکانت‌ها فعال نشده است.

نظرات مطالب
خلاصه اشتراک‌های روز شنبه 14 آبان 1390
جالبه! من خیلی جاها خونده بودم که متن مشکی روی پس زمینه سفید از همه خواناتره البته به شرطی که سفیدش مثل مهتابی نباشه. من هم علاقه به تم‌های تیره دارم ولی همه جا خونده بودم که متن باید تیره باشه. واجب شد که تجدید نظر کنم. 

البته یک مشکل دیگه اینه که مثلا پنجره‌های مختلف VS باز هم سفید هستند. یا مثلاً دیاگرام EDMX و ... .

راحی برای تغییر رنگ اونها هست؟ مثلاً پنجره Solution Explorer یا حتی تم Scroll ها!؟
نظرات مطالب
آموزش نصب مک بر روی Virtual Box
روی VirtualBox انجام دادم. میاد بالا همون مرحله اول که کشور رو می‌پرسه. اما نه Keyboard رو می‌شناسه نه Mouse رو. حتی رفتم تو تنظیمات USB ماشین، کیبورد و ماوس رو پیش فرض بهش اضافه کردم. و کیبورد ماوس دیگه‌ای هم به سیستم وصل کردم. (چون وقتی اونا رو پیش فرض میدادم به ماشین دیگه تو سیستم خودم کار نمی‌کردن). ممنون میشم راهنمایی کنین.
نظرات مطالب
ایجاد سیستم وضعیت آب و هوا مانند گوگل (بخش اول)

با سلام؛ در مطلب گفته شده برای گرفتن WOEID شهر‌ها به فلان سایت بروید و هر کشوری رو بخواییم باید بریم و دستی واردش اما من دیتابیس یا هر چیز دیگری رو میخوام که قابلیت این رو داشته باشه  که کاربر با وارد کردن کشور و شهر بتونه آب و هوا رو ببینه. شما میتونید راهنمایی ام کنید که چه کار باید بکنم و یا منبعی برای دیتابیس مختصات جغرافیایی کشورها وجود داره؟ ممنون میشم کمکم کنید. با تشکر

مطالب
Span در C# 7.2
C# 7.2 به همراه تعداد کوچکی از بهبودهای کامپایلر است و با Visual Studio 2017 نگارش 15.5 ارائه شده و روش فعالسازی آن با نگارش 7.1 آن یکی است (انتخاب گزینه‌ی «C# latest minor version (latest)» در تنظیمات پیشرفته‌ی Build خواص پروژه). همچنین اگر از VSCode استفاده می‌کنید، نگارش 1.14 افزونه‌ی #C آن، پشتیبانی کاملی را از C# 7.2 به همراه دارد؛ در اینجا، افزودن خاصیت <LangVersion>latest</LangVersion> به فایل csproj برنامه برای استفاده‌ی از آخرین نگارش کامپایلر نصب شده، کفایت می‌کند. البته باید دقت داشت کامپایلر C# 7.2 به همراه NET Core SDK 2.1.2. ارائه شده‌است. بنابراین تنها نصب آخرین نگارش افزونه‌ی #C مخصوص VSCode برای کامپایل آن کافی نیست و باید حداقل SDK یاد شده (یا نگارش جدیدتر آن) را هم نصب کنید.
 

نوع‌های جدید <Span<T و  <ReadOnlySpan<T در C# 7.2

نوع‌های جدید <Span<T و <ReadOnlySpan<T جهت ارائه‌ی ناحیه‌های اختیاری پیوسته‌ای از حافظه، شبیه به آرایه‌ها تدارک دیده شده‌اند و هدف استفاده‌ی از آن‌ها، تولید برنامه‌های سمت سرور با کارآیی بالا است.
برای کار با این نوع‌ها، هم نیاز به کامپایلر C# 7.2 است و هم نصب بسته‌ی نیوگت System.Memory:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <LangVersion>latest</LangVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="System.Memory" Version="4.4.0-preview1-25305-02" />
  </ItemGroup>
</Project>
این بسته از .NETStandard 1.0. به بعد را پشتیبانی می‌کند؛ یعنی با +NET 4.5+ ،Mono 4.6.  و +NET Core 1.0. سازگار است.


Spanها و امکان دسترسی به انواع حافظه

Spanها می‌توانند به حافظه‌ی مدیریت شده، حافظه‌ی بومی (native) و حافظه‌ی اختصاص داده شده‌ی در Stack اشاره کنند. به عبارتی Spanها یک لایه انتزاعی، برفراز تمام انواع و اقسام حافظه‌هایی هستند که می‌توانند در اختیار توسعه دهندگان NET. باشند.
- البته اکثر توسعه دهندگان دات نت از حافظه‌ی مدیریت شده استفاده می‌کنند. برای مثال Stack memory تنها از طریق کدهای unsafe و واژه‌ی کلیدی stackalloc قابل تخصیص است. این نوع حافظه بسیار سریع است و همچنین بسیار کوچک؛ کمتر از یک مگابایت که به خوبی در CPU cache جا می‌شود. اما اگر در این بین حجم حافظه‌ی تخصیصی بیشتر از یک مگابایت شود، بلافاصله استثنای StackOverflowException غیرقابل مدیریتی را به همراه خاتمه‌ی فوری برنامه به همراه خواهد داشت. برای نمونه از این نوع حافظه در جهت مدیریت رخ‌دادهای داخلی corefx زیاد استفاده می‌شود.
- حافظه‌ی مدیریت شده، همان حافظه‌ای است که توسط واژه‌ی کلیدی new در برنامه، جهت ایجاد اشیاء، تخصیص داده می‌شود و طول عمر آن تحت مدیریت GC است.
- حافظه‌ی مدیریت نشده یا بومی از دید GC مخفی است و توسط متدهایی مانند Marshal.AllocHGlobal و Marshal.AllocCoTaskMem در اختیار برنامه قرار می‌گیرند. این حافظه باید به صورت صریحی توسط توسعه دهنده به کمک متدهایی مانند Marshal.FreeHGlobal و Marshal.FreeCoTaskMem آزاد شود. وب سرور Kestrel مخصوص ASP.NET Core، از این روش جهت کار با آرایه‌های حجیم، جهت کاهش بار GC استفاده می‌کند.

مزیت کار با Spanها این است که دسترسی امن و type safeایی را به انواع حافظه‌های مهیا، جهت توسعه دهندگانی که عموما کدهای unsafe ایی را نمی‌نویسند و با اشاره‌گرها به صورت مستقیم کار نمی‌کنند، میسر می‌کند. برای مثال تا پیش از معرفی Spanها، برای دسترسی به 1000 عنصر یک آرایه‌ی 10 هزار عنصری و ارسال آن به یک متد، نیاز بود تا ابتدا یک کپی از این 1000 عنصر را تهیه کرد. این عملیات از لحاظ میزان مصرف حافظه و همچنین زمان انجام آن، بسیار هزینه‌بر است. با استفاده از <Span<T می‌توان یک دید مجازی از آن آرایه را بدون اختصاص آرایه‌ای و یا آرایه‌هایی جدید، ارائه کرد.


مثالی از کاربرد Spanها جهت کاهش تعداد بار تخصیص‌های حافظه

برای نمونه، متد IsValidName زیر، بررسی می‌کند که طول رشته‌ی دریافتی حداقل 2 باشد و حتما با یک حرف شروع شده باشد:
    static class NameValidatorUsingString
    {
        public static bool IsValidName(string name)
        {
            if (name.Length < 2)
                return false;

            if (char.IsLetter(name[0]))
                return true;

            return false;
        }
    }
در این حالت یک نمونه مثال از استفاده‌ی آن می‌تواند به صورت زیر باشد:
string fullName = "User 1";
string firstName = fullName.Substring(0, 4);
NameValidatorUsingString.IsValidName(firstName);
در اینجا زمانیکه از متد Substring استفاده می‌شود، در حقیقت تخصیص حافظه‌ی دومی جهت تولید firstName رخ می‌دهد.

همچنین اگر این اطلاعات را از طریق شبکه دریافت کرده باشیم، ممکن است به صورت آرایه‌ای از حروف دریافت شوند:
char[] anotherFullName = { 'A', 'B' };
که به صورت مستقیم در متد IsValidName قابل استفاده نیست و خطای عدم امکان تبدیل []char به string، از طرف کامپایلر صادر می‌شود:
NameValidatorUsingString.IsValidName(anotherFullName);
در این حالت برای استفاده‌ی از این آرایه، نیاز است یک تخصیص حافظه‌ی دیگر نیز صورت گیرد:
NameValidatorUsingString.IsValidName(new string(anotherFullName));

اکنون در C# 7.2، بازنویسی این متد توسط ReadOnlySpan، به صورت ذیل است:
    static class NameValidatorUsingSpan
    {
        public static bool IsValidName(ReadOnlySpan<char> name)
        {
            if (name.Length < 2)
                return false;

            if (char.IsLetter(name[0]))
                return true;

            return false;

        }
    }
که این مزایا را به همراه دارد:
ReadOnlySpan<char> fullName = "User 1".AsSpan();
ReadOnlySpan<char> firstName = fullName.Slice(0, 4);
NameValidatorUsingSpan.IsValidName(firstName);
کار با API مربوط به Spanها به همراه تخصیص حافظه‌ی جدیدی نیست. برای نمونه در اینجا متد Slice این API، سبب تخصیص حافظه‌ی جدیدی نمی‌شود (برخلاف متد Substring) و فقط به قسمتی از حافظه‌ی موجود اشاره می‌کند (بدون نیاز به کار مستقیم با اشاره‌گرها و کدهای unsafe).

و یا اینبار امکان استفاده‌ی از آرایه‌ای از کاراکترها، بدون نیاز به تخصیص حافظه‌ای جدید، برای بررسی اعتبار مقادیر دریافتی میسر است:
char[] anotherFullName = { 'A', 'B' };
NameValidatorUsingSpan.IsValidName(anotherFullName);

برای نمونه از یک چنین APIایی در پشت صحنه‌ی کتابخانه‌هایی مانند SignalR و یا Roslyn، برای بالا بردن کارآیی برنامه، با کاهش تعداد بار تخصیص‌های حافظه‌ی مورد نیاز، بسیار استفاده شده‌است. برای نمونه در NET Core 2.1.، حجم رشته‌های تخصیص داده شده‌ی در فریم ورک‌های وابسته، به این ترتیب به شدت کاهش یافته‌است.


مثال‌هایی از کار با API نوع Span

امکان ایجاد یک Span از یک array
var arr = new byte[10];
Span<byte> bytes = arr; // Implicit cast from T[] to Span<T>
پس از آن کار با این span همانند کار با آرایه‌های معمولی است؛ با این تفاوت که این span تنها یک دید مجازی از قسمتی از این آرایه را ارائه می‌دهد؛ بدون سربار تخصیص حافظه‌ی اضافی و کپی اطلاعات:
Span<byte> slicedBytes = bytes.Slice(start: 5, length: 2);
slicedBytes[0] = 42;
slicedBytes[1] = 43;
slicedBytes[2] = 44; // Throws IndexOutOfRangeException
bytes[2] = 45; // OK
در اینجا slicedBytes یک دید مجازی از ایندکس 5 تا 7 آرایه‌ی arr را ارائه می‌دهد. کار کردن با آن نیز همانند آرایه‌ها، توسط ایندکس‌ها میسر است.
همچنین تغییرات بر روی Span (غیر read only) بر روی آرایه‌ی اصلی نیز تاثیر گذار است. برای مثال در اینجا با تغییر bytes[2]، مقدار arr[2] نیز تغییر می‌کند.

و یا روش دیگر ایجاد Span استفاده از متد AsSpan است:
var array = new byte[100];
Span<byte> interiorRef1 = array.AsSpan().Slice(start: 20);
همین عملیات را توسط new Span نیز می‌توان به صورت ساده‌تری ارائه داد:
Span<byte> interiorRef2 = new Span<byte>(array: array, start: 20, length: array.Length - 20);


محدودیت‌های کار با Spanها

- Span تنها یک نوع stack-only است.
- Spanها در بین تردها به اشتراک گذاشته نمی‌شوند. هر استک در یک زمان تنها توسط یک ترد قابل دسترسی است. بنابراین Spanها thread-safe هستند.
- طول عمر Spanها کوتاه است و قابلیت قرارگیری بر روی heap با طول عمر بیشتر را ندارند؛ یعنی:
  • به صورت فیلد در یک نوع non-stackonly قابل تعریف نیستند:
class Impossible
{
   Span<byte> field;
}
فیلدهای یک کلاس در heap ذخیره می‌شوند. بنابراین محل ذخیره سازی spanها نیستند.
  • به عنوان پارامترهای متدهای async قابل استفاده نیستند. چون در این بین در پشت صحنه یک AsyncMethodBuilder تشکیل می‌شود که در قسمتی از آن، پارامترها بر روی heap قرار می‌گیرند.
  • هرجائیکه عملیات boxing صورت گیرد، نتیجه‌ی عملیات بر روی heap قرار می‌گیرد. بنابراین در یک چنین مواردی نمی‌توان از Spanها استفاده کرد. برای مثال تعریف Func<T> valueProvider و سپس فراخوانی ()valueProvider.Invoke به همراه یک boxing است. بنابراین نمی‌توان از spanها به عنوان نوع آرگومان جنریک استفاده کرد. این مورد هرچند کامپایل می‌شود، اما در زمان اجرا سبب خاتمه‌ی برنامه خواهد شد و یا نمونه‌ی دیگر، عدم امکان دسترسی به آن‌ها توسط reflection invoke APIs است که سبب boxing می‌شود.


معرفی نوع <Memory<T

با توجه به محدودیت‌های Span و خصوصا اینکه به عنوان پارامتر متدهای async قابل استفاده نیست (چون بر روی stack ذخیره می‌شوند)، نوع دیگری به نام <Memory<T نیز به همراه C# 7.2 ارائه شده‌است. البته این نوع هنوز به بسته‌ی نیوگت فوق اضافه نشده‌است و به همراه ارائه نهایی NET Core 2.1. ارائه خواهد شد.
این نوع، محدودیت <Span<T را نداشته و قابلیت ذخیره سازی بر روی heap را دارا است.
static async Task<int> ChecksumReadAsync(Memory<byte> buffer, Stream stream)
{
   int bytesRead = await stream.ReadAsync(buffer);
   return Checksum(buffer.Span.Slice(0, bytesRead));
   // Or buffer.Slice(0, bytesRead).Span
}
در اینجا نیز می‌توان از یک آرایه، یک <Memory<T را ایجاد و سپس یک <Span<T را از آن دریافت و با Sliceهای آن کار کرد.