نظرات نظرسنجی‌ها
آیا با وجود سی‌ام‌اس فروشگاهی قدرتمندی مثل nopCommerce یا SmartStore آیا منطقی است که ما دوباره خودمان از صفر کد بزنیم؟
چند تا از نمونه هایی که عملا به صورت عمیق (درگیر شدن در کد‌های هسته و ماژول‌های آن و توسعه سیستم) با آنها کار کرده ایم:
برای PHP
  1. woocommerce (فروشگاه‌های تا متوسط و عملیات تجاری سبک) - سفارشی سازی در حد وردپرس - راحته ولی رو اعصابه
  2. prestashop (فروشگاه‌های تا متوسط و عملیات تجاری متوسط) - سفارشی سازی متوسط - زیرساخت‌های خیلی پیشرفته درش وجود ندارد
  3. magento (فروشگاه‌های تا سایز بزرگ و عملیات تجاری بزرگ) - سفارشی سازی پیشرفته 
برای .NET
  1. nopCommerce(فروشگاه‌های تا متوسط و عملیات تجاری متوسط) -سفارشی سازی متوسط
  2. VirtoCommerce(فروشگاه‌های تا سایز بزرگ و عملیات تجاری بزرگ) -سفارشی سازی پیشرفته، زمان بر است ولی برای کارهای بزرگ لازم است.
موارد سفارشی 
  1. با ASP.NET چند مورد توسعه داشتیم.
هر کدام از اینها سطح مختلفی از پیچیدگی دارند و بر اساس این پیچیدگی سطوح مختلفی از ماژول‌های تجاری پیشرفته درآنها وجود دارد.
بری مثال در nopCommerce شما C# و ASP.NET و jQuery بلد باشید عمده کار انجام می‌شود. و پروژه لایه بندی پیچیده ای ندارد. شما با یک اپلیکیشن طرف هستید
ولی در VirtoCommerce باید تسلط کافی به مفاهیم برنامه نویسی شی گرا، لایه بندی، معماری سرویس گرا، C# و AngularJs و liquid و چندین مورد دیگر داشته باشید تا پروژه جلو برود. اینجا با چند اپلیکیشن طرف هستید و این پروژه برای اجرا شدن روی shared hosting ساخته نشده است.
آن چیزی که مشتری از یک سیستم ecommerce می‌بیند با چیزی که مدیران و پرسنل مجموعه فروشگاه می‌بینند متفاوت است، در اغلب سیستم‌ها چیزی که مستری می‌بیند تقریبا مشابه است ولی پشت صحنه زمین تا آسمان تفاوت دارد.
پشت صحنه یک فروشگاه اتفاقات زیادی می‌تواند در جریان باشد، چیزی که در بخش مدیریت سفارش nopcommerce وجود دارد ساده است، مدیریت امور مالی، مدیریت تحویل کالا، مدیریت اسناد، مدیریت موجودی و ... می‌تواند بسیار پیچیده‌تر باشد و حتی به کمک نرم افزارهای دیگر یا سرویس‌های آنلاین دیگر مدیریت شود.
Delivery, Payment, Tax, Inventory, Warehouse, Localization, Globalization و موارد متعدد دیگری هر کدام در این فروشگاه‌های آماده در حد نیاز پیاده سازی شده اند، و این نیازی که توسط تیم توسعه آن تعریف شده مشتریان هدف آن را مشخص می‌کند. اگر کمی در marketplace هر کدام از این‌ها چرخی بزنید و ماژول‌های مشابه را بررسی کنید صرفا از تفاوت قیمت و سطح پشتیبانی کیفیت هر کدام مشخص می‌شود.
مثلا ما در یکی از پروژه‌ها VirtoCommerce را به عنوان پایه پروژه انتخاب کردیم ولی بر اساس منطق تجاری تعریف شده ماژول‌های زیادی برای آن توسعه داده شد، حتی تغییراتی در هسته آن ایجاد شد. این پروژه 6 ماه با یک تیم 5 (مدیر پروژه+2 نفر دات نت+ گرافیست+1 نفر آندروید+ 1 نفر IOS) نفره طول کشید. در صورتی که مشابه همین کار را با Prestashop برای یک پروژه 15 درصد کوچکتر با یک تیم 3 نفره در مدت 4 ماه انجام دادیم. تازه در virtocommerce هیچ گزارشی وجود ندارد، همه چیز با PowerBi باید انجام شود.
قیمت اولی تقریبا 4 برابر دومی بود. هر دو پروژه وب سایت و نسخه native موبایل داشته اند.
ما درگیر تکنولوژی وابزار نبودیم چون تیم مسلط به هر کدام را داریم، ولی اگر مثلا فقط به .net مسلط هستید در همان حوزه ادامه دهید. البته PHP هزینه‌های کمتری دارد. 
حرف آخر: حتی در مایکروسافت هم چند وقت یک بار میندازن دور از اول می‌نویسند، رسیدن به ebay کار زیادی می‌برد شاید در طول مسیر حتی چند بار تغییر جهت بدهید. دیجی کالا اصلا اندازه ebay نیست. با هم مقایسه نکنید. در حال حاظر متد‌های تجارت الکترونیک در حال پیشرفت و تحول هستند. دیجیکالا فقط چند متد را در خود دارد فروشگا هایی هستند که چندین متد فروش را همزمان پیاده سازی کرده اند و مدیریت آنها به صورت یکپارچه انجام می‌شود.
یک فروشگاه آنلاین فقط نمای کار است کار فیزیکی پشت صحنه بسیار پیچیده‌تر است.
مطالب
مبانی TypeScript؛ Mixins
یکی از راه‌های محبوب دیگر برای ساخت کلاس‌ها با استفاده از اجزایی با قابلیت استفاده مجدد، ساخت آنها با ترکیب 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، پر میکند. 
مطالب
پیاده سازی پروژه‌های React با TypeScript - قسمت پنجم - تعیین نوع هوک useReducer
هوک استاندارد useReducer، یک نمونه‌ی پیچیده‌تر هوک useState، برای مدیریت حالت است؛ با ساختاری شبیه به Redux، اما تنها مخصوص یک کامپوننت. هوک useReducer شبیه به یک نسخه‌ی کوچک و محلی Redux است. در این قسمت، نحوه‌ی تعیین نوع‌های قسمت‌های مختلف آن‌را با TypeScript بررسی می‌کنیم.


