مطالب
مدیریت پیشرفته‌ی حالت در 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
نظرات مطالب
نمایش فرم‌های مودال Ajax ایی در ASP.NET MVC به کمک Twitter Bootstrap
بعد از اینکه صفحه‌ی مودال بسته شد، چطور می‌شود بر اساس نتیجه، موفق یا ناموفق بودن، اطلاعات صفحه‌ی اصلی را به‌روز کرد؟ من از روش زیر استفاده کردم. اما PartialView به‌روز نمی‌شود.
    function LoadCt() {
        $('#myModal').modal('hide');
        $.ajax({
            type: "POST",
            url: "/CONTROLLER/ACTIONMETHOD",
            data: JSON.stringify(),
            contentType: "application/json; charset=utf-8",
            dataType: "html",
            complete: function (xhr, status) {
                var data = xhr.responseText;
                $('cats').html(data);
            }
        });
    }
و در CONTROLLER:
        public ActionResult ACTIONMETHOD()
        {
            var model = stuff from _db;
            return PartialView("_Get", model);
        }

نظرات مطالب
نحوه اضافه کردن Auto-Complete به جستجوی لوسین در ASP.NET MVC و Web forms
افزونه‌ی مورد استفاده در آن به نام jquery.autocomplete.js سورسش پیوست هست. ajax را در آن جستجو کنید به مورد ذیل خواهید رسید:
$.ajax({
    //...
    data: $.extend({
             q: lastWord(term),
             limit: options.max
             }, extraParams),
       //...
});
در اینجا q مشخص است و extraParams آن متغیر. بنابراین برای ارسال اطلاعات اضافی باید extraParams را مانند کدهای ذیل مقدار دهی کرد. نحوه‌ی دریافت عناصر آن در سمت سرور، مانند نحوه‌ی دریافت q است.
    $(document).ready(function () {
        $("#term").autocomplete(url, {
            // ..... 
            extraParams: { id: 12, xyz: "test" }
        }).result(function (evt, row, formatted) {
            // .....
        });
    });
مطالب
MongoDb در سی شارپ (بخش نهم)
سال‌های مدیدی است که به طراحی پایگاه‌های sql پرداخته و تجاربی آموخته‌ایم. کتاب‌ها و مقالات زیادی در اینباره منتشر شده‌اند. از این‌رو در نحوه طراحی دیتابیس‌های رابطه‌ای اطلاعات زیادی کسب و مسائل زیادی را از این راه حل نموده‌ایم؛ ولی با ورود دیتابیس‌های NoSql و تنوع زیاد آن‌ها و روش‌های متنوعی که هر کدام از آن‌ها به طور جداگانه دارند باعث شد تجربه سال‌ها فعالیت و مدل ذهنی که داشتیم به یکباره تغییر کند و گاها بیشتر باعث گیج شدن می‌گردد. از این‌رو در این مقاله سعی داریم تکنیک‌ها مدل سازی اسناد را در دیتابیس مونگو، بررسی کنیم و مزایا و معایب هر یک را برشماریم.
در دیتابیس‌های قدیم، تمرکز بر روی نوشتن بود تا با کمترین افزونگی و تکرار و رعایت اصول ACID، اطلاعات را ذخیره نماییم. ولی در حال حاضر به دلیل دسترسی به فضاهای ذخیره سازی بزرگتر و همچنین افزایش ترافیک شبکه در واکشی دیتاها، قضیه عکس شده و تمرکز دیتابیس‌های NoSql بر روی خواندن میباشد. پس باید فاکتورهای مدل سازی طوری باشد تا خواندن در سریعترین حد امکان قرار بگیرد. البته مواردی چون حذف و به روزرسانی هم باید در این مورد بررسی شوند.
ارتباط اسناد با یکدیگر:
ارتباط اسناد از دو طریق امکان پذیر است:
  • حالت ارجاع  : شماره سند یا Object Id را شامل شده و در صورتیکه به اطلاعاتی نیاز داشتید، باید اطلاعات آن را در یک درخواست جداگانه واکشی نمایید. چون مونگو شامل جوین نبوده و جوین‌ها باید در سطح اپلیکیشن مدیریت شوند.
{
fname:'ali',
lname:'yeganeh',
accounts:[454354353,3455435]
}

  • حالت جاسازی سند (یا اسناد تو در تو) Embed :  در این حالت سند مورد نظر اطلاعات سند دیگری را در درون خود نگه میدارد. در این حالت به هیچ جوینی نیازی نیست و اطلاعات وابسته، به همراه خود سند اصلی واکشی می‌شوند. این نکته باید مورد توجه قرار بگیرد که مونگو یک دیتابیس غیر اتمیک هست و در صورتیکه اصل دیتا تغییر کند، تغییر یا به روزرسانی در سندهای Embed انجام نخواهد شد و در صورت نیاز باید خودتان به طور دستی آن را کنترل نمایید.
{
fname:'ali',
lname:'yeganeh',
accounts:[
{
  username:"ali",
  password:"123"
},
{
  username:"reza",
  password:"456"
}
]
}

