مطالب
تزریق وابستگی‌ها فراتر از کلاس‌ها در برنامه‌های 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" را بازگشت می‌دهد و وهله‌ی جدیدی در این حالت ایجاد نخواهد شد.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
اشتراک‌ها
قالب (Template) پروژه های .NET Boxed

یک Project Template برای سرویس‌ها با مجموعه ای از تنظیمات انجام شده و ضروری و البته قابل انتخاب. جهت تسریع در توسعه سرویس ها. با امکان ارائه رابط GraphQL.


قالب (Template) پروژه های .NET Boxed
نظرات نظرسنجی‌ها
کدامیک از سرویس دهنده‌های ابری زیر را پیشنهاد می‌کنید
ممنون میشم دوستانی که از این سرویس‌ها استفاده کردن بیشتر سعی کنن در مورد کیفیت و پشتیبانیشون  و در کل تجربه ای که با این سرویس دهنده‌ها داشتن بنویسن.
مطالب
توسعه سرویس‌های مبتنی بر REST در AngularJs با استفاده از RestAngular : بخش دوم
در بخش پیشین  کلیات کتابخانه‌ی Restangular  را بررسی کردیم. در این بخش قصد داریم تا در طی یک پروژه، امکانات و قابلیت‌های بی‌نظیر این سرویس را در یک پروژه‌ی واقعی مشاهده کنیم.

کلیات پروژه

در این پروژه قصد داریم تا لیست کتاب‌های یک کتابخانه را نمایش دهیم. این کتابها قابلیت ویرایش نام دارند و همچنین شما می‌توانید کتابهای جدیدی را به لیست کتابها اضافه نمایید. تصویر زیر خروجی این پروژه است:

پایگاه داده‌ی برنامه با نام LibDb درون پوشه‌ی app_data قرار داده شده‌است. این پایگاه داده تنها دارای یک جدول است؛ با نام Books که دیاگرام آن را در شکل زیر مشاهده می‌کنید:


پیاده سازی

در ابتدا یک پروژه‌ی Empty را با رفرنس web API ایجاد می‌کنیم. حال درون فایل WebApiConfig.cs تکه کد زیر را اضافه می‌کنیم. این تکه کد فیلدها و آبجکت‌هایی را که از سمت سرور بازگشت داده می‌شوند، به صورت camelCase تبدیل می‌کند.
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();

jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
در ادامه مدل edmx را ساخته و پس از آن Books Web Api را درون پوشه‌ی کنترلر ایجاد می‌کنیم. این کنترلر به صورت Default web Api Scaffold ساخته شده است و به دلیل اینکه به بحث ما مرتبط نیست؛ از توضیح بیشتر آن می‌گذریم.
حال پوشه‌ای را با نام app برای نگه داری فایل‌های AngularJs اضافه می‌کنیم و درون آن فایل app.js را ایجاد می‌کنیم:
var angularExample = angular.module('angularexample', ["restangular"])
angularExample.config(["RestangularProvider", function (RestangularProvider) {
    //RestangularProvider.setRestangularFields({
    //    id: "id"
    //});
    RestangularProvider.setBaseUrl('/api');
}]);

angularExample.controller("MainCtrl", ["Restangular", "$scope", function (Restangular, $scope) {
    var resource = Restangular.all('books');
    resource.getList().then(function (response) {
        $scope.books = response;
    });
    $scope.add = function () {
        resource.post($scope.newBook).then(function (newResource) {
            $scope.books.push(newResource);
        })
    }
}]);
محتویات فایل app.js را مشاهده می‌کنید. در ادامه درباره‌ی این قسمت بیشتر صحبت می‌کنیم.
حال در روت پروژه فایل index.html را ایجاد می‌کنیم:
<!DOCTYPE html>
<html ng-app="angularexample" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Rest Angular Sample</title>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
    <script src="http://cdn.jsdelivr.net/underscorejs/1.5.1/underscore-min.js"></script>
    <script src="/Scripts/restangular.min.js"></script>
    <script src="/app/app.js"></script>