ایجاد کامپوننت جدید ReducerButtons

برای توضیح مفاهیم این قسمت، فایل جدید src\components\ReducerButtons.tsx را به همراه محتوای زیر ایجاد می‌کنیم:
import React, { useReducer } from "react";

const initialState = { rValue: true };
type State = {  rValue: boolean; };
type Action = {   type: string; };

function reducer(state: State, action: Action) {
  switch (action.type) {
    case "one":
      return { rValue: true };
    case "two":
      return { rValue: false };
    default:
      return state;
  }
}

export const ReducerButtons = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      {state?.rValue && <h1>Visible</h1>}
      <button onClick={() => dispatch({ type: "one" })}>Action One</button>
      <button onClick={() => dispatch({ type: "two" })}>Action Two</button>
    </div>
  );
};
توضیحات:
- این کامپوننت دو دکمه را رندر می‌کند تا توسط آن‌ها بتوان چندین Action مختلف را جهت مدیریت حالت، سبب شد؛ مانند Action One و Two.
- initialState را در این مثال، یک شیء ساده در نظر گرفته‌ایم که تنها دارای یک مقدار boolean است.
- سپس نیاز به یک تابع ویژه را به نام reducer، داریم که مقدار state جاری و یک action را دریافت می‌کند. این تابع بر اساس نوع Action ای که به آن می‌رسد، state جدیدی را بازگشت می‌دهد و در قسمت رندر آن، با بررسی state?.rValue، سبب نمایش عبارتی خواهیم شد.
- در حین تعریف هوک useReducer، قسمت dispatch آن، تابعی است که یک Action را اجرا می‌کند. فراخوانی آن‌را در رویداد onClick دو دکمه‌ی تعریف شده مشاهده می‌کنید. این تابع یک شیء را دریافت می‌کند که type اکشن ارسالی به تابع reducer را مقدار دهی می‌کند.

توسط useReducer برای ایجاد تغییرات در شیء state کامپوننت جاری، از مفهومی به نام dispatch actions استفاده می‌شود. action در اینجا به معنای رخ‌دادن چیزی است؛ مانند کلیک بر روی یک دکمه و یا دریافت اطلاعاتی از یک API. در این حالت مقایسه‌ای بین وضعیت قبلی state و وضعیت فعلی آن صورت می‌گیرد و تغییرات مورد نیاز جهت اعمال به UI، محاسبه خواهند شد. اصلی‌ترین جزء آن، تابعی است به نام Reducer. این تابع، یک تابع خالص است و دو آرگومان را دریافت می‌کند. تابع Reducer، بر اساس action و یا رخ‌دادی، ابتدا کل state برنامه را دریافت می‌کند و سپس خروجی آن بر اساس منطق این تابع، یک state جدید خواهد بود. اکنون که این state جدید را داریم، برنامه‌ی React ما می‌تواند به تغییرات آن گوش فرا داده و بر اساس آن، UI را به روز رسانی کند.

اولین قسمتی را که در حین کار با useReducer توسط TypeScript به آن برخورد خواهیم کرد، نمایش خطاهایی در مورد نوع‌های پارامترهای state و action تابع reducer است. در حالت متداول جاوا اسکریپتی آن، این پارامترها، بدون نوع ذکر می‌شوند که در اینجا به any تفسیر خواهند شد و یک چنین تعاریفی با توجه به strict بودن تنظیمات tsconfig.json برنامه، مجاز نیست. به همین جهت نیاز به تعریف دو type جدید به نام‌های State و Action در اینجا وجود دارد تا خطاهای بدون نوع بودن پارامترهای تابع reducer برطرف شوند. نوع Action به همراه حداقل یک خاصیت به نام action رشته‌ای است و نوع State بر اساس initialState ای که تعریف کردیم، دارای یک خاصیت متناظر boolean است.

نکته‌ی جالب دومی که در اینجا توسط TypeScript گوشزد می‌شود، الزام به ذکر حالت default مربوط به switch ای است که در تابع reducer تعریف کرده‌ایم. اگر این حالت را حذف کنیم، بلافاصله خطای زیر را در قسمت تعریف useReducer نمایش می‌دهد:


عنوان می‌کند که خروجی تابع reducer، یک state جدید است؛ اما ممکن است از نوع never هم باشد که قابلیت ترجمه‌ی به نوع state را ندارد.


بهبود تعاریف نوع‌های useReducer