مدل هایی با ارتباط یک به یک : 
در این نوع مدل سازی، دو سند داریم که یکی از آن‌ها Principle و دیگری Dependent محسوب می‌شود. برای ذخیره سازی آن‌ها عموما از حالت Embed استفاده میشود. در این حالت چون ارتباط بین دو سند به صورت یک به یک میباشد، در واقع این امکان وجود دارد تا سند مادری به طور جداگانه وجود نداشته باشد و همان سند به صورت Embed ذخیره میشود. در این حالت مشکلی از لحاظ اتمیک نبودن مونگو پیش نمیاید و  ویرایش راحت‌تری خواهد داشت.
مدل‌هایی با ارتباط یک به چند:
این اسناد را می‌توان به دو حالت بالا بر حسب نیازمندی سیستم ذخیره کرد. فرض کنید مثال زیر را که در سایت مونگو هم عنوان شده‌‌است، داریم:
book
{
     name:'Scarlet Letter",
     Language:"English",
     Pages:124,
...
}

publisher
{
   name : "Orielly",
   ...

}
در این حالت هر کتاب باید ارتباطی با ناشر خود داشته باشد. در صورتیکه به صورت Embed داخل سند قرار بگیرد و هر کتابی شامل اطلاعات ناشر خود باشد، نکات زیر مورد بررسی قرار میگیرند:
book
{
     name:'Scarlet Letter",
     Language:"English",
     Pages:124,
...,
publisher:
{
   name : "Orielly",
   ...

}
}

نکات مثبت:
  1. در این حالت در صورتیکه واکشی هر کتاب به همراه اطلاعات ناشر را نیاز داشته باشیم و یا پرس وجوهای ترکیبی نیاز باشد، در سریعترین زمان ممکن واکشی انجام خواهد شد.
  2. درج و مدیریت آن راحت‌تر خواهد بود.
نکات منفی:
  1. در صورتیکه اطلاعات ناشر نیاز به تغییرات اساسی داشته باشد و باید در تمامی سندها اصلاح گردد، باید تمامی اسناد مربوط به اطلاعات کتاب به روزرسانی شوند که هزینه سنگین‌تری را خواهد داشت.
  2. دیتای تکراری زیادی ذخیره خواهد شد و در نتیجه حافظه بیشتری را میطلبد.
  3. در صورتیکه تنها به اطلاعات ناشر نیاز باشد و اطلاعات ناشر در سند دیگری وجود نداشته باشد و فقط در سند کتاب وجود داشته باشد، واکشی آن هزینه سنگین‌تری را خواهد طلبید. به همین جهت توصیه میشود در صورتیکه دیتای شما می‌تواند به صورت یک موجودیت مستقل هم عمل کند، اطلاعات آن در سند دیگری که من به آن سند اصلی میگویم ذخیره شوند تا نمونه‌ها از روی آخرین ویرایش آن ساخته شوند و موقعی‌که تنها به واکشی آن اطلاعات نیاز است، همان‌ها بیرون کشیده شوند.