</head>
<body>
    <div ng-controller="MainCtrl">
        <div ng-repeat="item in books">
            name is: {{item.name}}<br />
            change name: <input type="text" ng-model="item.name" /><button type="submit" ng-click="item.put();">update</button>
        </div>
        <div>
            add new: <br />
            <input type="text" ng-model="newBook.name" /><button type="submit" ng-click="add()">add</button>
        </div>
    </div>
</body>
و در نهایت در پوشه‌ی Scripts فایل‌های سرویس Restangular را قرار می‌دهیم.
تا به اینجای کار تمامی کارهای مورد نیاز تشکیل پروژه را انجام داده‌ایم. حال به بررسی بیشتر سرویس‌های این پروژه می‌پردازیم؛ یعنی کدهای درون فایل app.js.
ببینیم که برای دریافت لیست تمامی کتابها، ما چه کارهایی را انجام داده‌ایم! به تکه کد زیر دقت کنید:
    var resource = Restangular.all('books');
    resource.getList().then(function (response) {
        $scope.books = response;
    });
این تمامی آن چیزی است که ما برای دریافت لیست تمامی کتابها انجام داده‌ایم. به همین سادگی! در خط اول از متد all که در بخش قبل توضیح مختصری راجع به آن داده بودم برای دریافت لیست تمامی کتابها استفاده کردم که پارامتر درون آن آدرس Web Api است. البته همانطور که می‌دانید در ASP.NET Web Api ما همیشه به base address یک api نیز اضافه می‌کنیم. حال اینکه چرا api در اینجا نیامده در ادامه و در بخش تنظیمات کلی Restangular توضیح می‌دهم. در خط دوم نیز از اشاره‌گر resources متد getList را فراخوانی کرده و لیست کتابها را در response دریافت می‌کنیم.
پس از آن می‌خواهیم ببینیم که عملیات ایجاد یک کتاب جدید چگونه انجام می‌گردد. تکه کد زیر این عملیات را انجام می‌دهد:
    $scope.add = function () {
        resource.post($scope.newBook).then(function (newResource) {
            $scope.books.push(newResource);
        })
    }
ما یک متد با نام add ایجاد کرده‌ایم که در سمت View توسط دایرکتیو ng-click آن را فراخوانی می‌کنیم.
همانطور که مشاهده میکنید درون app.js متدی برای update موارد قبلی نیست. بیایید سری به View بزنیم. به المنت div زیر توجه کنید. همانطور که می‌بینید تمامی عملیات update با یک دستور ساده item.put حل و فصل شده.
        <div ng-repeat="item in books">
            name is: {{item.name}}<br />
            change name: <input type="text" ng-model="item.name" /><button type="submit" ng-click="item.put();">update</button>
        </div>
تمامی آنچه که گفته شد تنها بخشی از قابلیت‌های شگفت انگیز این افزونه بود. امیدوارم که مطلب مفید واقع شده باشد. سورس این پروژه را می‌توانید از لینک زیر دریافت کنید.

سورس پروژه: RestangularSample.rar  

نظرات مطالب
SignalR
سلام.
آیا  این امکان وجود داره که SingnalR   رو  با  RestFull سرویس  در سمت سرور  ترکیب کنم   و  از  سرویس در سمت کلاینت ، داخل یک  برنامه  WPF   استفاده کتم؟
مطالب
پَرباد - راهنمای اتصال و پیاده‌سازی درگاه‌های پرداخت اینترنتی (شبکه شتاب)

پَرباد چیست؟

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


آنچه که شما در این مطلب یاد خواهید گرفت:

  • طریقه نصب
  • ایجاد صورتحساب و ارسال کاربر به درگاه پرداخت
  • تایید صورتحساب
  • مردود کردن صورتحساب قبل از انتقال وجه از مشتری به فروشنده
  • برگشت وجه به حساب مشتری پس از تأیید صورتحساب
  • درگاه مجازی پرداخت (برای تست وب اپلیکیشن، بدون داشتن حساب واقعی در درگاه‌های بانکی)
  • تنظیمات
  • ذخیره سازی اطلاعات پرداخت


طریقه نصب

PM> Install-Package Parbad

برای وب سایت‌های بر پایه فریم ورک MVC