تا اینجا مهم‌ترین تغییرات تایپ‌اسکریپتی صورت گرفته، تعریف نوع‌های پارامترهای تابع reducer است. اکنون فرض کنید می‌خواهیم، سومین Action را هم به کامپوننت جاری اضافه کنیم:
<button onClick={() => dispatch({ type: "three" })}>Action Three</button>
با این تغییر، برنامه بدون مشکل کامپایل می‌شود، اما ... در عمل کار خاصی را هم انجام نمی‌دهد؛ چون switch تعریف شده‌ی در تابع reducer، یک case مخصوص به آن‌را تعریف نکرده‌است. یا حتی ممکن است در حین تعریف همان دو تابع dispatch، مقدار type را به اشتباه وارد کنیم و با حالت‌های تعریف شده‌ی در تابع reducer تطابقی نداشته باشد. به همین جهت علاقمندیم تا TypeScript بتواند جلوی یک چنین اشتباهاتی را نیز بگیرد؛ برای این منظور می‌توان از union types استفاده کرد:
type Action = {
   type: "one" | "two" | "three";
};
در اینجا تمام اکشن‌های ممکن و مدنظر را لیست کرده‌ایم که سبب خواهد شد تا اگر مقدار نوع اکشنی به درستی وارد نشود، بلافاصله خطایی را دریافت کنیم:


و یا حتی اگر مقدار action.type ای را در تابع reducer به اشتباه وارد کردیم، باز هم برنامه کامپایل نمی‌شود:



تا اینجا موفق شدیم جلوی ورود actionهای پیش بینی نشده را بگیریم. اما اگر در switch تعریف شده، "case "three را هم قید نکنیم، باز هم برنامه کامپایل می‌شود و خطایی گزارش نخواهد شد. برای این منظور فقط کافی است case default را حذف کنیم. چون action.type دیگر نمی‌تواند مقدار دیگری را بجز موارد ذکر شده‌ی در نوع Action داشته باشد. با حذف case default، اینبار ذکر "case "three یا هر کدام از مقادیر سه‌گانه‌ی مجازی که در اینجا تعریف کردیم، الزامی خواهد بود. در غیراینصورت، همان خطای دریافت خروجی never را از تابع reducer دریافت می‌کنیم که با ذکر هر سه case مجاز، برطرف می‌شود.
با این تغییرات، کدهای نهایی این قسمت به صورت زیر در خواهند آمد:
import React, { useReducer } from "react";

const initialState = { rValue: true };

type State = {
  rValue: boolean;
};

type Action = {
  type: "one" | "two" | "three";
};

function reducer(state: State, action: Action) {
  switch (action.type) {
    case "one":
      return { rValue: true };
    case "two":
      return { rValue: false };
    case "three":
      return { rValue: false };
  }
}

export const ReducerButtons = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      {state?.rValue && <h1>Visible</h1>}
      <button onClick={() => dispatch({ type: "one" })}>Action One</button>
      <button onClick={() => dispatch({ type: "two" })}>Action Two</button>
      <button onClick={() => dispatch({ type: "three" })}>Action Three</button>
    </div>
  );
};
نظرات مطالب
شروع به کار با Ember.js
- صرف نوشتن مطلبی در مورد موضوعی خاص، به معنای «بهترین بودن آن» و «برترین حالت ممکن» نیست. امروز راجع به ember.js مطلب نوشتم. شاید هفته‌‌های بعد در مورد meteor.js مطلب نوشتم.
- مقایسه‌ای در اینجا «AngularJS vs Ember»
- مقایسه‌ای کامل‌تر در اینجا «AngularJs vs EmberJs»
- «Rails JS frameworks: Ember.js vs. AngularJS»
- «AngularJS vs. Backbone.js vs. Ember.js»
- یک نمونه‌ی دیگر «The Top 10 Javascript MVC Frameworks Reviewed» 
- این نظرسنجی را هم دنبال کنید: «آیا به یادگیری یا ادامه‌ی استفاده از AngularJS خواهید پرداخت؟»  
مطالب
استفاده از کتابخانه‌ی moment-jalaali در برنامه‌های Angular
چندی قبل مطلب «نمایش تاریخ شمسی توسط JavaScript در AngularJS» را در این سایت مطالعه کردید. در اینجا قصد داریم معادل Angular آن‌را تهیه کنیم (واژه‌ی AngularJS به نگارش‌های 1x اشاره می‌کند و Angular به تمام نگارش‌های پس از 2).


نصب پیشنیازهای کار با moment-jalaali

ابتدا نیاز است بسته‌ی npm این کتابخانه را نصب کنیم که به همراه فایل‌های js مرتبط با آن می‌باشد:
 npm install moment-jalaali --save

سپس جهت بهبود تجربه‌ی کاربری با آن در IDEهای امروزی، خصوصا VSCode، بهتر است typings آن‌را نیز نصب کنیم؛ تا علاوه بر داشتن Intellisense، بتوان به صورت strongly typed با آن کار کرد:
 npm install @types/moment-jalaali --save-dev


VSCode به صورت خودکار پوشه‌ی مخصوص node_modules\@types را تحت نظر قرار می‌دهد و نصب بسته‌های typings در آن، سبب بارگذاری آنی آن‌ها خواهد شد.
به علاوه اگر به فایل tsconfig.json واقع در ریشه‌ی پروژه نیز دقت کنید، وجود تعریف ذیل، امکان خوانده شدن این تعاریف را توسط کامپایلر TypeScript میسر می‌کند:
{
    "typeRoots": [
      "node_modules/@types"
    ]
}

 
نحوه‌ی کار Strongly Typed با کتابخانه‌ی moment-jalaali در برنامه‌های مبتنی بر TypeScript

پس از نصب پیشنیازهای یاد شده، ابتدا برای دسترسی به امکانات این کتابخانه، ماژول آن‌را import می‌کنیم:
import * as momentJalaali from "moment-jalaali";