در روشی دیگری میتوان ارجاعی از ناشر را به شکل زیر در کتاب نگهداری کرد:
book
{
     name:'Scarlet Letter",
     Language:"English",
     Pages:124,
...,
publisher:1212121
}
نکات مثبت:
  1. عدم وجود تکرار اطلاعات
  2. چون تنها یک سند برای ویرایش وجود دارد، نیازی به اصلاح اسناد توکار نیست و ویرایش، هزینه کمتری خواهد داشت.
نکات منفی:
  1. عدم وجود جوین: در صورتیکه نیاز به جوین بزرگی باشد، این نوع جوین باید در سطح برنامه شما انجام شود و هزینه بر خواهد بود.

نگهداری نام کتاب‌ها در ناشر
انعطاف مونگو برای ایجاد مدل، گزینه‌های زیادی را پیش رو میگذارد و واقعا مدلسازی را بیشتر از قبل، چالش برانگیز میکند. در حالت دیگر میتوان اطلاعات کتاب را به صورت ارجاع، در سند ناشر نگهداری کرد. به عنوان مثال زمانیکه نیاز داریم کتب منتشرشده یک ناشر را ببینیم، شاید این گزینه بهتر باشد. البته در این حالت باید بتوان ارجاعات به کتاب را در تعداد محدودی نگهداری کرد؛ در غیر این صورت با تعداد زیادی ارجاع که شاید هیچگاه نیازی هم به آن‌ها نیست، خواهیم رسید و در این حالت شاید ارجاع به ناشر در سند کتاب بسیار بهتر به نظر برسد. البته میتوان در این حالت ناشر تنها به تعداد معدودی از آخرین کتابهایش دسترسی داشته باشد تا کاربر بتواند آخرین کتاب‌های منتشر شده‌ی ناشر را ببیند. 
حال با اطلاعات بالا چگونه مدلسازی کنیم؟
همانطور که گفتیم ابتدا تمرکز شما باید برای خواندن اطلاعات باشد و سپس معیارهایی چون به روزرسانی نیز بررسی گردند. به عنوان نمونه اطلاعات یک پست در وبلاگ را در نظر بگیرید. این سند شامل سندهای توکاری چون دسته بندی، اطلاعات نویسنده، معیارهایی چون امتیازدهی و بخش نظرات میباشد. در این حالت چون همه عناصر قرار است با یکدیگر بیرون کشیده شوند و در واقع تنها با یک سند سروکار داریم، کار بسیار سریعتر و راحت‌تر است. پس این ساختار گزینه مناسبی برای نمایش است:
Post
{
title:"C#",
body:"About C#",
tags:['C#','.Net','microsoft'],
Categories:[{name:'Programming'}],
votes:[{rate:3,user:42342},{rate:5,user:423445},...],
comments:[
{
text:"my comment1",
time:"10/2/1396",...},
...

]
}