PM> Install-Package Parbad.MVC5


ایجاد صورتحساب و ارسال کاربر به درگاه پرداخت

ابتدا یک شیٔ صورتحساب را به صورت زیر ایجاد کنید
var invoice = new Invoice( [Order Number], [Amount], [Verify URL]);

- Order Number شماره صورتحساب است و باید همیشه یک عدد یکتا باشد (تکراری نباشد).
- Amount مبلغ قابل پرداخت به ریال است.
- Verify URL یک آدرس در وب سایت شما، برای بازگشت مشتری پس از پرداخت و تأیید صورتحساب است.
برای مثال:
var invoice = new Invoice(1, 30000, "http://www.mywebsite.com/payment/verify" );
سپس صورتحساب را به درگاه مورد نظر ارسال میکنیم.
var result = Payment.Request(Gateways.Mellat, invoice);

شیٔ result حاوی شماره یکتا رجوع و وضعیت درخواست (موفقیت یا عدم موفقیت درخواست) است.
if (result.Status == RequestResultStatus.Success)
{
    // این متد، کاربر را به سمت وب سایت درگاه پرداخت هدایت میکند
    result.Process(Context);
}
else
{
    // در صورت تمایل می‌توانید پیغام مورد نظر از درگاه پرداخت را نمایش دهید
    var msg = result.Message;
}

در وب سایت‌های MVC می‌توانید به روش زیر عمل کنید

if (result.Status == RequestResultStatus.Success)
{
   // کاربر را به سمت وب سایت درگاه پرداخت هدایت میکند 
   return new RequestActionResult(result);
}
else
{
   return View("Error");
}


تأیید صورتحساب

پس از بازگشت کاربر از وب سایت بانک، باید از پرداخت صورتحساب توسط کاربر اطمینان حاصل کنید. کد زیر را باید در آدرسی که هنگام ساخت صورتحساب ذکر کرده بودید، قرار دهید.
var result = Payment.Verify(System.Web.HttpContext.Current);

شیٔ result در اینجا حاوی اطلاعاتی مانند: درگاه بانکی (که کاربر در آن صورتحساب را پرداخت کرده)، شماره رجوع، شماره تراکنش یکتای بانکی، وضعیت پرداخت و پیام درگاه است.
شما می‌توانید با بررسی این شیٔ، تصمیمات لازم را بگیرید.
if(result.Status == VerifyResultStatus.Success)
{
    // کاربر، صورتحساب را پرداخت کرده است و شما میتوانید ادامه عملیات خرید را انجام دهید
}
else
{
    // کاربر بنا به دلایلی صورتحساب را پرداخت نکرده است
    // شما همچنین میتوانید علت را در قالب یک پیام از پراپرتی پیام مشاهده کنید

    // بنابراین شما میتوانید این صورتحساب را در پایگاه داده خود مردود اعلام کنید
}


مردود کردن صورتحساب قبل از انتقال وجه از مشتری به فروشنده

در بعضی شرایط، پس از پرداخت صورتحساب توسط مشتری، شما متوجه می‌شوید که باید عملیات را لغو کنید.  
سناریو زیر را در نظر بگیرید:
در زمانیکه مشتری در وب سایت بانکی، صورتحساب را پرداخت میکرده است،  موجودی کالای خریداری شده توسط او در فروشگاه شما، به پایان رسیده ! حال باید این وجه پرداخت شده را فورا مردود اعلام کنید.
برای این منظور متد تأیید صورتحساب را به روش زیر بازنویسی کنید



همانطور که در تصویر می‌بینید، در هنگام بازگشت مشتری به وب سایت شما و تأیید کردن صورتحساب، شما می‌توانید اطلاعات تراکنش مورد نظر را که شامل، درگاه پرداخت بانکی، شماره سفارش و شماره رجوع است را دریافت کنید و سپس با استفاده از این اطلاعات، پایگاه داده خود را بررسی کرده و در صورت لزوم، متد Cancel را فراخوانی کنید. به این ترتیب به درگاه بانکی، هیچگونه تأییدیه ای اعلام نمی‌شود و این بدان معناست که اگر وجهی به حساب فروشگاه واریز شده باشد، پس از چند دقیقه (معمولا ۱۵ دقیقه) به حساب مشتری برگشت داده خواهد شد.