export class MomentJalaaliTestComponent implements OnInit {
  now: string;
  nowLongDateFormat: string;
  nowExtraLongDateFormat: string;

  ngOnInit() {
    this.persianDateTests();
  }

  persianDateTests() {
    // https://github.com/jalaali/moment-jalaali
    momentJalaali.loadPersian(/*{ usePersianDigits: true }*/); // نمایش فارسی نام ماه‌ها، روزها و امثال آن

    this.now = momentJalaali().format("jYYYY/jMM/jDD HH:mm");
    this.nowLongDateFormat = momentJalaali().format("jD jMMMM jYYYY [ساعت] LT");
    this.nowExtraLongDateFormat = momentJalaali().format(
      "dddd، jD jMMMM jYYYY [ساعت] LT"
    );
  }
}
- پس از import ماژولی به نام moment-jalaali، اکنون نحوه‌ی استفاده‌ی از آن‌را در متد persianDateTests مشاهده می‌کنید.
- متد momentJalaali.loadPersian باید تنها یکبار فراخوانی شود. کار آن تبدیل نام‌های روزها و ماه‌های میلادی، به شمسی است.
- پس از آن از طریق متد format آن، می‌توان انواع و اقسام حالات مختلف را بررسی کرد که در اینجا سه نمونه را مشاهده می‌کنید.



نوشتن یک Pipe سفارشی برای تبدیل تاریخ‌های میلادی دریافتی از سرور به قالب شمسی

پس آشنا شدن با نحوه‌ی استفاده‌ی از این کتابخانه در یک برنامه‌ی تایپ‌اسکریپتی، تبدیل کردن آن به یک Pipe سفارشی بسیار ساده‌است. برای این منظور ابتدا یک Pipe جدید را به ماژول فرضی custom-pipe.module اضافه می‌کنیم:
 ng g p CustomPipe/moment-jalaali -m custom-pipe.module
با این محتوا:
import { Pipe, PipeTransform } from "@angular/core";

import * as momentJalaali from "moment-jalaali";

@Pipe({
  name: "momentJalaali"
})
export class MomentJalaaliPipe implements PipeTransform {
  transform(value: any, args?: any): any {
    return momentJalaali(value).format(args);
  }
}
در اینجا نیز ابتدا ماژول moment-jalaali تعریف شده‌است و سپس توسط آن، value به عنوان پارامتر متد momentJalaali و args به عنوان پارامتر متد format ارسال شده‌اند. در حین استفاده‌ی از Pipe، مقدار value همان تاریخ دریافتی است و args به فرمت خاصی که توسط استفاده کننده مشخص می‌شود، تنظیم خواهد شد.
به این ترتیب می‌توان یک چنین تبدیلات سمت کاربری را انجام داد که نمونه‌ای از خروجی آن‌را در تصویر فوق نیز ملاحظه می‌کنید:
<h2>Server side dates:</h2>
<div *ngFor="let date of dates">
  <span dir="ltr">{{date | momentJalaali:'jYYYY/jMM/jDD hh:mm' }}</span>,
  <span dir="rtl">{{date | momentJalaali:'jD jMMMM jYYYY [ساعت] LT'}}</span>
</div>


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
مطالب
نقدی بر کتاب «مرجع کامل entity framework 4.1»

کتاب «مرجع کامل entity framework 4.1» نوشته‌ی آقای راد نزدیک به یک ماهی است که منتشر شده است. فرصتی پیدا شد تا این کتاب حدودا 260 صفحه‌ای را مطالعه کنم و در ادامه توضیحاتی را پیرامون آن مطالعه خواهید کرد.

بررسی کتاب