حال این تصور را داشته باشید که ما تنها یک پست را نشان نمیدهیم و بلکه پست‌ها به صورت یک لیست قرار است نمایش داده شوند و با گزینه‌ی مشاهده‌ی مطلب می‌توانیم یک پست را به صورت کامل ببینیم. در این صورت همه اطلاعات همانند قبل هستند، بجز بخش نظرات که دیگر در این حالت کاربردی ندارد و دیتای اضافی است که به ناچار باید خوانده شود. پس در این حالت میگوییم این مدل برای خواندن مناسب نیست، چون باید تمام نظرات اسنادی که در لیست قرار دارند هم خوانده شوند. پس باید بخش نظرات را از سند پست وبلاگ جدا کنیم.
{
POST:45453,
count:35,
comments:[...]
}
سپس میگوییم هر سند نهایتا 16 مگابایت اطلاعات را نگهداری میکند و هم اینکه تعداد نظرات ممکن است بسیار زیاد باشند. پس هر سند را به تعدادی نظر محدود میکنیم به این حالت میگویند داریم یک Bucket میسازیم و مثلا هر باکت را به 100 کامنت محدود میکنیم. تا به الان وضعیت طراحی بهتری نسبت به قبل پیدا کردیم:
{
post:345345,
capacity:100,
count:35,
bucket:2,
comments:[...]
}
در این حالت حتی میتوانیم کامنتها را صفحه بندی کرده و در هر صفحه یک باکت را بخوانیم. برای نمایش این دو مورد آخری برای جداسازی دیتا بسیار خوب است. حتی میتوان یک کامنت را به همراه پاسخ‌های آن که به صورت درخت واره قرار گرفته اند نیز در یک سند جداگانه ذخیره کرد.
نکاتی که باید در حین طراحی در نظر بگیرید:
  1. همیشه به این نکته توجه داشته باشید که نباید بگذارید تعداد آرایه‌های یک سند خیلی بزرگ شوند. در غیر اینصورت کارآیی مونگو به خصوص در حین ویرایش سند پایین خواهد آمد. در حین ویرایش، اگر سندی از اندازه‌ی خود بزرگتر نشود، مشکلی پیش نمیاید ولی اگر فضایی بیش از آنچه که  قبلا داشته به آن اضافه شود، سند نیاز به جابجایی و گسترش فضا خواهد داشت. در این حالت باید مونگو سند را به جای دیگری که فضای کافی برای آن وجود دارد، انتقال بدهد و میزان Disk Fragment به طبع بالا خواهد رفت. همچنین اندیس‌های آرایه‌ای هم با جابجا شدن دیتا نیاز به، به روزرسانی خواهند داشت و زمانی هم صرف به روزرسانی اندیس‌ها خواهد شد.
  2. مدیر محصول مونگو اظهار نظر صریحی در این مورد نکرد‌ه‌است، ولی به نظر می‌رسد نوع فرمت BSON از یک اسکن خطی در حافظه استفاده میکند و زمان بیشتری صرف پیدا کردن المان‌های انتهایی در آرایه خواهد شد؛ پس بیشتر عملیات در این نوع سند، با کندی مواجه خواهند شد. با توجه به کامنت‌هایی که در سایت‌ها و شبکه‌های اجتماعی یافت شده‌است، آرایه ای با بیش از صدهزار آیتم ساده میتواند آسیب زا باشد؛ به همین دلیل توصیه میشود که اگر بیش از صدهزار آیتم نیاز است، از همان حالت Bucket استفاده شود.
  3. استفاده از اندیس‌ها هم سابقه‌ی دیرینه‌ای داشته و سعی کنید کوئری هایی بزنید که بر اساس اندیس‌های تعریف شده باشند تا واکشی دیتا سریعتر شود. پس نحوه کوئری نویسی و انتخاب فیلدی که اندیس میشود بسیار مهم است.
  4. استفاده از Projection تاثیری بر خواندن اسناد ندارد و هر سند به طور کامل واکشی می‌شود. projection تنها در بار‌ه‌ی ترافیک یا انتقال حجم کمتری از اطلاعات به سمت کلاینت تاثیرگذار میباشد. پس استفاده از projection بجای جدا سازی اسناد را دنبال نکنید.