برگشت وجه به حساب مشتری پس از تأیید صورتحساب

var refundResult = Payment.Refund(new RefundInvoice([Order Number], [Amount]));
در اینجا، Order Number همان شماره سفارش صورتحساب و Amount مقداری از وجه و یا کل وجه برای برگشت به حساب مشتری است.
پس از آن شما می‌توانید نتیجه این عملیات را در شیٔ refundResult بررسی کنید.


درگاه مجازی پرداخت

درصورتیکه شما نیاز به تست عملکرد اپلیکیشن خود داشته باشید، نیازی به داشتن یک حساب واقعی در بانک‌های اینترنتی ندارید و می‌توانید اپلیکیشن خود را با یک درگاه مجازی بسیار ساده تست کنید. برای انجام این کار در هنگام ارسال صورتحساب، از میان درگاه‌های بانکی، درگاه مجازی پَرباد را انتخاب کنید.
var result = Payment.Request(Gateways.ParbadVirtualGateway, invoice);


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

اما قبل از کار با درگاه مجازی باید در فایل web.config وب اپلیکیشن خود، تنظیمات زیر را قرار دهید:
<system.webServer>
  <handlers>
   <add name="ParbadGatewayPage" verb="*" path="Parbad.axd" type="Parbad.Web.Gateway.ParbadVirtualGatewayHandler" />
  </handlers>
</system.webServer>
در اینجا، درگاه مجازی به عنوان یک HttpHandler معرفی شده است. مقداری که در مشخصه path ذکر شده، در واقع آدرس درگاه مجازی است که شما می‌توانید به دلخواه خود آن را وارد کنید. ما در این مثال از آدرس parbad.axd استفاده کرده ایم.
و در نهایت در وب اپلیکیشن خود، مسیر ذکر شده را به صورت زیر معرفی کنید:
ParbadConfiguration.Gateways.ConfigureParbadVirtualGateway(new ParbadVirtualGatewayConfiguration("Parbad.axd"));
در نتیجه در هنگام هدایت کاربر به درگاه مجازی، شما باید در نوار آدرس مرورگر خود، مقداری را که تنظیم کرده اید مشاهده کنید.


نکته مهم: فراموش نکنید، قبل از انتشار نهایی وب سایت بر روی سرور (نمایش عمومی)، تنظیمات HttpHandler مربوط به این درگاه مجازی را از درون فایل web.config حذف کنید. بدین صورت، این درگاه از دسترس عموم خارج خواهد بود.

تنظیمات پَرباد

بهترین مکان برای درج این تنظیمات در اپلیکیشن‌های ASP.NET WebForms فایل Global.asax.cs و در اپلیکیشن‌های ASP.NET MVC فایل Startup.cs است.
ASP.NET Web Forms
public class Global : HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        // configurations
    }
}

ASP.NET MVC
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // configurations
    }
}

تنظیمات درگاه‌های پرداخت

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


تنظیمات ذخیره سازی اطلاعات پرداخت

پَرباد برای ذخیره و بازیابی اطلاعات پرداخت، نیاز به یک منبع ذخیره سازی دارد.
منبع پیش فرض پَرباد، کلاس TemporaryMemoryStorage است که همانطور که از نام آن پیداست، اطلاعات را به صورت موقت در حافظه رَم سرور ذخیره میکند. اگر شما خودتان اطلاعات پرداخت را در پایگاه داده ذخیره میکنید، این منبع، گزینه مناسبی است به دلیل سرعت بسیار بالای حافظه رَم.
توجه: در نظر داشته باشید که اگر به هر دلیلی سرور و یا وب سایت شما، ری‌استارت شود، کلیه اطلاعات موجود در این منبع هم از بین خواهد رفت.
ذخیره و بازیابی توسط SQL Server
برای این منظور در قسمت تنظیمات، کد زیر را قرار داده و رشته اتصال و نام جدول پرداخت را معرفی کنید.
ParbadConfiguration.Storage = new SqlServerStorage("Connection String", "MyPaymentTableName");