در عنوان کتاب ذکر شده «مرجع کامل»؛ ولی خوب، 260 نمی‌تونه مرجع کامل باشه. بنابراین کمی رعایت اعتدال در کارهای بعدی لازم به نظر می‌رسد. همچنین یک مورد را هم همیشه در نشر کتب تخصصی در نظر داشته باشید: «ذکر شماره نگارش محصول» مورد نظر در عنوان کتاب، خیلی سریع کار شما را از مد افتاده خواهد کرد. خیالتان راحت باشد تا یک سال دیگر همینطور این شماره‌ها افزایش پیدا می‌کنند. خریداری هم که آنچنان اطلاعاتی از کل کار نداشته باشد، بر اساس همین شماره و بدون مطالعه متن، از خرید کتاب امتناع خواهد کرد.

  • فصل اول این کتاب به معرفی تاریخچه‌ی EF و لزوم استفاده از آن می‌پردازد. همچنین خلاصه‌ای از قابلیت‌های آن‌را همانند روش‌های model first ، database first و code first بیان می‌کند.
  • تمرکز فصل دوم بر نحوه‌ی استفاده از روش‌های model first و database first است به همراه نحوه‌ی تولید اسکریپت بانک اطلاعاتی در حالت model first.
  • فصل سوم کتاب به مرور جزئیات طراح EF در ویژوال استودیو جهت کار بهتر با موجودیت‌ها اختصاص دارد.
  • در فصل چهارم با روش‌های کوئری نویسی در EF آشنا خواهید شد. همچنین بر روی مباحث اجرای به تعویق افتاده و مفهوم آن هم بحث شده که بسیار ارزشمند است.
  • فصل پنجم کتاب به مباحث ثبت، حذف و به روز رسانی اطلاعات توسط EF اختصاص دارد. همچنین یک سری مباحث همانند سطح اول caching در NHibernate که در EF هم وجود دارد، بررسی شده است که البته نام آن در اینجا Object state و entity state است.
  • در فصل ششم در مورد نحوه‌ی نگاشت رویه‌های ذخیره شده SQL Server به اشیاء دات نتی بحث شده همچنین نحوه‌ی اجرا و استفاده از آن‌ها
  • فصل هفتم کتاب به ارتباطات بین موجود‌یت‌ها یا همان مباحث one to many و امثال آن اختصاص دارد به همراه نحوه‌ی تنظیمات آن در طراح EF در VS.NET
  • در فصل هشتم، به قالب‌های T4 پرداخته شده. ابتدا معرفی، سپس آشنایی با Syntax و نهایتا نحوه‌ی دستکاری و سفارشی سازی قالب‌های پیش فرض T4 مرتبط با EF ارائه شده‌اند.
  • فصل نهم به بررسی کاملتر مبحث model first که در فصل دوم معرفی شده می‌پردازد. ایجاد موجودیت‌ها، نحوه‌ی تعریف ارتباطات و نهایتا ایجاد بانک اطلاعاتی از روی آن
  • فصل دهم آن به مباحث جدید EF در مورد Code first اختصاص دارد. این فصل واقعا ارزشمند است چون ... نتیجه‌ی تحقیق بوده نه ترجمه. تقریبا با تمام تاریخچه‌ی مرتبط با code first در EF، محل‌های دریافت فایل‌ها، ابزارهای کمکی، روش‌های کوئری گرفتن،نحوه‌ی ایجاد بانک اطلاعاتی از روی کد، تعیین اعتبار و غیره در طی یک فصل آشنا خواهید شد.
  • در فصل یازدهم آن مروری بر WCF Data services و پروتکل OData صورت گرفته است. نحوه‌ی ایجاد و سپس فراخوانی آن توسط یک کلاینت. در عنوان کتاب ذکر شده : «مرجع»، بنابراین به دنبال یک کتاب خودآموز قدم به قدم نباشید. این کتاب بیشتر به «معرفی» امکانات موجود در EF در طی 260 صفحه می‌پردازد که الزاما با توجه به تعداد صفحات کتاب، بعضی از موارد آن مانند این فصل آخر، از عمق لازم برخوردار نیستند ولی، حداقل سرنخ را به دست شما خواهند داد.


مزایا:
  •  به روز بودن مطالب آن
  •  آشنایی و تسلط مؤلف/مترجم به مطالبی که تهیه کرده. این مورد در فصل دهم آن مشهود است.
  •  زبان فارسی (بله! خیلی مهمه! هستند کسانی که چند گیگ، ببخشید چند صد گیگ (!)، eBook به زبان انگلیسی دارند ولی حتی یکی از آن‌ها را هم تمام نکرده‌اند)
  •  متن روان و سلیس
  •  کیفیت خوب کتاب، صفحه بندی و امثال آن


معایب:
  •  قیمت نزدیک به 8000 تومان برای کتاب 260 صفحه‌ای به نظر زیاد است. البته با بالا رفتن قیمت‌ها (برای مثال 4 برابر شدن قیمت یک عدد نان لواش از سال قبل تا به امسال!)، بالاخره ... خوب این مسایل را هم به همراه خواهد داشت.
  •  تصاویر موجود در کتاب عموما بیش از اندازه کوچک شده‌اند. این مورد خواندن تعدادی از آن‌ها را با مشکل مواجه کرده است.
  •  در مورد متد الحاقی معروف Include در EF من مطلبی را در این کتاب پیدا نکردم. این مورد به بحث عدم نیاز به join نویسی صریح در EF مرتبط می‌شود.
  •  در مورد نحوه‌ی استفاده از EF با سایر بانک‌های اطلاعاتی بحث نشده. کتاب فقط به SQL Server منحصر است.
  •  در یکی از فصل‌ها به الگوی Repository در حد نامبردن اشاره شده. این مورد برای خواننده‌ای که اطلاعاتی از موضوع ندارد، کافی نیست. می‌شد یک فصل را به آن اختصاص داد.


در کل خواندن کتاب «معرفی» EF 4.1 ، به کسانی که با Silverlight و WCF RIA Services سر و کار دارند (و کوئری‌های آن برایشان کمی گنگ است) و همچنین عموم علاقمندانی که می‌خواهند جایگزینی برای ADO.NET (در یک سطح بالاتر از آن البته) پیدا کنند توصیه می‌شود.



در حاشیه!