مطالب
کامپوننت‌ها در AngularJS 1.5
در نسخه‌های  AngularJS 1.x عموماً با کمک کنترلرها و دایرکتیوها، می‌توانیم ویژگی‌های جدیدی را به اپلیکیشن‌هایمان اضافه کنیم؛ از دایرکتیوها برای ایجاد عناصر سفارشی HTML می‌توانستیم (می‌توانیم) استفاده کنیم. مشکل دایرکتیوها این است که برای ایجاد یک عنصر سفارشی ساده باید تنظیمات زیادی را انجام دهیم. در نسخه‌ی AngularJS 1.5 یک API جدید با نام کامپوننت معرفی شده است و این قابلیت، مدل ساده‌ی برنامه‌نویسی در کنترلرها و همچنین قدرت دایرکتیوها را در اختیارمان قرار خواهد داد. سینتکس این API خیلی شبیه به استفاده از کامپوننت‌ها در Angular 2.0 است. این یک مزیت مهم محسوب می‌شود؛ زیرا امکان مهاجرت از نسخه‌ی 1.5 به نسخه‌ی 2 را خیلی ساده خواهد کرد.

نحوه‌ی تعریف یک کامپوننت در AngularJS 1.5
همانند کنترلر و دایرکتیو، برای تعریف یک کامپوننت نیز باید از module API استفاده کنیم:

بنابراین برای ایجاد یک کامپوننت می‌توانیم به اینصورت عمل کنیم:

var app = angular.module("dntModule", []);
app.component("pmApp", {
  template: `Hello this is a simple component`
});

همانطور که مشاهده می‌کنید تابع component دو پارامتر را از ورودی دریافت خواهد کرد؛ نام کامپوننت و یک شیء برای تعیین تنظیمات کامپوننت. نام کامپوننت در اینجا به صورت camel case تعریف شده است؛ که در واقع یک convention برای Angular است. در این‌حالت برای استفاده‌ی از کامپوننت باید به اینصورت عمل کنیم:

<pm-app></pm-app>

در قسمت تنظیمات کامپوننت، در ساده‌ترین حالت یک template تعیین شده‌است که بیانگر نحوه‌ی رندر شدن یک کامپوننت می‌باشد. در اینحالت وقتی انگیولار به تگ فوق برسد، یک کامپوننت با نام pmApp را بارگذاری خواهد کرد.


ایجاد یک کامپوننت ساده

در ادامه می‌خواهیم یک کامپوننت ساده را جهت نمایش یکسری URL درون صفحه طراحی کنیم. ساختار صفحه index.html به صورت زیر خواهد بود:

<html ng-app="DNT">
<head>
    <meta charset="UTF-8">
    <title>Using Angular Component</title>
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
    <link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css">
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-md-3">
                <dnt-widget></dnt-widget>
            </div>
        </div>
    </div>
    <script src="bower_components/angular/angular.js"></script>
    <script src="scripts/app.js"></script>
    <script src="scripts/components/dnt-widget.component.js"></script>
</body>
</html>

در اینجا ابتدا توسط دایرکتیو ng-app، به Angular، ماژول‌مان را معرفی کرده‌ایم. سپس مداخل بوت‌استرپ و کتابخانه‌ی font-awesome را مشاهده می‌کنید. در ادامه، کتابخانه‌ی Angular و همچنین فایل app.js جهت معرفی ماژول برنامه معرفی شده‌است. در نهایت نیز یک فایل در مسیر ذکر شده برای قرار دادن کدهای کامپوننت در مسیر scripts/components اضافه شده‌است.

همانطور که ملاحظه می‌کنید، کامپوننت‌مان به صورت یک تگ سفارشی، درون صفحه قرار گرفته است:

<dnt-archive></dnt-archive>

در ادامه باید به Angular، نحوه‌ی تعریف این کامپوننت را اعلام کنیم. بنابراین یک فایل جاوا اسکریپتی را با نام dnt-widget.component، با محتویات زیر ایجاد کنید:

(function () {    
    "use strict";    
    var app = angular.module("DNT");    
    function DntArchiveController() {
      var model = this;      
      model.panel = {
          title: "Panel Title",
          items: [
              {
                  title: "Dotnettips", url: "https://www.dntips.ir"
              },
              {
                  title: "Google", url: "http://www.google.con"
              },
              {
                  title: "Yahoo", url: "http://www.yahoo.con"
              }
          ]
      };  
    };
    
    app.component("dntWidget", {
        templateUrl: '/scripts/components/dnt-widget.component.html',
        controllerAs: "model",
        controller: DntArchiveController
    });
} ());

