مطالب
مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت ششم - MobX چیست؟
پیش از بحث در مورد «مدیریت حالت»، باید با مفهوم «حالت» آشنا شد. «حالت» در اینجا همان لایه‌ی داده‌های برنامه است. زمانیکه بحث React و کتابخانه‌های مدیریت حالت آن مطرح می‌شود، می‌توان گفت حالت، شیءای است حاوی اطلاعاتی که برنامه با آن سر و کار دارد. برای مثال اگر برنامه‌ای قرار است لیستی از موارد را نمایش دهد، حالت برنامه، حاوی اشیاء متناظری خواهد بود. حالت، بر روی نحوه‌ی رفتار و رندر کامپوننت‌های React تاثیر می‌گذارد. بنابراین مدیریت حالت، روشی است برای ردیابی و مدیریت داده‌های مورد استفاده‌ی در برنامه و تقریبا تمام برنامه‌ها به نحوی نیاز به آن‌را خواهند داشت.
داشتن یک کتابخانه‌ی مدیریت حالت برای برنامه‌های React بسیار مفید است؛ خصوصا اگر این برنامه پیچیده باشد و برای مثال در آن نیاز به اشتراک گذاری داده‌ها، بین دو کامپوننت یا بیشتر که در یک رده سلسه مراتبی قرار نمی‌گیرند، وجود داشته باشد. اما حتی اگر از یک کتابخانه‌ی مدیریت حالت استفاده شود، شاید راه حلی را که ارائه می‌کند آنچنان تمیز و قابل انتظار نباشد. با MobX می‌توان از ساختارهای پیچیده‌ی شیءگرا به سادگی استفاده کرد (mutation مستقیم اشیاء در آن مجاز است) و همچنین برای کار با آن به همراه React، نیاز به کدهای کمتری است نسبت به Redux. در اینجا از مفاهیم Reactive programming استفاده می‌شود؛ اما سعی می‌کند پیچیدگی‌های آن‌را مخفی کند. در نام MobX، حرف X به Reactive بودن آن اشاره می‌کند (مانند RxJS) و ob آن از observable گرفته شده‌است. M هم به حرف ابتدای نام شرکتی اشاره می‌کند که این کتابخانه را ایجاد کرده‌است.


خواص محاسبه شده در جاوا اسکریپت

برای کار با MobX، نیاز است تا ابتدا با یکسری از مفاهیم آن آشنا شد؛ مانند خواص محاسبه شده (computed properties). برای مثال در اینجا یک کلاس متداول جاوا اسکریپتی را داریم:
class Person {
    constructor(firstName, lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
    }

    fullName() {
      return `${this.firstName} ${this.lastName}`;
    }
}
که در آن از طریق سازنده، دو پارامتر نام و نام خانوادگی دریافت شده و سپس به دو خاصیت جدید، نسبت داده شده‌اند. اکنون برای محاسبه‌ی نام کامل، که حاصل جمع این دو است، می‌توان متد fullName را به این کلاس اضافه کرد. روش کار با آن نیز به صورت زیر است:
const person = new Person('Vahid', 'N');
person.firstName; // 'Vahid'
person.lastName; // 'N'
person.fullName; // function fullName() {…}
اگر بر اساس متغیر person که بیانگر وهله‌ای از شیء Person است، سعی در خواندن مقادیر خواص شیء ایجاد شده کنیم، آن‌ها را دریافت خواهیم کرد. اما ذکر person.fullName (بدون هیچ () در مقابل آن)، تنها اشاره‌گری را به آن متد بازگشت می‌دهد و نه نام کامل را که البته یکی از ویژگی‌های جالب جاوا اسکریپت است و امکان ارسال آن‌را به سایر متدها، به صورت پارامتر میسر می‌کند.
در ES6 برای اینکه تنها با ذکر person.fullName بدون هیچ پرانتزی در مقابل آن بتوان به مقدار کامل fullName رسید، می‌توان از روش زیر و با ذکر واژه‌ی کلیدی get، در پیش از نام متد، استفاده کرد:
class Person {
    constructor(firstName, lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
    }

    get fullName() {
      return `${this.firstName} ${this.lastName}`;
    }
}
در اینجا هرچند fullName هنوز یک متد است، اما اینبار فراخوانی person.fullName، حاصل جمع دو خاصیت را بازگشت می‌دهد و نه اشاره‌گری به آن متد را.
اگر شبیه به همین قطعه کد را بخواهیم در ES5 پیاده سازی کنیم، روش آن به صورت زیر است:
function Person(firstName, lastName) {
   this.firstName = firstName;
   this.lastName = lastName;
}

Object.defineProperty(Person.prototype, 'fullName', {
   get: function () {
      return this.firstName + ' ' + this.lastName;
   }
});
به این ترتیب می‌توان یک خاصیت محاسبه شده‌ی ویژه‌ی ES5 را تعریف کرد.

اکنون فرض کنید قسمتی از state برنامه‌ی React، قرار است خاصیت ویژه‌ی fullName را نمایش دهد. برای اینکه UI برنامه با تغییرات نام و نام خانوادگی، متوجه تغییرات fullName که یک خاصیت محاسباتی است، شود و آن‌را رندر مجدد کند، باید در طی یک حلقه‌ی بی‌نهایت، مدام آن‌را فراخوانی کند و نتیجه‌ی جدید را با نتیجه‌ی قبلی محاسبه کرده و تغییرات را نمایش دهد. اینجا است که MobX یک چنین پیاده سازی‌هایی را به کمک مفهوم decorators، ساده می‌کند.


Decorators در جاوا اسکریپت

تزئین کننده‌ها یا decorators در سایر زبان‌های برنامه نویسی نیز وجود دارند؛ اما پیاده سازی آن‌ها در جاوا اسکریپت هنوز در مرحله‌ی آزمایشی است. Decorators در جاوا اسکریپت چیزی نیستند بجز بیان زیبای higher-order functions.
higher-order functions، توابعی هستند که توابع دیگر را با ارائه‌ی قابلیت‌های بیشتری، محصور می‌کنند. به همین جهت هر کاری را که بتوان با تزئین کننده‌ها انجام داد، همان را با توابع معمولی جاوا اسکریپتی نیز می‌توان انجام داد. یک نمونه از این higher-order functions را در سری جاری تحت عنوان higher-order components با متد connect کتابخانه‌ی react-redux مشاهده کرده‌ایم. متد connect، متدی است که متدهای نگاشت state به props و نگاشت dispatch به props را دریافت کرده و سپس یک کامپوننت را نیز دریافت می‌کند و آن‌را به صورت محصور شده‌ای ارائه می‌دهد تا بجای کامپوننت اصلی مورد استفاده قرار گیرد؛ به یک چنین کامپوننت‌هایی، higher-order components گفته می‌شود.

برای تعریف تزئین کننده‌ها، به نحوه‌ی پیاده سازی Object.defineProperty در مثال فوق دقت کنید:
Object.defineProperty(Person.prototype, 'fullName', {
    enumerable: false,
    writable: false,
    get: function () {
      return this.firstName + ' ' + this.lastName;
    }
});
در اینجا Person.prototype یک target است. ثابت fullName، یک کلید است. سایر خواص ذکر شده، مانند enumerable، writable و get، تحت عنوان Descriptor شناخته می‌شوند.
در ذیل روش تعریف یک تزئین کننده را مشاهده می‌کنید که دقیقا از یک چنین الگویی پیروی می‌کند:
function decoratorName(target, key, descriptor) {
 // …
}
برای مثال در اینجا روش پیاده سازی تزئین کننده‌ی readonly را ملاحظه می‌کنید:
function readonly(target, key, descriptor) {
   descriptor.writable = false;
   return descriptor;
}
سپس روش اعمال آن به یک خاصیت محاسباتی در کلاس Person به صورت زیر است:
class Person {
    constructor(firstName, lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
    }