شاید بپرسید چرا این کتاب در 260 صفحه و چرا فقط در 1000 نسخه منتشر شده است. چرا اینقدر تعداد کتاب‌های تخصصی کم است. چرا بیشتر تمایل به چاپ کتاب‌های نصب ویندوز و امثال آن است تا مثلا کتاب EF 4.1 یا خدای نکرده NHibernate ! پاسخ هم در یک جمله خلاصه می‌شود: «نگرانی ناشر از بازگشت سرمایه»
این شما هستید که با پشتیبانی خود می‌توانید این امیدواری را به ناشرین کشور بدهید تا «جرات کنند» بیشتر به طرف کتاب‌های تخصصی بروند و این پشتیبانی با صرفا گفتن چقدر عالی، دست شما درد نکنه، خیلی خوب بود، باز هم از این کارها بکنید،‌ معنا پیدا نمی‌کند! باید لطف کنید و «خرید کنید». هیچ راه دیگری هم ندارد. الان چند عدد کتاب ASP.NET MVC 3.0 در کشور به زبان فارسی وجود دارد؟ چند عدد کتاب تخصصی SQL Server 2008 R2 را می‌توانید پیدا کنید؟ در مورد کتابخانه پردازش موازی دات نت 4 چطور؟ و ...
البته منهای نگرانی این بحث بازگشت سرمایه ، یک مورد دیگر هم سبب این نوع تاخیرها هست. یادم میاد کتاب الگوهای طراحی برنامه نویسی شیءگرا در سی شارپ رو که چند سال قبل به ناشر دادم نحوه‌ی پرداخت آن به این صورت بود: نزدیک به 10 درصد پشت جلد، در طی چند قسط، آن هم 6 ماه پس از انتشار عمومی کتاب! خوب همین شد که من دیگر به طرف این کار نرفتم. چون واقعا نوشتن، یک «کار» کامل است. باید وقت گذاشت (6 ماه حداقل یا بیشتر)، تحقیق کرد، ریاضت کشید و دست آخر 6 ماه پس از انتشار کتاب ... با توجه به اینکه کتاب رو که الان شما به دست ناشر می‌دید شاید یکسال دیگر منتشر شود (بسته به تعداد کاری که در دست دارد).
در هر حال، با تمام این تفاسیر، هستند کسانی که «امیدوارانه» نسبت به نوشتن کتاب‌های تخصصی مانند «مرجع کامل entity framework 4.1» اقدام می‌کنند و شما هم حداقل کاری که می‌توانید جهت حمایت از این نوع حرکات بکنید، «خرید است». در غیراینصورت مدام اینطرف اونطرف ننویسید که چرا کتاب WPF 4.0 یا WCF 4.0 به زبان فارسی نداریم. پشتیبانی نمی‌کنید؟! خوب ... نداریم! «همین!»
یک مورد دیگر هم هست البته. عده‌ای هستند که مثلا کلاس‌های میلیونی، جهت آموزش این مباحث برگزار می‌کنند. خوب این‌ها هم مسلما خوشحال نخواهند شد که مثلا کتاب WCF 4.0 و مباحث SOA مرتبط با آن به زبان فارسی منتشر شود یا حتی در این زمینه پیش قدم شوند. این هم هست!


مطالب
Roslyn #6
معرفی Analyzers

پیشنیاز این بحث نصب مواردی است که در مطلب «شروع به کار با Roslyn » در قسمت دوم عنوان شدند:
الف) نصب SDK ویژوال استودیوی 2015
ب) نصب قالب‌های ایجاد پروژه‌های مخصوص Roslyn

البته این قالب‌ها چیزی بیشتر از ایجاد یک پروژه‌ی کلاس Library جدید و افزودن ارجاعاتی به بسته‌ی نیوگت Microsoft.CodeAnalysis، نیستند. اما درکل زمان ایجاد و تنظیم این نوع پروژه‌ها را خیلی کاهش می‌دهند و همچنین یک پروژه‌ی تست را ایجاد کرده و تولید بسته‌ی نیوگت و فایل VSIX را نیز بسیار ساده می‌کنند.


هدف از تولید Analyzers

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


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

در اینجا یک کلاس نمونه را مشاهده می‌کنید که در آن فیلدهای کلاس به صورت public تعریف شده‌اند.
    public class Student
    {
        public string FirstName;
        public string LastName;
        public int TotalPointsEarned;

        public void TakeExam(int pointsForExam)
        {
            TotalPointsEarned += pointsForExam;
        }

        public void ExtraCredit(int extraPoints)
        {
            TotalPointsEarned += extraPoints;
        }


        public int PointsEarned { get { return TotalPointsEarned; } }
    }
هرچند این کلاس از دید کامپایلر بدون مشکل است و کامپایل می‌شود، اما از لحاظ اصول کپسوله سازی اطلاعات دارای مشکل است و نباید جمع امتیازات کسب شده‌ی یک دانش آموز به صورت مستقیم و بدون مراجعه‌ی به متدهای معرفی شده، از طریق فیلدهای عمومی آن قابل تغییر باشد.
بنابراین در ادامه هدف ما این است که یک Roslyn Analyzer جدید را طراحی کنیم تا از طریق آن هشدارهایی را جهت تبدیل فیلدهای عمومی به خصوصی، به برنامه نویس نمایش دهیم.


با اجرای افزونه‌ی View->Other windows->Syntax visualizer، تصویر فوق نمایان خواهد شد. بنابراین در اینجا نیاز است FieldDeclaration‌ها را یافته و سپس tokenهای آن‌ها را بررسی کنیم و مشخص کنیم که آیا نوع یا Kind آن‌ها public است (PublicKeyword) یا خیر؟ اگر بلی، آن مورد را به صورت یک Diagnostic جدید گزارش می‌دهیم.


ایجاد اولین Roslyn Analyzer

پس از نصب پیشنیازهای بحث، به شاخه‌ی قالب‌های extensibility در ویژوال استودیو مراجعه کرده و یک پروژه‌ی جدید از نوع Analyzer with code fix را آغاز کنید.