توضیح کدهای فوق:

همانطور که مشاهده می‌کنید، برای پارمتر دوم کامپوننت، سه پراپرتی را تعیین کرده‌ایم:

templateUrl: به کمک این پراپرتی به Angular گفته‌ایم که محتوای قالب این کامپوننت، درون یک فایل HTML مجزا قرار دارد و به صورت linked template می‌باشد.

controllerAs: یکی از مزایای استفاده از کامپوننت‌ها، استفاده از controller as syntax می‌باشد. لازم به ذکر است اگر این پراپرتی را مقداردهی نکنیم، به صورت پیش‌فرض مقدار ctrl$ در نظر گرفته خواهد شد.

controller: مزیت دیگر کامپوننت‌ها، استفاده از کنترلرها است. با استفاده از این پراپرتی، یک کنترلر را برای کامپوننت‌مان رجیستر کرده‌ایم. در نتیجه زمانیکه‌ی Angular می‌خواهد کامپوننت‌مان را نمایش دهد، تابع تعریف شده برای این پراپرتی، جهت ایجاد یک controller instance فراخوانی خواهد شد. بنابراین هر پراپرتی یا تابعی که برای این controller instance تعریف کنیم، به راحتی درون ویوی آن جهت اعمال بایندینگ در دسترس خواهد بود (در نتیجه نیازی به scope$ نخواهد بود).

درون کنترلر نیز برای راحتی کار و همچنین به عنوان یک best practice، مقدار this را توسط یک متغیر با نام model، کپچر کرده‌ایم. در اینجا یک شیء را با نام panel نیز به مدل اضافه کرده‌ایم.


محتویات تمپلیت:

<div class="panel panel-default">
    <div class="panel-heading">
        <h3 class="panel-title">
            <span class="fa fa-archive"></span>
            {{ model.panel.title}}
        </h3>
    </div>
    <ul class="list-group">
        <li class="list-group-item" ng-repeat="item in model.panel.items">
            <span class="fa fa-industry"></span>
            <a href="{{ item.url }}">{{ item.title }}</a>
        </li>
    </ul>
</div>

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



همانطور که مشاهده می‌کنید استفاده از کامپوننت‌ها در Angular 1.5 در مقایسه با ایجاد دایرکتیوها و کنترلر‌ها خیلی ساده‌تر است. در واقع امکانات این API جدید تنها به مثال فوق ختم نمی‌شود؛ بلکه این API یک سیستم مسیریابی جدید را نیز معرفی کرده است که در قسمت‌های بعدی به آن نیز خواهیم پرداخت.


جهت تکمیل بحث نیز یک تقویم شمسی ساده را در اینجا قرار داده‌ام. می‌توانید جهت مرور بحث جاری به کدهای آن مراجعه کنید. البته هدف از تعریف این پروژه تنها یک مثال ساده برای معرفی کامپوننت‌ها بود و طبیعتاً باگ‌های زیادی دارد. اگر مایل بودید می‌توانید در توسعه‌ی آن مشارکت نمائید.


کدهای این قسمت را نیز از اینجا می‌توانید دریافت کنید.

مطالب
دانلود مجوز SSL یک سایت HTTPS
اگر به مرورگرها دقت کرده باشید، امکان نمایش SSL Server Certificate یک سایت استفاده کننده از پروتکل HTTPS را دارند. برای مثال در فایرفاکس اگر به خواص یک صفحه مراجعه کنیم، در برگه امنیت آن، امکان مشاهده جزئیات مجوز SSL سایت جاری فراهم است:



سؤال: چگونه می‌توان این مجوزها را با کدنویسی دریافت یا تعیین اعتبار کرد؟