    @readonly get fullName() {
      return `${this.firstName} ${this.lastName}`;
    }
}
ذکر یک تزئین کننده با @ شروع می‌شود. سپس متد fullName را دریافت کرده و نگارش جدیدی از آن‌را بازگشت می‌دهد؛ بطوریکه readonly باشد.


مثال‌هایی از تزئین کننده‌ها

برای نمونه می‌توان تزئین کننده‌ی bindThis@ را طراحی کرد تا کار bind شیء this را به متدهای کامپوننت‌های React انجام دهد و یا کتابخانه‌ای به نام core-decorators وجود دارد که به صورت زیر نصب می‌شود:
> npm install core-decorators
و به همراه این تزئین کننده‌ها می‌باشد:
@autobind
@deprecate
@readonly
@memoize
@debounce
@profile
برای مثال autobind آن، همان کار bind شیء this را انجام می‌دهد. deprecate جهت نمایش یک اخطار، در کنسول توسعه دهندگان مرورگر، جهت گوشزد کردن منسوخ بودن قسمتی از برنامه، استفاده می‌شود.

نمونه‌ی دیگری از این کتابخانه‌ها lodash-decorators است که تعدادی دیگر از تزئین کننده‌ها را ارائه می‌کند.


MobX چگونه کار می‌کند؟

انجام یکسری از کارها با Redux مشکل است؛ برای مثال تغییر دادن یک شیء تو در توی پیچیده که شامل تهیه‌ی یک کپی از آن، اعمال تغییرات و غیره‌است. اما با MobX می‌توان با اشیاء جاوا اسکریپتی به همان صورتی که هستند کار کرد. برای مثال آرایه‌ای را با متدهای push و pop تغییر داد (mutation اشیاء مجاز است) و یا خواص اشیاء را به صورت مستقیم ویرایش کرد، در این حالت MobX اعلام می‌کند که ... من می‌دانم که چه تغییری صورت گرفته‌است. بنابراین سبب رندر مجدد UI خواهم شد.


ایجاد یک برنامه‌ی خالی React برای آزمایش MobX

در اینجا برای بررسی MobX، یک پروژه‌ی جدید React را ایجاد می‌کنیم:
> create-react-app state-management-with-mobx-part1
> cd state-management-with-mobx-part1
> npm start
در ادامه کتابخانه‌ی mobx را نیز نصب می‌کنیم. برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save mobx
البته برای کار با MobX، الزاما نیازی به طی مراحل فوق نیست؛ ولی چون این قالب، یک محیط آماده‌ی کار با ES6 را فراهم می‌کند، به سادگی می‌توان فایل index.js آن‌را خالی کرد و سپس شروع به کدنویسی و آزمایش MobX نمود.


مثالی از MobX، مستقل از React

در اینجا نیز همانند روشی که در بررسی Redux در پیش گرفتیم، ابتدا MobX را به صورت کاملا مستقل از React، با یک مثال بررسی می‌کنیم و سپس در قسمت‌های بعد آن‌را به React متصل می‌کنیم. برای این منظور ابتدا فایل src\index.js را به صورت زیر تغییر می‌دهیم:
import { autorun, observable } from "mobx";

import React from "react";
import ReactDOM from "react-dom";

ReactDOM.render(
  <div>
    <input type="text" id="text-input" />
    <div id="text-display"></div>
    <div id="text-display-uppercase"></div>
  </div>,
  document.getElementById("root")
);

const input = document.getElementById("text-input");
const textDisplay = document.getElementById("text-display");
const loudDisplay = document.getElementById("text-display-uppercase");

console.log({ observable, autorun, input, textDisplay, loudDisplay });
در اینجا یک text-box، به همراه دو div، در صفحه رندر خواهند شد که قرار است با ورود اطلاعاتی در text-box، یکی از آن‌ها (text-display) این اطلاعات را به صورت معمولی و دیگری (text-display-uppercase) آن‌را به صورت uppercase نمایش دهد. روش کار انجام شده هم مستقل از React است و به صورت مستقیم، با استفاده از DOM API و document.getElementById عمل شده‌است. همچنین در ابتدای این فایل، دو import را از کتابخانه‌ی mobx مشاهده می‌کنید.
- با استفاده از observable می‌خواهیم تغییرات یک شیء جاوا اسکریپتی را تحت نظر قرار داده و هر زمانیکه تغییری در شیء رخ داد، از آن مطلع شویم.
برای مثال شیء ساده‌ی جاوا اسکریپتی زیر را در نظر بگیرید:
{
  value: "Hello world!",
  get uppercase() {
    return this.value.toUpperCase();
  }
}
این شیء دارای دو خاصیت است که یکی معمولی و دیگری به صورت یک خاصیت محاسباتی، تعریف شده‌است. مشکلی که با این شیء وجود دارد این است که اگر مقدار خاصیت value آن تغییر کند، از آن مطلع نخواهیم شد تا پس از آن برای مثال در مورد رندر مجدد DOM، تصمیم گیری شود. چون از دیدگاه React، مقدار ارجاع به این شیء با تغییر خواص آن، تغییری نمی‌کند. به همین جهت اگر نحوه‌ی مقایسه، بر اساس مقایسه‌ی ارجاعات به اشیاء باشد (strict === reference check)، چون شیء تغییر یافته نیز به همان شیء اصلی اشاره می‌کند، بنابراین دارای ارجاع یکسانی خواهند بود و سبب رندر مجدد DOM نمی‌شوند.
به همین جهت اینبار شیء فوق را توسط یک observable ارائه می‌دهیم، تا بتوانیم به تغییرات خواص آن گوش فرا دهیم:
const text = observable({
  value: "Hello world!",
  get uppercase() {
    return this.value.toUpperCase();
  }
});
در ادامه یک EventListener را به text-box تعریف شده اضافه کرده و در رخ‌داد keyup آن، سبب تغییر خاصیت value شیء text فوق، بر اساس مقدار تایپ شده می‌شویم:
input.addEventListener("keyup", event => {
   text.value = event.target.value;
});
اکنون چون شیء text، یک observable است، هر زمانیکه که خاصیتی از آن تغییر می‌کند، می‌خواهیم بر اساس آن، DOM را به صورت دستی به روز رسانی کنیم. برای اینکار نیاز به متد autorun دریافتی از mobx خواهیم داشت:
autorun(() => {
   textDisplay.textContent = text.value;
   loudDisplay.textContent = text.uppercase;
});
هر زمانیکه شیء observable ای که داخل متد autorun تحت نظر قرار گرفته شده، تغییر کند، سبب اجرای callback method ارسالی به آن خواهد شد. برای مثال در اینجا چون text.value را به event.target.value متصل کرده‌ایم، هربار که کلیدی فشرده می‌شود، سبب بروز تغییری در خاصیت value خواهد شد. در نتیجه‌ی آن، autorun اجرا شده و مقادیر درج شده‌ی در divهای صفحه را بر اساس خواص value و uppercase شیء text، تغییر می‌دهد:

برای آزمایش آن، برنامه را اجرا کرده و متنی را داخل textbox وارد کنید:


نکته‌ی جالب اینجا است که هرچند فقط خاصیت value را تغییر داده‌ایم (تغییر مستقیم خواص یک شیء؛ بدون نیاز به ساخت یک clone از آن)، اما خاصیت محاسباتی uppercase نیز به روز رسانی شده‌است.

زمانیکه mobx را به یک برنامه‌ی React متصل می‌کنیم، قسمت autorun، از دید ما مخفی خواهد بود. در این حالت فقط یک شیء معمولی جاوا اسکریپتی را مستقیما تغییر می‌دهیم و ... در نتیجه‌ی آن رندر مجدد UI صورت خواهد گرفت.


یک observable چگونه کار می‌کند؟

در اینجا یک شبه‌کد را که بیانگر نحوه‌ی عملکرد یک observable است، مشاهده می‌کنید:
const onChange = (oldValue, newValue) => {
  // Tell MobX that this value has changed.
}

const observable = (value) => {
  return {
    get() { return value; },
    set(newValue) {
      onChange(this.get(), newValue);
      value = newValue;
    }
  }
}
یک observable هنگامیکه شی‌ءای را در بر می‌گیرد. هر زمانیکه مقدار جدیدی را به خاصیتی از آن نسبت دادیم، سبب فراخوانی متد onChange شده و به این صورت است که کتابخانه‌ی MobX متوجه تغییرات می‌گردد و بر اساس آن امکان ردیابی تغییرات را میسر می‌کند.


کدهای کامل این قسمت را می‌توانید از اینجا دریافت کنید: state-management-with-mobx-part1.zip
نظرات مطالب
سفارشی سازی ASP.NET Core Identity - قسمت سوم - نرمال سازها و اعتبارسنج‌ها
سلام 
من از کلاس CustomUserValidator استفاده کردم و تمام موارد صحیح است بجز یک حالت اگر در اینپوت Username ما نام کاربری را اشتباه وارد کنیم خطای این قسمت صحیح است منتها دو خطای دیگر صادر میشود که ایمیل معتبر نیست و ... . در واقع در زمان خطاهای سمت کلاینت وقتیکه در اینپوت Username خطا صادر شود خطاهای قسمت ایمیل هم نمایش داده میشود و به همچنین برای اینپوت Email . چطور میتونم فقط خطای مربوط به اینپوتی را که فوکوس در آن است نمایش دهم نه هر دو اینپوت ؟ممنون
نظرات مطالب
پشتیبانی از حذف و به‌روز رسانی دسته‌ای رکوردها در EF 7.0

یک نکته‌ی تکمیلی: روش استفاده از خواص راهبری، در به‌روزسانی‌های دسته‌ای

در همین مثال مطلب جاری، فرض کنید موجودیت کاربر، به همراه خاصیت محاسباتی تعداد کتاب‌ها (NumberOfBooks) هم هست:

public class User
{
    // ...

    public virtual List<Book> Books { get; set; } = new();

    public int NumberOfBooks { get; set; }
}

اگر سعی کنیم این خاصیت را به صورت زیر به‌روز رسانی کنیم که در آن مقدار تعداد کتاب‌ها به کمک خاصیت راهبری Books محاسبه شود:

await context.Users.Where(x=> x.Id == 1).ExecuteUpdateAsync(s => s.SetProperty(user => user.NumberOfBooks,
                user => user.Books.Count()));

به خطای زیر می‌رسیم:

... could not be translated. Additional information: Translation of member 'Books' on entity type 'User' failed. This commonly occurs when the specified member is unmapped.

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

await context.Users
             .Where(user => user.Id == 1)
             .Select(user => new
             {
                user.NumberOfBooks,
                BooksCount = user.Books.Count()
             })
             .ExecuteUpdateAsync(s => s.SetProperty(arg => arg.NumberOfBooks, arg => arg.BooksCount));

در اینجا تنها کاری که انجام شده، انجام محاسبات و نگاشت‌های لازم، پیش از فراخوانی متد ExecuteUpdateAsync است.

مطالب
پردازش داده‌های جغرافیایی به کمک SQL Server و Entity framework
پشتیبانی SQL Server از Spatial data

از SQL Server 2008 به بعد، نوع داده جدیدی به نام geography به نوع‌های قابل تعریف ستون‌ها اضافه شده‌است. در این نوع ستون‌ها می‌توان طول و عرض جغرافیایی یک نقطه را ذخیره کرد و سپس به کمک توابع توکاری از آن‌ها کوئری گرفت.


در اینجا نمونه‌ای از نحوه‌ی تعریف و همچنین مقدار دهی این نوع ستون‌ها را مشاهده می‌کنید:
 CREATE TABLE [Geo](
[id] [int] IDENTITY(1,1) NOT NULL,
[Location] [geography] NULL
)

 insert into Geo( Location , long, lat ) values
( geography::STGeomFromText ('POINT(-121.527200 45.712113)', 4326))
متد geography::STGeoFromText یک SQL CLR function است. این متد در مثال فوق، مختصات یک نقطه را دریافت کرده‌است. همچنین نیاز دارد بداند که این نقطه توسط چه نوع سیستم مختصاتی ارائه می‌شود. عدد 4326 در اینجا یک SRID یا Spatial Reference System Identifier استاندارد است. برای نمونه اطلاعات ارائه شده توسط Google و یا Bing توسط این استاندارد ارائه می‌شوند.
در اینجا متدهای توکار دیگری مانند geography::STDistance برای یافتن فاصله مستقیم بین نقاط نیز ارائه شد‌ه‌اند. خروجی آن بر حسب متر است.


پشتیبانی از Spatial Data در Entity framework

پشتیبانی از نوع مخصوص geography، در EF 5 توسط نوع داده‌ای DbGeography ارائه شد. این نوع داده‌ای immutable است. به این معنا که پس از نمونه سازی، دیگر مقدار آن قابل تغییر نیست.
در اینجا برای نمونه مدلی را مشاهده می‌کنید که از نوع داده‌ای DbGeography استفاده می‌کند:
using System.Data.Entity.Spatial;

namespace EFGeoTests.Models
{
    public class GeoLocation
    {
        public int Id { get; set; }
        public DbGeography Location { get; set; }
        public string Name { get; set; }
        public string Type { get; set; }

        public override string ToString()
        {
            return string.Format("Name:{0}, Location:{1}", Name, Location);
        }
    }
}
به همراه یک Context، تا کلاس GeoLocation در معرض دید EF قرار گیرد:
using System;
using System.Data.Entity;
using EFGeoTests.Models;

namespace EFGeoTests.Config
{
    public class MyContext : DbContext
    {
        public DbSet<GeoLocation> GeoLocations { get; set; }

        public MyContext()
            : base("Connection1")
        {
            this.Database.Log = sql => Console.Write(sql);
        }
    }
}
برای مقدار دهی خاصیت Location از نوع DbGeography می‌توان از متد ذیل استفاده کرد که بسیار شبیه به متد geography::STGeoFromText عمل می‌کند:
   private static DbGeography createPoint(double longitude, double latitude,  int coordinateSystemId = 4326)
  {
       var text = string.Format(CultureInfo.InvariantCulture.NumberFormat,"POINT({0} {1})", longitude, latitude);
       return DbGeography.PointFromText(text, coordinateSystemId);
  }


تهیه منبع داده‌ی جغرافیایی