قالب Stand-alone code analysis tool آن دقیقا همان برنامه‌های کنسول بحث شده‌ی در قسمت‌های قبل است که تنها ارجاعی را به بسته‌ی نیوگت Microsoft.CodeAnalysis به صورت خودکار دارد.
قالب پروژه‌ی Analyzer with code fix علاوه بر ایجاد پروژه‌های Test و VSIX جهت بسته بندی آنالایزر تولید شده، دارای دو فایل DiagnosticAnalyzer.cs و CodeFixProvider.cs پیش فرض نیز هست. این دو فایل قالب‌هایی را جهت شروع به کار تهیه‌ی آنالیز کننده‌های مبتنی بر Roslyn ارائه می‌دهند. کار DiagnosticAnalyzer آنالیز کد و ارائه‌ی خطاهایی جهت نمایش به ویژوال استودیو است و CodeFixProvider این امکان را مهیا می‌کند که این خطای جدید عنوان شده‌ی توسط آنالایزر، چگونه باید برطرف شود و راه‌کار بازنویسی Syntax tree آن‌را ارائه می‌دهد.
همین پروژه‌ی پیش فرض ایجاد شده نیز قابل اجرا است. اگر بر روی F5 کلیک کنید، یک کپی جدید و محصور شده‌ی ویژوال استودیو را باز می‌کند که در آن افزونه‌ی در حال تولید به صورت پیش فرض و محدود نصب شده‌است. اکنون اگر پروژه‌ی جدیدی را جهت آزمایش، در این وهله‌ی محصور شده‌ی ویژوال استودیو باز کنیم، قابلیت اجرای خودکار آنالایزر در حال توسعه را فراهم می‌کند. به این ترتیب کار تست و دیباگ آنالایزرها با سهولت بیشتری قابل انجام است.
این پروژه‌ی پیش فرض، کار تبدیل نام فضاهای نام را به upper case، به صورت خودکار انجام می‌دهد (که البته بی‌معنا است و صرفا جهت نمایش و ارائه‌ی قالب‌های شروع به کار مفید است).
نکته‌ی دیگر آن، تعریف تمام رشته‌های مورد نیاز آنالایزر در یک فایل resource به نام Resources.resx است که در جهت بومی سازی پیام‌های خطای آن می‌تواند بسیار مفید باشد.

در ادامه کدهای فایل DiagnosticAnalyzer.cs را به صورت ذیل تغییر دهید:
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
 
namespace CodingStandards
{
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public class CodingStandardsAnalyzer : DiagnosticAnalyzer
    {
        public const string DiagnosticId = "CodingStandards";

        // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat.
        internal static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), Resources.ResourceManager, typeof(Resources));
        internal static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
        internal static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AnalyzerDescription), Resources.ResourceManager, typeof(Resources));
        internal const string Category = "Naming";

        internal static DiagnosticDescriptor Rule = 
            new DiagnosticDescriptor(
                DiagnosticId, 
                Title, 
                MessageFormat, 
                Category, 
                DiagnosticSeverity.Error, 
                isEnabledByDefault: true, 
                description: Description);
 
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get { return ImmutableArray.Create(Rule); }
        }

        public override void Initialize(AnalysisContext context)
        {
            // TODO: Consider registering other actions that act on syntax instead of or in addition to symbols
            context.RegisterSyntaxNodeAction(analyzeFieldDeclaration, SyntaxKind.FieldDeclaration);
        }

        static void analyzeFieldDeclaration(SyntaxNodeAnalysisContext context)
        {
            var fieldDeclaration = context.Node as FieldDeclarationSyntax;
            if (fieldDeclaration == null) return;
            var accessToken = fieldDeclaration
                                .ChildTokens()
                                .SingleOrDefault(token => token.Kind() == SyntaxKind.PublicKeyword);

            // Note: Not finding protected or internal
            if (accessToken.Kind() != SyntaxKind.None)
            {
                // Find the name of the field:
                var name = fieldDeclaration.DescendantTokens()
                              .SingleOrDefault(token => token.IsKind(SyntaxKind.IdentifierToken)).Value;
                var diagnostic = Diagnostic.Create(Rule, fieldDeclaration.GetLocation(), name, accessToken.Value);
                context.ReportDiagnostic(diagnostic);
            }
        }
    }
}
توضیحات:

اولین کاری که در این کلاس انجام شده، خواندن سه رشته‌ی AnalyzerDescription (توضیحی در مورد آنالایزر)، AnalyzerMessageFormat (پیامی که به کاربر نمایش داده می‌شود) و AnalyzerTitle (عنوان پیام) از فایل Resources.resx است. این فایل را گشوده و محتوای آن‌را مطابق تنظیمات ذیل تغییر دهید:


سپس کار به متد Initialize می‌رسد. در اینجا برخلاف مثال‌های قسمت‌های قبل، context مورد نیاز، توسط پارامترهای override شده‌ی کلاس پایه DiagnosticAnalyzer فراهم می‌شوند. برای مثال در متد Initialize، این فرصت را خواهیم داشت تا به ویژوال استودیو اعلام کنیم، قصد آنالیز فیلدها یا FieldDeclaration را داریم. پارامتر اول متد RegisterSyntaxNodeAction یک delegate یا Action است. این Action کار فراهم آوردن context کاری را برعهده دارد که نحوه‌ی استفاده‌ی از آن‌را در متد analyzeFieldDeclaration می‌توانید ملاحظه کنید.
سپس در اینجا نوع نود در حال آنالیز (همان نودی که کاربر در ویژوال استودیو انتخاب کرده‌است یا در حال کار با آن است)، به نوع تعریف فیلد تبدیل می‌شود. سپس توکن‌های آن استخراج شده و بررسی می‌شود که آیا یکی از این توکن‌ها کلمه‌ی کلیدی public هست یا خیر؟ اگر این فیلد عمومی تعریف شده بود، نام آن‌را یافته و به عنوان یک Diagnostic جدید بازگشت و گزارش می‌دهیم.


ایجاد اولین Code fixer