قطعه کد زیر، نحوه دریافت مجوز SSL یک سایت را نمایش می‌دهد:
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;

namespace DownloadCerts
{
    class Program
    {
        static void Main(string[] args)
        {
            // صرفنظر از خطاهای احتمالی مجوز
            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

            var url = "https://pdfreport.codeplex.com";
            var request = WebRequest.Create(url) as HttpWebRequest;
            request.Method = WebRequestMethods.Http.Head;
            using (var response = request.GetResponse())
            { /* در اینجا مجوز، در صورت وجود دریافت شده */  }

            if (request.ServicePoint.Certificate == null)
                return;

            // ذخیره سازی مجوز در فایل
            var cert = new X509Certificate2(request.ServicePoint.Certificate);
            Console.WriteLine("Expiration Date: {0}", cert.GetExpirationDateString());
            var data = cert.Export(X509ContentType.Cert);
            File.WriteAllBytes("site.cer", data);

            Process.Start(Environment.CurrentDirectory);
        }
    }
}
ممکن است مجوز یک سایت معتبر نباشد. کلاس WebRequest در حین مواجه شدن با یک چنین سایت‌هایی، یک WebException را صادر می‌کند. از این جهت که می‌خواهیم حتما این مجوز را دریافت کنیم، بنابراین در ابتدای کار، ServerCertificateValidation را غیرفعال می‌کنیم.
سپس یک درخواست ساده را به آدرس سرور مورد نظر ارسال می‌کنیم. پس از پایان درخواست، خاصیت request.ServicePoint.Certificate با مجوز SSL یک سایت مقدار دهی شده است. در ادامه نحوه ذخیره سازی این مجوز را با فرمت cer مشاهده می‌کنید.


نظرات مطالب
صفحه بندی، مرتب سازی و جستجوی پویای اطلاعات به کمک Kendo UI Grid
برای این حالات آیا میشه فانکشن‌های kendo رو override کرد یا مثلا بهشون اضافه کرد function سفارشی :
$("#report-grid").data("kendoGrid").options.editable.mode = "popup";
$("#report-grid").data("kendoGrid").options.editable.mode = "inline";
$("#report-grid").data("kendoGrid").options.editable.mode = "incell";
در نظر بگیرید که ما در پروژه(دات نت کور) مثلا به جای اینکه حالت ویرایش یا حذف و اضافه کردن(عملیات CRUD بدون خواندن) داخل خود کندو انجام بشه، بره در یک صفحه جدید یا بهتر بگم view متناظر با خود اون عملیات (Update - Delete - Create)؟ یا باید این رو با خود دات نت مدیریتش کرد؟ بخاطر اینکه تعداد پراپرتی‌های Model ما زیاد هستش و بصورت inline یا حتی popup فرم بهم ریخته میشه.
نظرات مطالب
ASP.NET MVC #17
کلا یک آنتی فورجری توکن برای کل صفحه کافی است. روشی که آقای رضایی کمی بالاتر مطرح کردند (قرار دادن آن در فایل layout) هم جالب است. به این صورت در همه‌جا و در تمام صفحات حضور خواهد داشت؛ آن‌هم فقط یکبار و نه بیشتر. نحوه‌ی خواندن و اضافه کردن آن نیز به صورت زیر خواهد بود:
function addToken(data) {
   data.__RequestVerificationToken = $("input[name=__RequestVerificationToken]").val();
   return data;
}
 
$.ajax({
   // .....
   data: addToken({ postId: postId }), // اضافه کردن توکن
   dataType: "html", // نوع داده مهم است
   // .....
});
سه نکته اینجا مهم است:
- data type حتما باید در این حالت html باشد.
- در قسمت data متد addToken کار افزودن خودکار آنتی فورجری‌توکن صفحه را انجام می‌دهد (محل آن مهم نیست که داخل فرم باشد یا خارج از آن).
- سطر contentType نباید ذکر شود.