فیلد‌های مورد نیاز در این جدول:

ذخیره و بازیابی اطلاعات توسط روش مورد نظر شما:
در صورتیکه مایلید ذخیره و بازیابی را به روش خود انجام دهید، کلاس Storage را پیاده سازی کنید
public class MyStorage : Storage
{
    // Implement methods here...
}

و کلاس مورد نظر را در تنظیمات به عنوان منبع، معرفی کنید.
ParbadConfiguration.Storage = new MyStorage();

لازم به ذکر است که این کلاس شامل متد‌های synchronous و همچنین asynchronous است. بنابراین در صورتیکه برای مثال در هنگام ارسال درخواست به بانک، از متد‌های async استفاده می‌کنید، نیازی به پیاده سازی کردن متد‌های synchronous نیست.
در صورتیکه هر گونه پیشنهاد یا انتقاد نسبت به کارکرد این سیستم دارید، صمیمانه منتظر شنیدن آن در راستای توسعه این سیستم هستم.
همچنین در صورت تمایل به توسعه آن، می‌توانید آن را در گیت هاب دنبال کنید و یا لطفا پیشنهادات، بحث‌ها و نظرات خود را در صفحه مخصوص این پروژه ارسال کنید. 
با تشکر.
مطالب
SignalR - قسمت دوم
در قسمت اول بحث‌های مقدماتی درباره وب زمان واقعی (real time web) و معرفی کتابخونه SignalR به همراه یک مثال ساده رو با هم دیدیم. در ادامه به جزئیات ریزی از کتابخونه SignalR که توسط آقای David Fowler توسعه داده میشه میپردازم.

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

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

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

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

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

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

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

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

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

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

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

مطالب
یکپارچه کردن ELMAH با WCF RIA Services

پیشتر در مورد ELMAH مطلبی را منتشر کرده بودم و اگر برنامه نویس ASP.NET هستید و با ELMAH آشنایی ندارید،‌ جدا نیمی از عمر کاری شما بر فنا است!
هاست پیش فرض یک WCF RIA Service هم یک برنامه‌ی ASP.NET است. بنابراین کلیه‌ی خطاهای رخ داده در سمت سرور را باید بتوان به نحوی لاگ کرد تا بعدا با مطالعه‌ی آن‌ها اطلاعات ارزشمندی را از نقایص برنامه در عمل و پیش از گوشزد شدن آن‌ها توسط کاربران، دریافت، بررسی و رفع کرد.
کلیه خطاها را لاگ می‌کنم تا:
- بدانم معنای جمله‌ی "برنامه کار نمی‌کنه" چی هست.
- بدون روبرو شدن با کاربران یا حتی سؤال و جوابی از آن‌ها بدانم دقیقا مشکل از کجا ناشی شده.
- بدانم رفتارهای عمومی کاربران که منجر به بروز خطا می‌شوند کدام‌ها هستند.
- بدانم در کدامیک از قسمت‌های برنامه تعیین اعتبار ورودی کاربران یا انجام نشده یا ضعیف و ناکافی است.
- بدانم زمانیکه دوستی (!) قصد پایین آوردن برنامه را با تزریق SQL داشته، دقیقا چه چیزی را وارد کرده، در کجا و چه زمانی؟
- بتوانم Remote worker خوبی باشم.

ELMAH هم برای لاگ کردن خطاهای مدیریت نشده‌ی یک برنامه‌ی ASP.NET ایجاد شده است. بنابراین باید بتوان این دو (WCF RIA Services و ELMAH) را به نحوی با هم سازگار کرد. برای اینکار نیاز است تا یک مدیریت کننده‌ی خطای سفارشی را با پیاده سازی اینترفیس IErrorHandler تهیه کنیم (تا خطاهای مدیریت نشده‌ی حاصل را به سمت ELMAH هدایت کند) و سپس آن‌را به کمک یک ویژگی یا Attribute به DomainService خود جهت لاگ کردن خطاها اعمال نمائیم. روش تعریف این Attribute را در کدهای بعد ملاحظه خواهید نمود (در اینجا نیاز است تا دو ارجاع را به اسمبلی‌های Elmah.dll که دریافت کرده‌اید و اسمبلی استاندارد System.ServiceModel نیز به پروژه اضافه نمائید):