در ادامه فایل CodeFixProvider.cs پیش فرض را گشوده و تغییرات ذیل را به آن اعمال کنید. در اینجا مهم‌ترین تغییر صورت گرفته نسبت به قالب پیش فرض، اضافه شدن متد makePrivateDeclarationAsync بجای متد MakeUppercaseAsync از پیش موجود آن است:
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
 
namespace CodingStandards
{
    [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CodingStandardsCodeFixProvider)), Shared]
    public class CodingStandardsCodeFixProvider : CodeFixProvider
    {
        public sealed override ImmutableArray<string> FixableDiagnosticIds
        {
            get { return ImmutableArray.Create(CodingStandardsAnalyzer.DiagnosticId); }
        }

        public sealed override FixAllProvider GetFixAllProvider()
        {
            return WellKnownFixAllProviders.BatchFixer;
        }

        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

            // TODO: Replace the following code with your own analysis, generating a CodeAction for each fix to suggest
            var diagnostic = context.Diagnostics.First();
            var diagnosticSpan = diagnostic.Location.SourceSpan;

            // Find the type declaration identified by the diagnostic.
            var declaration = root.FindToken(diagnosticSpan.Start)
                                   .Parent.AncestorsAndSelf().OfType<FieldDeclarationSyntax>()
                                   .First();

            // Register a code action that will invoke the fix.
            context.RegisterCodeFix(
                CodeAction.Create("Make Private", 
                c => makePrivateDeclarationAsync(context.Document, declaration, c)),
                diagnostic);
        }

        async Task<Document> makePrivateDeclarationAsync(Document document, FieldDeclarationSyntax declaration, CancellationToken c)
        {
            var accessToken = declaration.ChildTokens()
                .SingleOrDefault(token => token.Kind() == SyntaxKind.PublicKeyword);

            var privateAccessToken = SyntaxFactory.Token(SyntaxKind.PrivateKeyword);

            var root = await document.GetSyntaxRootAsync(c);
            var newRoot = root.ReplaceToken(accessToken, privateAccessToken);

            return document.WithSyntaxRoot(newRoot);
        }
    }
}
اولین کاری که در یک code fixer باید مشخص شود، تعیین FixableDiagnosticIds آن است. یعنی کدام آنالایزرهای از پیش تعیین شده‌ای قرار است توسط این code fixer مدیریت شوند که در اینجا همان Id آنالایزر قسمت قبل را مشخص کرده‌ایم. به این ترتیب ویژوال استودیو تشخیص می‌دهد که خطای گزارش شده‌ی توسط CodingStandardsAnalyzer قسمت قبل، توسط کدام code fixer موجود قابل رفع است.
کاری که در متد RegisterCodeFixesAsync انجام می‌شود، مشخص کردن اولین مکانی است که مشکلی در آن گزارش شده‌است. سپس به این مکان منوی Make Private با متد متناظر با آن معرفی می‌شود. در این متد، اولین توکن public، مشخص شده و سپس با یک توکن private جایگزین می‌شود. اکنون این syntax tree بازنویسی شده بازگشت داده می‌شود. با Syntax Factory در قسمت سوم آشنا شدیم.

خوب، تا اینجا یک analyzer و یک code fixer را تهیه کرده‌ایم. برای آزمایش آن دکمه‌ی F5 را فشار دهید تا وهله‌ای جدید از ویژوال استودیو که این آنالایزر جدید در آن نصب شده‌است، آغاز شود. البته باید دقت داشت که در اینجا باید پروژه‌ی CodingStandards.Vsix را به عنوان پروژه‌ی آغازین ویژوال استودیو معرفی کنید؛ چون پروژه‌ی class library آنالایزرها را نمی‌توان مستقیما اجرا کرد. همچنین یکبار کل solution را نیز build کنید.
پس از اینکه وهله‌ی جدید ویژوال استودیو شروع به کار کرد (بار اول اجرای آن کمی زمانبر است؛ زیرا باید تنظیمات وهله‌ی ویژه‌ی اجرای افزونه‌ها را از ابتدا اعمال کند)، همان پروژه‌ی Student ابتدای بحث را در آن باز کنید.


نتیجه‌ی اعمال این افزونه‌ی جدید را در تصویر فوق ملاحظه می‌کنید. زیر سطرهای دارای فیلد عمومی، خط قرمز کشیده شده‌است (به علت تعریف DiagnosticSeverity.Error). همچنین حالت فعلی و حالت برطرف شده را نیز با رنگ‌های قرمز و سبز می‌توان مشاهده کرد. کلیک بر روی گزینه‌ی make private، سبب اصلاح خودکار آن سطر می‌گردد.


روش دوم آزمایش یک Roslyn Analyzer

همانطور که از انتهای بحث قسمت دوم به‌خاطر دارید، این آنالایزرها را می‌توان به کامپایلر نیز معرفی کرد. روش انجام اینکار در ویژوال استودیوی 2015 در تصویر ذیل نمایش داده شده‌است.


نود references را باز کرده و سپس بر روی گزینه‌ی analyzers کلیک راست نمائید. در اینجا گزینه‌ی Add analyzer را انتخاب کنید. در صفحه‌ی باز شده بر روی دکمه‌ی browse کلیک کنید. در اینجا می‌توان فایل اسمبلی موجود در پوشه‌ی CodingStandards\bin\Debug را به آن معرفی کرد.


بلافاصله پس از معرفی این اسمبلی، آنالایزر آن شناسایی شده و همچنین فعال می‌گردد.


در این حالت اگر برنامه را کامپایل کنیم، با خطاهای جدید فوق متوقف خواهیم شد و برنامه کامپایل نمی‌شود (به علت تعریف DiagnosticSeverity.Error).