برای تدارک یک مثال واقعی جغرافیایی، نیاز به اطلاعاتی دقیق داریم. این نوع اطلاعات عموما توسط یک سری فایل مخصوص به نام Shapefiles که حاوی اطلاعات برداری جغرافیایی هستند ارائه می‌شوند. برای نمونه اطلاعات جغرافیایی به روز ایران را از آدرس ذیل می‌توانید دریافت کنید:
http://download.geofabrik.de/asia/iran.html
http://download.geofabrik.de/asia/iran-latest.shp.zip

پس از دریافت این فایل، به تعدادی فایل با پسوندهای shp، shx و dbf خواهیم رسید.
فایل‌های shp بیانگر فرمت اشکال ذخیره شده هستند. فایل‌های shx یک سری ایندکس بوده و فایل‌های dbf از نوع بانک اطلاعاتی dBase IV می‌باشند.
همچنین اگر فایل‌های prj را باز کنید، یک چنین اطلاعاتی در آن موجودند:
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
نکته‌ی مهمی که در اینجا باید مدنظر داشت، استاندارد GCS_WGS_1984 آن است. این استاندارد معادل است با استاندارد EPSG 4326. عدد 4326 آن جهت ثبت این اطلاعات در یک بانک اطلاعاتی SQL Server حائز اهمیت است (پارامتر coordinateSystemId در متد createPoint) و ممکن است از هر فایلی به فایل دیگر متفاوت باشد.



خواند‌ن فایل‌های shp در دات نت

پس از دریافت فایل‌های shp و بانک‌های اطلاعاتی مرتبط با اطلاعات جغرافیایی ایران، اکنون نوبت به پردازش این فایل‌های مخصوص با فرمت بانک اطلاعاتی فاکس پرو مانند، رسیده‌است. برای این منظور می‌توان از پروژه‌ی سورس باز ذیل استفاده کرد:

این پروژه در خواندن فایل‌های shp بدون نقص عمل می‌کند اما توانایی خواندن نام‌های فارسی وارد شده در این نوع بانک‌های اطلاعاتی را ندارد. برای رفع این مشکل، سورس آن را از Codeplex دریافت کنید. سپس فایل Shapefile.cs را گشوده و ابتدای خاصیت Current آن‌را به نحو ذیل تغییر دهید:
        /// <summary>
        /// Gets the current shape in the collection
        /// </summary>
        public Shape Current
        {
            get 
            {
                if (_disposed) throw new ObjectDisposedException("Shapefile");
                if (!_opened) throw new InvalidOperationException("Shapefile not open.");
               
                // get the metadata
                StringDictionary metadata = null;
                if (!RawMetadataOnly)
                {
                    metadata = new StringDictionary();
                    for (int i = 0; i < _dbReader.FieldCount; i++)
                    {
                        string value = _dbReader.GetValue(i).ToString();
                        if (_dbReader.GetDataTypeName(i) == "DBTYPE_WVARCHAR")
                        {
                            // برای نمایش متون فارسی نیاز است
                            value = Encoding.UTF8.GetString(Encoding.GetEncoding(720).GetBytes(value));
                        }
                        metadata.Add(_dbReader.GetName(i),
                            value);
                    }
                }
در اینجا فقط سطر استفاده از Encoding خاصی با شماره 720 و تبدیل آن به UTF8 اضافه شده‌است. پس از آن بدون مشکل می‌توان برچسب‌های فارسی را از فایل‌های dBase IV این نوع بانک‌های اطلاعاتی استخراج کرد (اصلاح شده‌ی آن در فایل پیوست مطلب موجود است).
using System.Collections.Generic;
using System.Linq;
using Catfood.Shapefile;

namespace EFGeoTests
{
    public class MapPoint
    {
        public Dictionary<string, string> Metadata { set; get; }
        public double X { set; get; }
        public double Y { set; get; }
    }

    public static class ShapeReader
    {
        public static IList<MapPoint> ReadShapeFile(string path)
        {
            var results = new List<MapPoint>();

            using (var shapefile = new Shapefile(path))
            {
                foreach (var shape in shapefile)
                {
                    if (shape.Type != ShapeType.Point)
                        continue;

                    var shapePoint = shape as ShapePoint;
                    if (shapePoint == null)
                        continue;


                     var metadataNames = shape.GetMetadataNames();
                    if(!metadataNames.Any())
                        continue;

                    var metadata = new Dictionary<string, string>();
                    foreach (var metadataName in metadataNames)
                    {
                        metadata.Add(metadataName,shape.GetMetadata(metadataName));
                    }

                    results.Add(new MapPoint
                    {
                        Metadata = metadata,
                        X = shapePoint.Point.X,
                        Y = shapePoint.Point.Y
                    });
                }
            }

            return results;
        }
    }
}
در کدهای فوق به کمک کتابخانه‌ی C# Esri Shapefile Reader، اطلاعات نقاط بانک اطلاعاتی shape files را خوانده و به صورت لیست‌هایی از MapPoint بازگشت می‌دهیم. نکته‌ی مهم آن، Metadata است که از هر فایلی به فایل دیگر می‌توان متفاوت باشد. به همین جهت این اطلاعات را به شکل ویژگی‌های key/value در این نوع بانک‌های اطلاعاتی ذخیره می‌کنند.


افزودن اطلاعات جغرافیایی به بانک اطلاعاتی SQL Server به کمک Entity framework

فایل places.shp را در مجموعه فایل‌هایی که در ابتدای بحث عنوان شدند، می‌توانید مشاهده کنید. قصد داریم اطلاعات نقاط آن‌را به مدل GeoLocation انتساب داده و سپس ذخیره کنیم:
            var points = ShapeReader.ReadShapeFile("IranShapeFiles\\places.shp");
            using (var context = new MyContext())
            {
                context.Configuration.AutoDetectChangesEnabled = false;
                context.Configuration.ProxyCreationEnabled = false;
                context.Configuration.ValidateOnSaveEnabled = false;

                if (context.GeoLocations.Any())
                    return;

                foreach (var point in points)
                {
                    context.GeoLocations.Add(new GeoLocation
                    {
                        Name = point.Metadata["name"],
                        Type = point.Metadata["type"],
                        Location = createPoint(point.X, point.Y)
                    });
                }

                context.SaveChanges();
            }
تعریف متد createPoint را که بر اساس X و Y نقاط، معادل قابل پذیرش آن‌را جهت SQL Server تهیه می‌کند، در ابتدای بحث مشاهده کردید.
در فایل‌های مرتبط با places.shp، متادیتا name، معادل نام شهرهای ایران است و type آن بیانگر شهر، روستا و امثال آن می‌باشد.
پس از اینکه اطلاعات مکان‌های ایران، در SQL Server ذخیره شدند، نمایش بصری آن‌ها را در management studio نیز می‌توان مشاهده کرد:



کوئری گرفتن از اطلاعات جغرافیایی

فرض کنید می‌خواهیم مکان‌هایی را با فاصله کمتر از 5 کیلومتر از تهران پیدا کنیم:
            var tehran = createPoint(51.4179604, 35.6884243);

            using (var context = new MyContext())
            {
                // find any locations within 5 kilometers ordered by distance
                var locations = context.GeoLocations
                    .Where(loc => loc.Location.Distance(tehran) < 5000)
                    .OrderBy(loc => loc.Location.Distance(tehran))
                    .ToList();

                foreach (var location in locations)
                {
                    Console.WriteLine(location.Name);
                }
            }
همانطور که پیشتر نیز عنوان شد، متد Distance بر اساس متر کار می‌کند. به همین جهت برای تعریف 5 کیلومتر به نحو فوق عمل شده‌است. همچنین نحوه‌ی مرتب سازی اطلاعات نیز بر اساس فاصله از یک مکان مشخص صورت گرفته‌است.
و یا اگر بخواهیم دقیقا بر اساس مختصات یک نقطه، مکانی را بیابیم، می‌توان از متد SpatialEquals استفاده کرد:
            var tehran = createPoint(51.4179604, 35.6884243);
            using (var context = new MyContext())
            {
                // find any locations within 5 kilometers ordered by distance
                var tehranLocation = context.GeoLocations.FirstOrDefault(loc => loc.Location.SpatialEquals(tehran));
                if (tehranLocation != null)
                {
                    Console.WriteLine(tehranLocation.Type);
                }
            }

کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید:
EFGeoTests.zip
 
مطالب
مزیت‌های استفاده از رویه‌های ذخیره شده؛ واقعیت یا توهم؟!

متن زیر یک سری نکات و یا شاید توهماتی را مطرح می‌کند که در مورد رویه‌های ذخیره شده در اس کیوال سرور رایج هستند.

1- رویه‌های ذخیره شده در مقابل SQL Injection مقاوم هستند. کوئری‌های Ad hoc همیشه این آسیب پذیری را به همراه دارند.
نادرست است! رویه‌های ذخیره شده‌ای که رشته‌ها را به صورت پارامتر دریافت کرده و آن‌ها را به صورت یک عبارت sql اجرا می‌کنند، آسیب پذیر هستند. اگر هنگام استفاده از کوئری‌های Ad hoc از پارامترها استفاده شود، در برابر حملات SQL Injection مصون خواهید بود.

2- execution plan رویه‌های ذخیره شده کش می‌شوند اما این Plan برای کوئری‌های Ad hoc هر بار محاسبه و تولید می‌گردد.
نادرست است! اس کیوال سرور تا این اندازه بی هوش نیست! اگر execution plan ایی موجود باشد حتما استفاده خواهد شد و برای موتور اس کیوال سرور اصلا اهمیتی ندارد که کوئری در حال اجرا از یک رویه ذخیره شده صادر شده است یا از یک کوئری Ad hoc . رویه‌های ذخیره شده پیش کامپایل شده نیستند و مانند تمامی کوئری‌های دیگر در زمان اجرا کامپایل می‌شوند.

3- زمانیکه از رویه ذخیره شده استفاده می‌کنید همه چیز را در یک مکان به صورت متمرکز و مجتمع خواهید داشت (مدیریت بهتر)
نادرست است! در یک مکان متمرکز در اختیار شما نیستند. برنامه جای خود را دارد و رویه‌های ذخیره شده در دیتابیس در جای دیگری قرار دارند و برای مثال اگر قرار باشد یک پارامتر را به رویه ذخیره شده خود اضافه کنید، کدهای شما نیز باید تغییر کنند.

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

5- می‌توان رویه ذخیره شده را بدون نیاز به توزیع مجدد برنامه تغییر داد.
این مورد تا حدودی صحیح است. اگر تنها بحث بهینه سازی و امثال آن مطرح باشد صحیح است اما اگر واقعا نیاز به تغییر یک کوئری در رویه ذخیره شده وجود داشته باشد به احتمال زیاد برنامه نیز باید دستخوش تغییراتی گردد تا این دو با هم هماهنگ شوند.

نظر شما چیست؟


بازخوردهای دوره
مثال - نمایش درصد پیشرفت عملیات توسط SignalR
جهت اطلاع؛ همیشه آخرین نسخه 1.x مخصوص دات نت 4 را در صفحه ذیل بررسی کنید:
http://www.nuget.org/packages/Microsoft.AspNet.SignalR
برای مثال در این تاریخ Microsoft ASP.NET SignalR 1.1.4 نسخه آخر 1.x است و از لحاظ امنیتی نیاز است این به روز رسانی صورت گیرد.  
مطالب
نحوه کار با ftp - بخش اول
امروز می‌خوام نحوه کار با FTP بصورت ساده برای کاربران و برنامه نویسان مبتدی رو آموزش بدم.

برای استفاده از FTP نیاز به یک اکانت FTP در سایت مورد نظر بهمراه دسترسی به پوشه ای مشخص می‌باشد.
برای مثال ما یک اکانت FTP در سایت dotnettip.info داریم که به پوشه upload دسترسی داره.

ابتدا در فایل Web.config و در بین تگ های  appSettings مقادیر زیر را برای دسترسی به اکانت و نام کاربری و رمز عبور ذخیره می‌کنیم.
<add key="FtpAddress" value="ftp://ftp.dotnetips.info" />
<add key="FtpUser" value="uploadcenter" />
<add key="FtpPass" value="123123" />
<add key="FolderPath" value="~/Upload/" />
*نکته : برای امنیت بیشتر و دسترسی به اطلاعات اکانت می‌شود از روش‌های دیگری نیز استفاده کرد.

در ادامه یک کلاس در App_code  پروژه خود با نام FTPHelper ایجاد می‌کنیم و کد زیر را در آن قرار می‌دهیم:
تکه کد بالا برای ست کردن مقادیر نام کاربری و رمز عبور و آدرس FTP در کلاس مذکور که بصورت پیش‌فرض از web.config پر می‌شود ایجاد و بکار خواهد رفت.
using System.Net;
using System.IO;
using System.Configuration; 


public class FtpHelper
{
public FtpHelper()
{
       //Default Value Set From Application 
         _hostname = ConfigurationManager.AppSettings.GetValues("FtpAddress")[0]; 
        _username = ConfigurationManager.AppSettings.GetValues("FtpUser")[0]; 
        _password = ConfigurationManager.AppSettings.GetValues("FtpPass")[0];  
}
    
    #region "Properties"
    private string _hostname;
    /// <summary>
    /// Hostname
    /// </summary>
    /// <value></value>
    /// <remarks>Hostname can be in either the full URL format
    /// ftp://ftp.myhost.com or just ftp.myhost.com
    /// </remarks>
    public string Hostname
    {
        get
        {
            if (_hostname.StartsWith("ftp://"))
            {
                return _hostname;
            }
            else
            {
                return "ftp://" + _hostname;
            }
        }
        set
        {
            _hostname = value;
        }
    }
    private string _username;
    /// <summary>
    /// Username property
    /// </summary>
    /// <value></value>
    /// <remarks>Can be left blank, in which case 'anonymous' is returned</remarks>
    public string Username
    {
        get
        {
            return (_username == "" ? "anonymous" : _username);
        }
        set
        {
            _username = value;
        }
    }
    private string _password;
    public string Password
    {
        get
        {
            return _password;
        }
        set
        {
            _password = value;
        }
    }


    #endregion
}


سپس فضای نام‌های زیر را در کلاس خود قرار می‌دهیم.
using System.Net;
using System.IO;
 
حالا برای بارگذاری فایل می‌توانیم از یک تابع بصورت shared استفاده کنیم که بتوان با دادن آدرس فایل بصورت فیزیکی به تابع و مشخص کردن پوشه مورد نظر آنرا در هاست مقصد (FTP) بارگذاری کرد.توجه داشته باشیذ که تابع فوق نیازی به قرار گرفتن در کلاس بالا (FtpHelper) ندارد.یعنی می‌توان آنرا در هرجای برنامه پیاده سازی نمود.
public static bool Upload(string fileUrl)
        {
            if (File.Exists(fileUrl))
            {
                FtpHelper ftpClient = new FtpHelper();
                string ftpUrl = ftpClient.Hostname + System.IO.Path.GetFileName(fileUrl);


                FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(ftpUrl);
                ftp.Credentials = new NetworkCredential(ftpClient.Username, ftpClient.Password);

                ftp.KeepAlive = true;
                ftp.UseBinary = true;
                ftp.Timeout = 3600000;
                ftp.KeepAlive = true;
                ftp.Method = WebRequestMethods.Ftp.UploadFile;

                const int bufferLength = 102400;
                byte[] buffer = new byte[bufferLength];
                int readBytes = 0;

                //open file for reading
                using (FileStream fs = File.OpenRead(fileUrl))
                {
                    try
                    {
                        //open request to send
                        using (Stream rs = ftp.GetRequestStream())
                        {
                            do
                            {
                                readBytes = fs.Read(buffer, 0, bufferLength);
                                fs.Write(buffer, 0, readBytes);
                            } while (!(readBytes < bufferLength));
                            rs.Close();
                        }

                    }
                    catch (Exception)
                    {
                     //Optional Alert for Exeption To Application Layer
                       //throw (new ApplicationException("بارگذاری فایل با خطا  رو به رو شد"));
 
                    }
                    finally
                    {
                        //ensure file closed
                        //fs.Close();
                    }

                }

                ftp = null;
                return true;
            }
            return false;



        }


تکه کد بالا فایل مورد نظر را در صورت وجود به صورت تکه‌های 100 کیلوبایتی بر روی ftp بارگذاری می‌کند، که می‌توانید مقدار آنرا نیز تغییر دهید.
اینکار باعث افزایش سرعت بارگذاری در فایل‌های با حجم بالا برای بارگذاری می‌شود.

در بخش‌های بعدی نحوه ایجاد پوشه ، حذف فایل ، حذف پوشه و دانلود فایل از روی FTP را بررسی خواهیم کرد.
مطالب
کنترل نوع‌های داده با استفاده از EF در SQL Server
ورود سیستم‌های ORM مانند EF تحولی عظیم در در مباحث کار و تغییرات بر روی داده‌ها یا Data Manipulation بود. به طور خلاصه اصلی‌ترین هدف یک ORM، ایجاد فرامین شیء گرا به جای فرامین رابطه‌ای است؛ ولی در این بین نکات دیگری هم مد نظر گرفته شده‌است که یکی از آن‌ها پشتیبانی از چندین دیتابیس هست تا توسعه گران از یک سیستم واحد جهت اتصال به همه‌ی دیتابیس‌ها استفاده کنند و نیازی به دانش اضافه و سیستم جداگانه‌ای برای هر دیتابیس نباشد؛ مانند ADO که در دات نت به چندین دسته نقسیم شده بود و هم اینکه در صورتی که تمایلی به تغییر دیتابیس در آینده داشتید، کدها برای توسعه باز باشند و نیازی به تغییر سیستم نباشد.
ولی اگر کمی بیشتر به دنیای واقعی وارد شویم گاهی اوقات نیاز است که تنها بر روی یک دیتابیس فعالیت کنیم و یک دیتابیس نیاز است تا حد ممکن بهینه طراحی شود تا کارآیی بانک در حال حاضر و به خصوص در آینده تا حدی تضمین شود.
من همیشه در مورد EF یک نظری داشتم و آن اینست که با اینکه یک ORM، یک هدف مهم را در نظر دارد و آن اینست که تا حد ممکن استانداردهایی را که بین تمامی دیتابیس‌ها مشترک است، رعایت کند، ولی باز قابل قبول است اگر بگوییم که کاربران EF انتظار داشته باشند تا اطلاعات بیشتری در مورد sql server در آن نهفته باشد. از یک سو هر دو محصول مایکروسافت هستند و از سوی دیگر مطمئنا توسعه گران محصولات دات نت بیش از هر چیزی به sql server نگاه ویژه‌تری دارند. پس مایکروسافت در کنار حفظ آن ویژگی‌های مشترک، باید به حفظ استانداردهای جدایی برای sql server هم باشد.

تعدادی از برنامه نویسان در هنگام ایجاد Domain Model کم لطفی‌های زیادی را می‌کنند که یکی از آن‌ها عدم کنترل نوع داده‌های خود است. مثلا برای رشته‌ها هیچ محدودیتی را در نظر نمی‌گیرند. شاید در سمت کلاینت اینکار را انجام می‌دهند؛ ولی نکته‌ی مهم در طرف دیتابیس است که چگونه تعریف می‌شود. در این حالت (nvarchar(MAX در نظر گرفته میشود که به معنی اشاره به منطقه دوگیگابایتی از اطلاعات است. در نکات بعدی، قصد داریم این مرحله را یک گام به جلوتر پیش ببریم و آن هم ایجاد نوع داده‌های بهینه‌تر در Sql Server است.

نکته مهم: بدیهی است که تغییرات زیر، ORM شما را تنها به sql server مقید می‌کند که بعدها در صورت تغییر دیتابیس نیاز به حذف موارد زیر را خواهید داشت؛ در غیر اینصورت به مشکل عدم ایجاد دیتابیس برخواهید خورد.

اولین مورد مهم بحث تاریخ و زمان است؛ وقتی ما یک نوع داده را تنها DateTime در نظر بگیریم، در Sql Server هم همین نوع داده وجود دارد و انتخاب میشود. ولی اگر شما واقعا نیازی به این نوع داده نداشته باشید چطور؟ در حال حاضر من بر روی یک برنامه‌ی کارخانه کار میکنم که بخش کارمندان و گارگران آن سه داده زمانی زیر را شامل می‌شود:
        public DateTime BirthDate { get; set; }
        public DateTime HireDate { get; set; }
        public DateTime? LeaveDate { get; set; }

حال به جدول زیر نگاه کنید که هر نوع داده چه مقدار فضا را به خود اختصاص می‌دهد:
  SmallDateTime   4 بایت
  DateTime  8 بایت
  DateTime2  6 تا 8 بایت
  DateTimeOffset   8 تا 10 بایت
  Date  3 بایت
  Time  3 تا 5 بایت

از این جدول چه می‌فهمید؟ با یک نگاه می‌توان فهمید که ساختار بالای من باید 24 بایت گرفته باشد؛ برای ساختاری که هم تاریخ و هم زمان (ساعت) را پشتیبانی می‌کند. ولی با نگاه دقیق‌تر به نام پراپرتی‌ها این نکته روشن می‌شود که ما یک گپ Gap (فضای بیهوده) داریم چون زمان تولد، استخدام و ترک سازمان اصلا نیازی به ساعت ندارند و همان تاریخ کافی است. یعنی نوع Date با حجم کلی 9 بایت؛ که در نتیجه 15 بایت صرفه جویی در یک رکورد صورت خواهد گرفت.

پس کد بالا را به شکل زیر تغییر می‌دهم:

          [Column(TypeName = "date")]
        public DateTime BirthDate { get; set; }

        [Column(TypeName = "date")]
        public DateTime HireDate { get; set; }

        [Column(TypeName = "date")]
        public DateTime? LeaveDate { get; set; }
خصوصیت Column از نسخه 4.5دات نت فریم ورک اضافه شده و در فضای نام System.ComponentModel.DataAnnotations.Schema قرار گرفته است.
نوع‌هایی که در بالا با سایز متغیر هستند، به نسبت دقتی که برای آن تعیین می‌کنید، سایز می‌گیرند. مثل (time(0 که 3 بایت از حافظه را می‌گیرد. در صورتی که time معرفی کنید، به جای اینکه از شیء DateTime استفاده کنید، از شی Timespan استفاده کنید، تا در پشت صحنه از نوع داده time استفاده کند. در این حالت حداکثر حافظه یعنی 5 بایت را برخواهد داشت و بهترین حالت ممکن این هست که نیاز خود را بسنجید و خودتان دقت آن را مشخص کنید. دو شکل زیر نحوه‌ی تعریف نوع زمان را مشخص می‌کنند. یکی حالت پیش فرض و دیگری انتخاب دقت:
     public class Testtypes
    {
           public TimeSpan CloseTime { get; set; }
            public TimeSpan CloseTime2 { get; set; }
    }
   public class TestConfig : EntityTypeConfiguration<Testtypes>
    {
        public TestConfig()
        {
            this.Property(x => x.CloseTime2).HasPrecision(3);
        }
    }
در تکه کد بالا همه از نوع time تعریف می‌شوند ولی در خصوصیت شماره یک نهایت استفاده از نوع تایم یعنی (time(7 مشخص می‌شود. ولی در خصوصیت بعدی چون در کانفیگ دقت آن را مشخص کرده‌ایم از نوع (time(3 تعریف می‌شود.

مورد دوم در مورد داده‌های اعشاری است:
بسیاری از برنامه نویسان تا آنجا که دیده‌ام از نوع float و single و double برای اعداد اعشاری استفاده می‌کنند ولی باید دید که در آن سمت دیتابیس، برای این نوع داده‌ها چه اتفاقی می‌افتد. نوع float در دات نت، با نوع single برابری میکند؛ هر دو یک نام جدا دارند، ولی در واقع یکی هستند. عموما برنامه نویسان به طور کلی بیشتر از همان single استفاده می‌کنند و برای انتساب برای این دو نوع هم حتما باید حرف f را بعد از عدد نوشت:
float flExample=23.2f;
باید توجه کنید که اگر مثلا float انتخاب کردید، تصور نکنید که همان float در دیتابیس خواهد بود. این دو متفاوت هستند تبدیلات به شکل زیر رخ می‌دهد:
//real
public float FloatData { get; set; }
//real
public Single SingleData { get; set; }
//float
public double DoubleData { get; set; }
  همه نوع‌های بالا اعداد اعشاری هستند که به صورت تقریبی و به صورت نماد اعشاری ذخیره می‌گردند و برای به دست آوردن مقدار ذخیره شده، هیچ تضمینی نیست همان عددی که وارد شده است بازگردانده شود. اگر تا به حال در برنامه هایتان به چنین مشکلی برنخوردید دلیلش اعداد اعشاری کوچک بوده است. ولی با بزرگتر شدن عدد، این تفاوت به خوبی دیده می‌شود. 
حالا اگر بخواهیم اعداد اعشاری را به طور دقیق ذخیره کنیم، مجبور به استفاده از نوع decimal هستیم. در دات نت آنچنان محدودیتی بر سر استفاده‌ی از آن نداریم. ولی در سمت سرور داده‌ها بهتر هست برای آن تدابیری اندیشیده شود. هر عدد دسیمال از دقت و مقیاس تشکیل می‌شود. دقت آن تعداد ارقامی است که در عدد وجود دارد و مقیاس آن تعداد ارقام اعشاری است. به عنوان مثال عدد زیر دقتش 7 و مقیاسش 3 است:
4235.254
در صورتی که عدد اعشاری را به دسیمال نسبت دهیم باید حرف m را بعد از عدد وارد کنیم:
decimal d1=4545.112m;
برای اعداد صحیح نیازی نیست.
برای تعیین نوع دسیمال از  fluent api استفاده می‌کنیم:
modelBuilder.Entity<Class>().Property(object => object.property).HasPrecision(7, 3);
کد زیر برای خصوصیت شماره یک، دقت 18 و مقیاس 2 را در نظر می‌گیرد و دومین خصوصیت طبق آنچه که برایش تعریف کرده ایم دقت 7 و مقیاس 3 است:
     public class Testtypes
    {
        public Decimal Decimal1 { get; set; }
        public Decimal Decimal2 { get; set; }

     }

 public class TestConfig : EntityTypeConfiguration<Testtypes>
    {
        public TestConfig()
        {
            this.Property(x => x.Decimal2).HasPrecision(7, 3);
        }
    }

مورد سوم مبحث رشته هاست
:
کدهای زیر را مطالعه فرمایید:
[StringLength(25)]
public string FirstName { get; set; }

[StringLength(30)]
[Column(TypeName = "varchar")]
public string EnProductTitle { get; set; }


public string ArticleContent { get; set; }
[Column(TypeName = "varchar(max)")]
public string ArticleContentEn { get; set; }
اولین رشته بالا (نام) را به محدوده‌ای از کاراکترها محدود کرده‌ایم. به طور پیش فرض تمامی رشته‌ها به صورت nvarchar در نظر گرفته می‌شوند. بدین ترتیب در رشته نام کوچک (nvarchar(25 در نظر گرفته خواهد شد. حال اگر بخواهیم فقط حروف انگلیسی پشتیبانی شوند، مثلا نام فنی کالا را بخواهید وارد کنید، بهتر هست که نوع آن به طرز صحیحی تعریف شود که در کد بالا با استفاده از خصوصیت Column نوع varchar را معرفی می‌کنم. بدین ترتیب تعریف نهایی نوع به شکل (varchar(30 خواهد بود. استفاده از fluentApi‌ها هم در این رابطه به شکل زیر است:
this.Property(e => e.EnProductTitle).HasColumnType("VARCHAR").HasMaxLength(30);
برای مواردی که محدوده‌ای تعریف نشود (nvarchar(MAX در نظر گرفته میشود مانند پراپرتی ArticleContent بالا. ولی اگر قصد دارید فقط حروف اسکی پشتیبانی گردند، مثلا متن انگلیسی مقاله را نیز نگه می‌دارید بهتر هست که نوع آن بهیته‌ترین حالت در نظر گرفته شود که برای پراپرتی ArticleContentEn نوع (varchar(MAX تعریف کرده‌ایم. 
همانطور که گفتیم پیش فرض رشته‌ها nvarchar است، در صورتی که دوست دارید این پیش فرض را تغییر دهید روش زیر را دنبال کنید:
modelBuilder.Properties<string>().Configure(c => c.HasColumnType("varchar"));


//=========== یا

modelBuilder.Properties<string>().Configure(c => c.IsUnicode(false));


جهت تکمیل بحث نیز هر کدام از متغیرهای عددی در سی شارپ معادل نوع‌های زیر در Sql Server هستند:
//tinyInt
public byte Age { get; set; }

//smallInt
public Int16 OldInt { get; set; }

//int
public int Int32 { get; set; }

//Bigint
public Int64 HighNumbers { get; set; }

مطالب
#Defensive Code in C - قسمت چهارم
 Automated Code Test

با توجه به فاکتور‌های موجود در Defensive Coding، یکی از مواردی که کیفیت کد شما را تضمین خواهد کرد، استفاده کردن از (ACT) Automated Code Test می‌باشد. در این قسمت قصد داریم مزایای تست اتوماتیک و Unit Test را به عنوان یکی دیگر از ابعاد Defensive Coding ذکر کنیم.


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

ACT به معنی نوشتن کد، جهت تست قابلیت‌های نرم افزار می‌باشد. به این معنی که شما جهت تست کد خود، یکسری کد می‌نویسید که این کدها وظیفه دارند کد‌های جدیدی را که به نرم افزار خود اضافه کرده اید، تست کنند و اجرای آنها توسط زیرساخت‌های موجود (Test Frameworks) به صورت اتوماتیک انجام می‌شود.

حال قصد داریم اجزای ACT را که در شکل ذیل نمایش داده شده‌اند، تشریح کنیم.


· structured: برای بیان این مسئله، از مفهوم AAA استفاده می‌شود. A اول به معنی Arrange اطلاعاتی است که برای تست مورد نیاز است. A دوم به معنی Act یا اجرای متد در حالت تست است و A سوم بمعنی Assert یا بررسی نتایج تست می‌باشد. این ساختار، ساختاری است که در ادامه برای ایجاد تست‌ها از آن استفاده می‌کنیم.

· Self-documented: ساختار تست به گونه‌ای است که خود مستند می‌باشد و با بررسی کلی ساختار آن می‌توان به هدف تست پی برد.

· Automatic: با استفاده از Test Framework ها، فرآیند تست اتوماتیک می‌شود.

· Repeatable: یکی از مزیت‌های ACT این است که می‌توان آن را برای دفعات مکرر تکرار کرد.

· TARDIS: مخفف  Time And Relative Dimension In Space می‌باشد؛ با توجه به این مسئله ACT از کد شما در میان زمان و فضا محافظت می‌کند. ACT  عملکرد اصلی کد شما را در حال حاضر و در زمانی در آینده  تایید می‌کند؛ زمانیکه کد شما در حال توسعه می‌باشید و هر لحظه قابلیت‌های جدیدی به آن اضافه می‌شود، ACT تضمین می‌کند که این تغییرات، قابلیت‌های موجود در سیستم را تحت تاثیر قرار نمی‌دهند. بنابراین ACT از کد شما در مقابل زمان و فضا محافظت می‌کند.

روشهای مختلفی برای انجام دادن ACT وجود دارند که در این مقاله بر روی Unit Test تمرکز خواهیم کرد. Unit Test یکسری تست‌ها هستند که توسط برنامه نویس نوشته و اجرا می‌شود. هدف این روش این است که کد به قسمت‌های کوچکی تقسیم شود و بررسی شود که این قسمت‌ها آن گونه که انتظار می‌رود، عمل می‌کنند.

برای رسیدن به این هدف باید کد را به صورت متدهای Clean و Testable نوشت. این متد‌ها قسمت‌های مستقلی از کد هستند که می‌توانند تست شوند. همان طور که در شکل زیر مشاهده می‌کنید، برای هر متد می‌توان تست‌های مختلفی نوشت و حالت‌های مختلف مربوط به ورودی‌های معتبر، ورودی‌های نامعتبر و بروز Exception را تست کرد.


بسیاری از برنامه نویسان و مدیران  پروژه درمقابل مسئله استفاده از Unit Test در توسعه نرم افزار حساسیت‌های خاصی نشان می‌دهند. بسیاری از آنها اظهار می‌کنند که برای این کار زمان کافی نداریم و استفاده کردن از این روش برای ما هزینه بر می‌باشد. اما ما در جواب این دسته از افراد باید موارد زیر را که بیشتر هم بر جنبه زمانی تاکید دارند، بیان کنیم.


· Save time:
  استفاده کردن از Unit Test از هدر رفتن زمان شما جلوگیری می‌کند. هر برنامه نویسی می‌داند که حتی چند خط کد ساده هم نیاز به تست و باز بینی دارد. بنابراین برنامه نویس مجبور است آن ماژول اصلی از نرم افزار را که چند خط کد در آن نوشته است، به گونه ای اجرا کرده و فرآیند بیزینسی این ماژول را برای سناریو‌های مختلف، بصورت دستی تست کند. حال فرض کنید در ادامه‌ی این کار، شخص برنامه نویس مجبور شود کد را بطور مرتب تغییر دهد. بنابراین در این حالت مجبور است این فرآیند را چندین و چند بار تکرار کند (نرم افزار را اجرا کند، به منوی X برود، فرم Y را باز کند، حالت‌های مختلف را در فرم بررسی کند).
راه حلی که Unit Test برای حل این مشکل ارئه می‌دهد این است که برای انجام این فرآیند می‌توان کد نوشت و آن را بارها و بارها اجرا کرد. وظیفه‌ی Unit Test ها این است که اطلاعات مورد نیاز متد یا واحدی که می‌خواهند آن را تست کنند، فرآهم می‌آورند، متد را با اطلاعات فرآهم شده زیر تست می‌برند و سپس نتایج بدست آمده را بررسی خواهند کرد. شما می‌توانید در صورت تغییراتی در متد‌ها یا واحد‌ها، Unit  Test را بارها و بار‌ها برای تست عملکرد صحیح آن متد، بعد از تغییرات اجرا کنید. همان طور که می‌بینید تبدیل کردن این فرآیند دستی به یک فرآیند سیستمی و اتوماتیک می‌تواند در جلوگیری از هدر رفت زمان بسیار تاثیر گذار باشد.

· Find Bugs Faster:
با استفاده از Unit Test شما می‌توانید فرآیند پیدا کردن خطاها را بسیار سریعتر انجام دهید. برای مثال فرض کنید که شما گزارش یکسری خطا‌ها را در نرم افزار، دریافت کرده‌اید. به جای اینکه سعی کنید بصورت دستی، فرآیند‌ها را در نرم افزار مرور کنید تا دوباره شرایط بروز خطا یا شرایطی را که خطا در آن رخ داده است، جهت درک دلیل خطا یا خطا‌ها ایجاد کنید،  با استفاده از Unit Test می‌توانید به راحتی و در سریع‌ترین زمان ممکن و بصورت اتوماتیک خطا‌ها را پیدا کنید.

· Refactor Safely:
Unit Test به شما اجازه می‌دهد که به راحتی کد خود را Refactor کنید. فرض کنید که می‌خواهید کدی را که دارای یکسری پیچیدگی‌ها می‌باشد و نگهداری و توسعه آن سخت است، Refactor کنید. بدون استفاده از Unit Test، این Refactor کردن دارای ریسک بسیار زیادی است و ممکن است منجر به بروز خطاهای زیادی شود؛ در حالیکه با استفاده از Unit Test، بعد از Refactor کردن کد، می‌توان Test ها را اجرا کرده و از عدم وجود خطا در کدها به راحتی مطمئن شد.

· Enhance Your Value:
با نوشتن Unit Test برای کد‌های خود می‌توانید یک ارزش افزوده را به کد‌های خود اضافه کنید. به دلیل اینکه نوشتن Unit Test ویژگی Self-documented کد شما را افزایش می‌دهد و به افرادی که در تیم هستند کمک می‌کنند Business نرم افزار را بهتر درک کنند.

· Minimize Interruptions:
داشتن مجموعه‌ای مناسب از Unit Test ها باعث می‌شود تا Interrupt های ناخواسته در Code شما بوجود نیاید. برای مثال حالتی را در نظر بگیرید که بدلیل ورود داده‌های ناخواسته، نرم افزار دچار خطا می‌شود. دراین وضعیت در صورتیکه از Unit Test استفاده شود، هندل کردن این شرایط ناخواسته و Interrupt، بسیار راحت‌تر خواهد بود.