//add a reference to "Elmah.dll"
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Web;

namespace ElmahWcf
{
public class HttpErrorHandler : IErrorHandler
{
#region IErrorHandler Members
public bool HandleError(Exception error)
{
return false;
}

public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (error == null)
return;

if (HttpContext.Current == null) //In case we run outside of IIS
return;

Elmah.ErrorSignal.FromCurrentContext().Raise(error);
}
#endregion
}
}

//add a ref to "System.ServiceModel" assembly
using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace ElmahWcf
{
public class ServiceErrorBehaviorAttribute : Attribute, IServiceBehavior
{
Type errorHandlerType;
public ServiceErrorBehaviorAttribute(Type errorHandlerType)
{
this.errorHandlerType = errorHandlerType;
}

#region IServiceBehavior Members

public void AddBindingParameters(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{ }

public void ApplyDispatchBehavior(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandler;
errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;
cd.ErrorHandlers.Add(errorHandler);
}
}

public void Validate(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{ }
#endregion
}
}
اکنون پس از تعریف ویژگی ServiceErrorBehavior، نوبت به اعمال آن می‌رسد. به فایل DomainService خود مراجعه کرده و یک سطر زیر را به آن اضافه نمائید:
    [ServiceErrorBehavior(typeof(HttpErrorHandler))] //Integrating with ELMAH
[EnableClientAccess()]
public partial class MyDomainService : LinqToEntitiesDomainService<myEntities>

در ادامه نحوه‌ی افزودن تعاریف متناظر با ELMAH به Web.Config برنامه ذکر شده است. این تعاریف برای IIS6 و 7 به بعد هم تکمیل گردیده است. خطاها هم به صورت فایل‌های XML در پوشه‌ای به نام Errors که به ریشه‌ی سایت اضافه خواهید نمود (یا هر پوشه‌ی دلخواه دیگری)، لاگ می‌شوند.
به نظر من این روش، از ذخیره سازی اطلاعات لاگ‌ها در دیتابیس بهتر است. چون اساسا زمانیکه خطایی رخ می‌دهد شاید مشکل اصلی همان ارتباط با دیتابیس باشد.
قسمت ارسال خطاها به صورت ایمیل نیز comment شده است که در صورت نیاز می‌توان آن‌را فعال نمود:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="elmah">
<section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah"/>
<section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
<section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
<section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah"/>
<section name="errorTweet" requirePermission="false" type="Elmah.ErrorTweetSectionHandler, Elmah"/>
</sectionGroup>
</configSections>

<elmah>
<security allowRemoteAccess="1" />
<errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/Errors" />
<!-- <errorMail
from="errors@site.net"
to="nasiri@site.net"
subject="prj-error"
async="true"
smtpPort="25"
smtpServer="mail.site.net"
noYsod="true" /> -->
</elmah>

<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
<add name="DomainServiceModule"
preCondition="managedHandler"
type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</modules>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add name="Elmah" verb="POST,GET,HEAD" path="myelmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
</handlers>
</system.webServer>
<system.web>
<globalization
requestEncoding="utf-8"
responseEncoding="utf-8"
/>
<authentication mode="Forms">
<!--one month ticket-->
<forms name=".403AuthV"
cookieless="UseCookies"
slidingExpiration="true"
protection="All"
path="/"
timeout="43200" />
</authentication>
<httpHandlers>
<add verb="POST,GET,HEAD" path="myelmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
</httpHandlers>
<httpModules>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
<add name="DomainServiceModule"
type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</httpModules>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</assemblies>
</compilation>
</system.web>
<connectionStrings>
</connectionStrings>
<system.serviceModel>
<serviceHostingEnvironment
aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
</system.serviceModel>
</configuration>
اکنون برای مثال به یکی از متدهای DomainService خود سطر زیر را اضافه کرده و برنامه را آزمایش کنید:
throw new Exception("This is an ELMAH test");

سپس به آدرس http://localhost/myelmah.axd مراجعه نموده و اطلاعات لاگ شده حاصل را بررسی کنید:


این روش با WCF Services های متداول هم کار می‌کند. فقط در این سرویس‌ها باید aspNetCompatibilityEnabled مطابق تگ‌های ذکر شده‌ی system.serviceModel فوق در web.config لحاظ شوند (این مورد به صورت پیش فرض در WCF RIA Services وجود دارد). همچنین ویژگی زیر نیز باید به سرویس شما اضافه گردد:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

منابع مورد استفاده:
Integrating ELMAH for a WCF Service
Making WCF and ELMAH play nice together
Getting ELMAH to work with WCF services



پ.ن.
اگر به خطاهای ASP.NET دقت کرده باشید که به yellow screen of death هم مشهور هستند (در مقابل صفحات آبی ویندوز!)، ابتدای آن خیلی بزرگ نوشته شده Server Error و سپس ادامه‌ی خطا. همین مورد دقیقا یادم هست که هر بار سبب بازخواست مدیران شبکه بجای برنامه نویس‌ها می‌شد! (احتمالا این هم یک نوع بدجنسی تیم ASP.NET برای گرفتن حال ادمین‌های شبکه است! و گرنه مثلا می‌توانستند همان ابتدا بنویسند program/application error بجای server error)

مطالب
آشنایی با CLR: قسمت هشتم
در قسمت پنجم در مورد ابزار Ngen کمی صحبت کردیم و در این قسمت هم در مورد آن صحبت هایی خواهیم کرد. گفتیم که این ابزار در زمان نصب، اسمبلی‌ها را کامپایل می‌کند تا در زمان اجرا JIT وقتی برای آن نگذارد. این کار دو مزیت به همراه دارد:
  1. بهینه سازی زمان آغاز به کار برنامه
  2. کاهش صفحات کاری برنامه: از آنجا که برنامه از قبل کامپایل شده، فراهم کردن صفحه بندی از ابتدای کار امر چندان دشواری نخواهد بود؛ لذا در این حالت صفحه بندی حافظه به صورت پویاتری انجام می‌گردد. شیوه‌ی کار به این صورت است که اسمبلی‌ها به چندین پروسه‌ی کاری کوچک‌تر تبدیل شده تا صفحه بندی هر کدام جدا صورت گیرد و محدوده‌ی صفحه بندی کوچکتر می‌شود. در نتیجه کمتر نقصی در صفحه بندی دیده شده یا کلا دیده نخواهد شد. نتیجه‌ی کار هم در یک فایل ذخیره می‌گردد که این فایل می‌تواند نگاشت به حافظه شود تا این قسمت از حافظه به طور اشتراکی مورد استفاده قرار گیرد و بدین صورت نیست که هر پروسه‌ای برای خودش قسمتی را گرفته باشد.
موقعی که اسمبلی، کد IL آن به کد بومی تبدیل می‌شود، یک اسمبلی جدید ایجاد شده که این فایل جدید در مسیر زیر قرار می‌گیرد:
%SystemRoot%\Assembly\NativeImages_v4.0.#####_64
نام دایرکتوری اطلاعاتی شامل نسخه CLR و اطلاعاتی مثل اینکه برنامه بر اساس چه نسخه‌ای 32 یا 64 بیت کامپایل شده است.

معایب
احتمالا شما پیش خود می‌گویید این مورد فوق العاده امکان جالبی هست. کدها از قبل تبدیل شده‌اند و دیگر فرآیند جیت صورت نمی‌گیرد. در صورتیکه ما تمامی امکانات یک CLR مثل مدیریت استثناءها و GC و ... را داریم، ولی غیر از این یک مشکلاتی هم به کارمان اضافه می‌شود که در زیر به آنها اشاره می‌کنیم:

عدم محافظت از کد در برابر بیگانگان: بعضی‌ها تصور می‌کنند که این کد را می‌توانند روی ماشین شخصی خود کامپایل کرده و فایل ngen را همراه با آن ارسال کنند. در این صورت کد IL نخواهد بود ولی موضوع این هست اینکار غیر ممکن است و هنوز استفاده از اطلاعات متادیتا‌ها پابرجاست به خصوص در مورد اطلاعات چون reflection و serialization‌ها . پس کد IL کماکان همراهش هست. نکته‌ی بعدی اینکه انتقال هم ممکن نیست؛ بنا به شرایطی که در مورد بعدی دلیل آن را متوجه خواهید شد.

از سینک با سیستم خارج میشوند: موقعیکه CLR، اسمبلی‌ها را به داخل حافظه بار می‌کند، یک سری خصوصیات محیط فعلی را با زمانیکه عملیات تبدیل IL به کد ماشین صورت گرفته است، چک می‌کند. اگر این خصوصیات هیچ تطابقی نداشته باشند، عملیات JIT همانند سابق انجام می‌گردد. خصوصیات و ویژگی‌هایی که چک می‌شوند به شرح زیر هستند:
  • ورژن CLR: در صورت تغییر، حتی با پچ‌ها و سرویس پک ها.
  • نوع پردازنده: در صورت تغییر پردازنده یا ارتقا سخت افزاری.
  • نسخه سیستم عامل : ارتقاء با سرویس پک ها.
  • MVID یا Assemblies Identity module Version Id: در صورت کامپایل مجدد تغییر می‌کند.
  • Referenced Assembly's version ID: در صورت کامپایل مجدد اسمبلی ارجاع شده.
  • تغییر مجوزها: در صورتی که تغییری نسبت به اولین بار رخ دهد؛ مثلا در قسمت قبلی در مورد اجازه نامه اجرای کدهای ناامن صحبت کردیم. برای نمونه اگر در همین اجازه نامه تغییری رخ دهد، یا هر نوع اجازه نامه دیگری، برنامه مثل سابق (جیت) اجرا خواهد شد.
پی نوشت: در آپدیت‌های دات نت فریم ورک به طور خودکار ابزار ngen صدا زده شده و اسمبلی‌ها مجددا کمپایل و دخیره میشوند و برنامه سینک و آپدیت باقی خواهد ماند. 

کارایی پایین کد در زمان اجرا: استفاده از ngen از ابتدا قرار بود کارآیی را با حذف جیت بالا ببرد، ولی گاهی اوقات در بعضی شرایط ممکن نیست. کدهایی که ngen تولید می‌کند به اندازه‌ی جیت بهینه نیستند. برای مثال ngen نمی‌تواند بسیاری از دستورات خاص پردازنده را جز در زمان runtime مشخص کند. همچنین فیلدهایی چون static را از آنجا که نیاز است آدرس واقعی آن‌ها در زمان اجرا به دست بیاید، مجبور به تکنیک و ترفند میشود و موارد دیگری از این قبیل.
پس حتما نسخه‌ی ngen شده و غیر ngen را بررسی کنید و کارآیی هر دو را با هم مقایسه کنید. برای بسیاری از برنامه‌ها کاهش صفحه بندی یک مزیت و باعث بهبود کارآیی می‌شود. در نتیجه در این قسمت ngen برنده اعلام می‌شود.

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

برای بسیاری از برنامه‌های کلاینت که تجربه‌ی startup طولانی دارند، مایکروسافت ابزاری را به نام Managed Profile Guided Optimization Tool یا MPGO .exe دارد. این ابزار به تحلیل اجرای برنامه شما پرداخته و بررسی می‌کند که در زمان آغازین برنامه چه چیزهایی نیاز است. اطلاعات به دست آمده از تحلیل به سمت ngen فرستاده شده تا کد بومی بهینه‌تری تولید گردد. موقعیکه شما آماده ارائه برنامه خود هستید، برنامه را از طریق این تحلیل و اجرا کرده و با قسمت‌های اساسی برنامه کار کنید. با این کار اطلاعاتی در مورد اجرای برنامه در داخل یک پروفایل embed شده در اسمبلی، قرار گرفته و ngen موقع تولید کد، این پروفایل را جهت تولید کد بهینه مطالعه خواهد کرد.

در مقاله‌ی بعدی در مورد FCL صحبت‌هایی خواهیم کرد.