مطالب
ساخت یک برنامه ساده‌ی جاوااسکریپتی با استفاده از الگوی MVC

در این مقاله قصد داریم با استفاده از جاوااسکریپت خالص، یک برنامه‌ی ساده را با الگوی MVC انجام دهیم. این برنامه، عملیات CRUD را پیاده سازی میکند و تنها به سه فایل index.html , script.js , style.css  نیاز دارد و از هیچ کتابخانه یا فریم ورک دیگری در آن استفاده نمیکنیم.

در الگوی MVC
  • M مخفف  Model می‌باشد و کار مدیریت داده‌ها را بر عهده دارد.
  • V مخفف View  می‌باشد و وظیفه‌ی نمایش داده‌ها به کاربر را بر عهده دارد.
  • C  مخفف  Controller می‌باشد و پل ارتباطی بین Model و  View می‌باشد و مدیریت درخواست‌ها را بر عهده دارد.
در برنامه‌ی جاری همه چیز با جاوا اسکریپت هندل میشود و فایل  index.html  فقط دارای یک المنت با آیدی مشخصی است. کد زیر ساختار فایل  index.html می‌باشد:
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>الگوی MVC در جاوااسکریپت</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>

    <div id="root"></div>

    <script src="script.js"></script>
</body>

</html>
فایل  style.css  آن نیز دارای دستورات ساده‌ای است و یا میتوان از Normalize.css به همراه استایل دلخواه استفاده کرد و یا از فریم ورکهای مطرح دیگر استفاده نمود. کدهای فایل sytle.css آن نیز به شکل زیر خواهد بود:
*,
*::before,
*::after {
  box-sizing: border-box
}

html {
  color: #444;
}

#root {
  max-width: 450px;
  margin: 2rem auto;
  padding: 0 1rem;
}

form {
  display: flex;
  margin-bottom: 2rem;
}

[type="text"],
button {
  display: inline-block;
  -webkit-appearance: none;
  padding: .5rem 1rem;
  border: 2px solid #ccc;
  border-radius: 4px;
}

button {
  cursor: pointer;
  background: #007bff;
  color: white;
  border: 2px solid #007bff;
  margin: 0 .5rem;
}

[type="text"] {
  width: 100%;
}

[type="text"]:active,
[type="text"]:focus {
  outline: 0;
  border: 2px solid #007bff;
}

[type="checkbox"] {
  margin-right: 1rem;
}

h1 {
  color: #222;
}

ul {
  padding: 0;
}

li {
  display: flex;
  align-items: center;
  padding: 1rem;
  margin-bottom: 1rem;
  background: #f4f4f4;
  border-radius: 4px;
}

li span {
  display: inline-block;
  padding: .5rem;
  width: 250px;
  border-radius: 4px;
  border: 2px solid transparent;
}

li span:hover {
  background: rgba(179, 215, 255, 0.52);
}

li span:focus {
  outline: 0;
  border: 2px solid #007bff;
  background: rgba(179, 207, 255, 0.52)
}


فایلهای HTML و CSS را برای شروع کار آماده نمودیم و از این پس با فایل  script.js، ادامه کار را پیش می‌بریم. برای جداسازی هر قسمت از اجزای MVC، کلاسی خاص را تدارک می‌بینیم. پس سه کلاس خواهیم داشت به‌نام‌های  Model , View , Controller و در سازنده کلاس کنترلر، دو شی از View و  Model را بعنوان ورودی دریافت میکنیم. همانطور که پیش‌تر توضیح داده شد، قسمت Controller، پل ارتباطی بین View و Model می‌باشد. کد فایل  script.js را به شکل زیر تغییر میدهیم:
class Model {
    constructor() {}
}

class View {
    constructor() {}
}

class Controller {
    constructor(model, view) {
        this.model = model
        this.view = view
    }
}


const app = new Controller(new Model(), new View())

در ادامه کار در کلاس Model شروع به کدنویسی میکنیم و متدهای مد نظر را برای عملیات CRUD، در آن اضافه میکنیم. چهار تابع را به کلاس Model به‌نامهای addTodo  ، editTodo  ، deleteTodo ، toggleTodo اضافه میکنیم. در کد زیر، در بالای هر تابع، توضیحی در مورد عملکرد تابع ذکر شده است:
class Model {
    constructor() {
        // یک آرایه از اطلاعات پیش فرض 
        this.todos = [{
                id: 1,
                text: 'Run a marathon',
                complete: false
            },
            {
                id: 2,
                text: 'Plant a garden',
                complete: false
            },
        ]
    }

    // متدی برای افزودن آیتم جدید به آرایه
    addTodo(todoText) {
        const todo = {
            id: this.todos.length > 0 ? this.todos[this.todos.length - 1].id + 1 : 1,
            text: todoText,
            complete: false,
        }

        this.todos.push(todo)
    }

    // متدی برای بروزسانی آیتم مورد نظر
    editTodo(id, updatedText) {
        this.todos = this.todos.map(todo =>
            todo.id === id ? {
                id: todo.id,
                text: updatedText,
                complete: todo.complete
            } : todo
        )
    }

    // انجام میدهد filter با استفاده از متد id  تابعی که عملیات حذف را بوسیله فیلد   
    deleteTodo(id) {
        this.todos = this.todos.filter(todo => todo.id !== id)
    }

    //  متدی که در آن مشخص میکنیم کار مد نظرانجام شده یا خیر 
    toggleTodo(id) {
        this.todos = this.todos.map(todo =>
            todo.id === id ? {
                id: todo.id,
                text: todo.text, 
                complete: !todo.complete
            } : todo
        )
    }
}

میتوانیم برای تست و نحوه عملکرد آن با استفاده از شیء app،  با دستور زیر، آیتمی را به آرایه اضافه کنیم و در کنسول آن را نمایش دهیم:
app.model.addTodo('Take a nap')
console.log(app.model.todos)

در حال حاضر با هر بار reload  شدن صفحه، فقط اطلاعات پیش فرض، درون آرایه todos قرار میگیرد؛ ولی در ادامه آن را در local storage ذخیره میکنیم.

برای ساختن قسمت View، از جاوااسکریپت استفاده میکنیم و DOM را تغییر میدهیم. البته اینکار را بدون استفاده از JSX و یا یک templating language انجام خواهیم داد. قسمت‌های دیگر برنامه مانند Controller و Model نباید درگیر تغییرات DOM یا CSS یا عناصر HTML باشند و تمام این موارد توسط View هندل میشود. کد View به نحو زیر خواهد بود:

class View {
    constructor() {}

    // ایجاد یک المنت با کلاسهای استایل دلخواه
    createElement(tag, className) {
        const element = document.createElement(tag)
        if (className) element.classList.add(className)

        return element
    }

    // DOM انتخاب و گرفتن آیتمی خاص از 
    getElement(selector) {
        const element = document.querySelector(selector)

        return element
    }
}

سپس قسمت سازنده کلاس View را تغییر میدهیم و تمام المنت‌های مورد نیاز را در آن ایجاد میکنیم:

  • ارجاعی به المنتی با آی‌دی root
  • تگ h1 برای عنوان
  • یک form، input  و دکمه‌ای برای افزودن آیتمی جدید به آرایه‌ی todos
  • یک المنت ul برای نمایش آیتم‌های  todos
سپس کلاس  View به شکل زیر خواهد بود:
    constructor() {
        // root ارجاعی به المنتی با آیدی
        this.app = this.getElement('#root')

        // عنوان برنامه
        this.title = this.createElement('h1')
        this.title.textContent = 'Todos'

        // فرم ، اینپوت ورودی و دکمه
        this.form = this.createElement('form')

        this.input = this.createElement('input')
        this.input.type = 'text'
        this.input.placeholder = 'Add todo'
        this.input.name = 'todo'

        this.submitButton = this.createElement('button')
        this.submitButton.textContent = 'Submit'

        // برای نمایش عناط آرایه یا همان لیست کارها
        this.todoList = this.createElement('ul', 'todo-list')

        // افزودن اینپوت ورودی و دکمه به فرم
        this.form.append(this.input, this.submitButton)

        // ایجاد شده است app که اینجا ارجاعی به آن بنام  root اضافه کردن تمام آیتمهای بالا در المنتی با آیدی 
        this.app.append(this.title, this.form, this.todoList)
    }

در قسمت View، دو تابع هم برای getter و setter داریم که از underscore در اول نام آنها استفاده شده که نشان دهنده این است، توابع از خارج از کلاس در دسترس نیستند (شبیه private  در سی شارپ؛ البته این یک قرارداد هست یا convention)
get _todoText() {
  return this.input.value
}

_resetInput() {
  this.input.value = ''
}

در ادامه این کلاس، یک تابع دیگر هم برای نمایش آرایه داریم که هر زمان عناصر آن تغییر کردند، بتواند نمایش به‌روز اطلاعات را نشان دهد:
displayTodos(todos){
 //...
}

متد displayTodos یک المنت ul و li‌هایی را به تعداد عناصر todos ایجاد میکند و آنها را نمایش میدهد. هر زمانکه تغییراتی مانند اضافه شدن، حذف و ویرایش در todos صورت گیرد، این متد دوباره فراخوانی میشود و لیست جدید را نمایش میدهد. محتوای متد dispayTodos به شکل زیر خواهد بود:

  displayTodos(todos) {
    // حذف تمام نودها
    while (this.todoList.firstChild) {
      this.todoList.removeChild(this.todoList.firstChild)
    }

    // اگر هیچ آیتمی در آرایه نبود این پاراگراف با متن پیش فرض نمایش داده میشود
    if (todos.length === 0) {
      const p = this.createElement('p')
      p.textContent = 'Nothing to do! Add a task?'
      this.todoList.append(p)
    } else {
      // وعناصرمربوطه را ایجاد میکند liاگه درون آرایه آیتمی قرار دارد پس به ازای آن یک عنصر 
      todos.forEach(todo => {
        const li = this.createElement('li')
        li.id = todo.id

        const checkbox = this.createElement('input')
        checkbox.type = 'checkbox'
        checkbox.checked = todo.complete

        const span = this.createElement('span')
        span.contentEditable = true
        span.classList.add('editable')

        if (todo.complete) {
          const strike = this.createElement('s')
          strike.textContent = todo.text
          span.append(strike)
        } else {
          span.textContent = todo.text
        }

        const deleteButton = this.createElement('button', 'delete')
        deleteButton.textContent = 'Delete'
        li.append(checkbox, span, deleteButton)

        // نود ایجاد شده به لیست اضافه میکند
        this.todoList.append(li)
      })
    }

    // برای خطایابی و نمایش در کنسول
    console.log(todos)
  }


در نهایت قسمت Controller را که پل ارتباطی بین View و Model می‌باشد، کامل میکنیم. اولین تغییراتی که در کلاس Controller ایجاد میکنیم، استفاده از متد displayTodos در سازنده‌ی این کلاس می‌باشد و با هر بار تغییر این متد، دوباره فراخوانی میشود:

class Controller {
  constructor(model, view) {
    this.model = model
    this.view = view

    // نمایش اطلاعات پیش فرض
    this.onTodoListChanged(this.model.todos)
  }

  onTodoListChanged = todos => {
    this.view.displayTodos(todos)
  }
}


چهار تابعی را که در قسمت Model ایجاد نمودیم و کار ویرایش، حذف، افزودن و اتمام کار را انجام میدادند، در کلاس کنترلر آنها را هندل میکنیم و زمانیکه کاربر دکمه‌ای را برای افزودن یا تیک حذف آیتمی، زد، تابع مربوطه توسط کنترلر در Model فراخوانی شود:

handleAddTodo = todoText => {
  this.model.addTodo(todoText)
}

handleEditTodo = (id, todoText) => {
  this.model.editTodo(id, todoText)
}

handleDeleteTodo = id => {
  this.model.deleteTodo(id)
}

handleToggleTodo = id => {
  this.model.toggleTodo(id)
}

چون کنترلر نمیتواند بصورت مستقیم فراخوانی شود و این توابع باید درون DOM تنظیم شوند تا به ازای رخدادهایی همچون click و change، فراخوانی شوند. پس از این توابع در قسمت View استفاده میکنیم و به کلاس View، موارد زیر را اضافه میکنیم:

bindAddTodo(handler) {
  this.form.addEventListener('submit', event => {
    event.preventDefault()

    if (this._todoText) {
      handler(this._todoText)
      this._resetInput()
    }
  })
}

bindDeleteTodo(handler) {
  this.todoList.addEventListener('click', event => {
    if (event.target.className === 'delete') {
      const id = parseInt(event.target.parentElement.id)

      handler(id)
    }
  })
}

bindToggleTodo(handler) {
  this.todoList.addEventListener('change', event => {
    if (event.target.type === 'checkbox') {
      const id = parseInt(event.target.parentElement.id)

      handler(id)
    }
  })
}


برای bind کردن این متدها در کلاس Controller، کدهای زیر را اضافه میکنیم:

this.view.bindAddTodo(this.handleAddTodo)
this.view.bindDeleteTodo(this.handleDeleteTodo)
this.view.bindToggleTodo(this.handleToggleTodo)


برای ذخیره اطلاعات در local storage، در سازنده کلاس Model، کد زیر را اضافه میکنیم:

 this.todos = JSON.parse(localStorage.getItem('todos')) || []

متد دیگری هم در کلاس Model برای به‌روز رسانی مقادیر local storage قرار میدهیم:

_commit(todos) {
  this.onTodoListChanged(todos)
  localStorage.setItem('todos', JSON.stringify(todos))
}

متدی هم برای تغییراتی که هر زمان بر روی todos  اتفاق می‌افتد، فراخوانی شود:

deleteTodo(id) {
  this.todos = this.todos.filter(todo => todo.id !== id)

  this._commit(this.todos)
}
در پایان میتوانید سورس کد مقاله جاری را از اینجا دانلود نمایید.
این مقاله صرفا جهت آشنایی و نمونه کدی از پیاده سازی الگوی  MVC  در جاوااسکریپت می‌باشد.
مطالب
پیاده سازی پروژه‌های React با TypeScript - قسمت هشتم - تعیین نوع کامپوننت‌های کلاسی
با ارائه‌ی React Hooks، استفاده‌ی از کامپوننت‌های کلاسی، کمتر و کمتر شده‌اند. اما جهت تکمیل و خاتمه‌ی سری جاری، این نوع را نیز در این قسمت بررسی می‌کنیم.


تعیین نوع props و state در کامپوننت‌های کلاسی

برای این منظور ابتدا فایل جدید src\components\BigC.tsx را ایجاد کرده و سپس توسط میانبر rcc، ساختار ابتدایی این کامپوننت را ایجاد می‌کنیم. در ادامه آن‌را در کامپوننت src\App.tsx استفاده خواهیم کرد:


یکی از مزایای دیگر کار با تایپ‌اسکریپت، فعال شدن intellisense مرتبط با افزونه‌هایی مانند typescript hero و auto import است که نمونه‌ای از آن‌را در تصویر فوق مشاهده می‌کنید. فقط کافی است نام المان مرتبط را نوشت و سپس با استفاده از افزونه‌های یاد شده، به صورت خودکار import آن‌را اضافه کرد.

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


در اینجا نیز همانند کامپوننت‌های تابعی، باید ابتدا نوع Props را تعیین کرد و تنها تفاوت آن، روش معرفی این type جدید به کامپوننت است:
import React, { Component } from "react";

type Props = {
  title: string;
};

type State = {
  status: string;
};

export default class BigC extends Component<Props, State> {
  render() {
    return (
      <div>
        <h1>I'm in a class component</h1>
      </div>
    );
  }
}
در کامپوننت‌های کلاسی، نوع State و نوع Props، از طریق آرگومان‌های جنریک Component، به نحوی که مشاهده می‌کنید، مشخص می‌شوند.
مابقی نکات آن مانند props اختیاری و غیره، تفاوت خاصی را با کامپوننت‌های تابعی که پیشتر بررسی کردیم، ندارند و یکی هستند.


مقایسه‌ای بین Types و Interfaces

در این سری بیشتر از types استفاده شد، تا اینترفیس‌های تایپ‌اسکریپت. برای مثال بجای نوع زیر:
type State = {
   rValue: boolean;
};
می‌توان از اینترفیس معادل آن نیز استفاده کرد:
interface State {
  rValue: boolean;
}
اما از کدامیک باید استفاده کرد؟
در تایپ‌اسکریپت، از type بیشتر برای تعریف یک نوع جدید استفاده می‌شود، اما از اینترفیس‌ها برای تعریف موجودیت‌ها و entities. برای مثال از type می‌توان برای تعریف نامی برای نوع‌های primitive, union, intersection نیز استفاده کرد، اما اینترفیس‌ها همواره برای تعیین نوع اشیاء مورد استفاده قرار می‌گیرند. نمی‌توان دو نوع هم‌نام را تعریف کرد، اما اگر دو اینترفیس هم نام را تعریف کنید، با هم یکی می‌شوند. نوع‌ها برخلاف اینترفیس‌ها قابلیت پیاده سازی نداشته و بیشتر جنبه‌ی یک اعلان را دارند و ... در نهایت استفاده‌ی از هر کدام بیشتر یک انتخاب شخصی است.


منابع تکمیلی

یکی از بهترین منابع تکمیلی استفاده‌ی از TypeScript در React، مخزن کد react-typescript-cheatsheet است که به همراه مجموعه‌ای از نکات مرتبط است.
نظرات مطالب
مشکل همزمانی خواندن و به روز رسانی اطلاعات در برنامه‌های وب
شبیه سازی Row level locking در Entity Framework


using (var scope = new TransactionScope(...))
{
    using (var context = new YourContext(...))
    {
        var wallet = 
            context.ExecuteStoreQuery<UserWallet>("SELECT UserId, WalletId, Balance FROM UserWallets WITH (UPDLOCK) WHERE ...");

        // your logic

        scope.Complete();
    }
}

اشتراک‌ها
بررسی تغییرات Blazor در دات نت 8

What's New in Blazor for .NET 8
Come find out about the future of Blazor in .NET 8! We'll explore all the upcoming features and improvements, including our effort to create a unified full stack web UI programming model that combines the strengths of client and server. We hope to see you there!

You will learn:
How Blazor is becoming the best option for full stack web development
How Blazor in .NET 8 will provide full flexibility to build web apps however works best for you
How to try out the latest Blazor features in .NET 8
 

بررسی تغییرات Blazor در دات نت 8
مطالب
مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت هفتم - بررسی مفاهیم Mobx
MobX از 4 مفهوم اصلی تشکیل می‌شود:

- Observable state: در MobX نیز همانند Redux، کل شیء state به صورت یک شیء جاوا اسکریپتی ارائه می‌شود؛ با این تفاوت که در اینجا این شیء، یک Observable است که نمونه‌ای از مفهوم آن‌را در مثال قسمت قبل بررسی کردیم.
- Actions: متدهایی هستند که state را تغییر می‌دهند.
- Computed properties: در مورد مفهوم خواص محاسباتی در قسمت قبل بحث کردیم. این خواص، مقدار خود را بر اساس تغییرات سایر خواص Observable، به روز می‌کنند.
- Reactions: سبب بروز اثرات جانبی (side effects) می‌شوند؛ مانند تعامل با دنیای خارج. نمونه‌ای از آن، متد autorun است که تغییرات Observableها را ردیابی می‌کند.

برای مثال خاصیت محاسباتی fullName، تغییرات سایر خواص Observable را احساس کرده و مقدار خودش را به روز می‌کند. سپس یک Reaction به آن، می‌تواند به روز رسانی DOM، جهت نمایش این تغییرات باشد و یا نمونه‌ی دیگری که می‌تواند بسیاری از این مفاهیم را نمایش دهد، کلاس زیر است:
import { action, observable, computed } from 'mobx';

class PizzaCalculator {
  @observable numberOfPeople = 0;
  @observable slicesPerPerson = 2;
  @observable slicesPerPie = 8;

  @computed get slicesNeeded() {
    console.log('Getting slices needed');
    return this.numberOfPeople * this.slicesPerPerson;
   }

  @computed get piesNeeded() {
    console.log('Getting pies needed');
    return Math.ceil(this.slicesNeeded / this.slicesPerPie);
   }

   @action addGuest() {
     this.numberOfPeople!++;
   }
}
- دراینجا با استفاده از decorator syntax کتابخانه‌ی mobx، خواص و متدهای این کلاس معمولی ES6 را مزین کرده‌ایم.
- برای مثال زمانیکه تعریف observable numberOfPeople@ را داریم، به این معنا است که می‌خواهیم تغییرات تعداد افراد را تحت نظر قرار دهیم و اگر تغییری در مقدار آن صورت گرفت، آنگاه مقدار خواص محاسباتی که با computed@ مزین شده‌اند، به صورت خودکار به روز رسانی شوند.
- action@ به این معنا است که متدی در اینجا، سبب بروز تغییری در state کلاس جاری می‌شود. MobX به همراه یک strict mode است که اگر فعال باشد، ذکر تزئین کننده‌ی action@ بر روی یک چنین متدهایی ضروری است، در غیراینصورت، الزامی به درج آن نیست.

در این قطعه کد تعدای console.log را هم ملاحظه می‌کنید. علت آن نمایش مفهوم کش کردن اطلاعات در MobX است. فرض کنید برای بار اول، مقدار یکی از خواصی را که به صورت observable تعریف شده‌اند، تغییر می‌دهیم. در این حالت تمام خواص محاسباتی وابسته‌ی به آن‌ها، به صورت خودکار مجددا محاسبه شده و console.log‌ها را نیز مشاهده خواهیم کرد. اگر برای بار دوم همین فراخوانی صورت گیرد و تغییری در مقادیر خواص observable صورت نگیرد، MobX از نگارش کش شده‌ی این خواص محاسباتی استفاده می‌کند و بی‌جهت سبب رندر مجدد UI نخواهد شد که در نهایت کارآیی بالایی را سبب خواهد شد. برای پیاده سازی یک چنین قابلیتی با Redux باید از مفهومی مانند React.memo و Memoization و کتابخانه‌های کمکی مانند Reselect استفاده کرد؛ اما در اینجا به صورت توکار و خودکار اعمال می‌شود.


ساختارهای داده‌ای که توسط MobX پشتیبانی می‌شوند

MobX از اکثر ساختارهای داده‌ای متداول در جاوا اسکریپت پشتیبانی می‌کند؛ برای مثال:
- اشیاء مانند ({})observable
- آرایه‌ها مانند ([])observable
- Maps مانند observable(new Map())

چند نکته:
- همانطور که در قسمت قبل نیز ذکر شد، decorators در اصل یکسری تابع هستند و برای مثال می‌توان observable را به صورت observable@ و یا به صورت یک تابع معمولی مورد استفاده قرار داد.
- اگر شیء‌ای را به صورت ({})observable معرفی کنیم، با افزودن خواصی به آن پس از این فراخوانی، این خواص دیگر مورد ردیابی قرار نخواهند گرفت. علت آن‌را هم در شبه‌کد زیر می‌توان مشاهده کرد:
const extendObservable = (target, source) => {
  source.keys().forEach(key => {
    const wrappedInObservable = observable(source[key]);
    Object.defineProperty(target, key, {
      set: value.set.
      get: value.get
    });
  });
};
کاری که متد observable انجام می‌دهد، شمارش کلیدهای (خواص) شیء ارسالی به آن است و سپس محصور کردن آن‌ها درون یک شیء observable و در آخر بازگشت آن.
برای رفع این مشکل می‌توان از Map استفاده کرد. یعنی در اینجا اگر قرار است تعداد خواص اشیاء را به صورت پویا تغییر دهید، آن‌ها را به صورت Map تعریف کنید؛ چون متد set آن توسط observableها ردیابی می‌شود.


استفاده از MobX با React توسط کتابخانه‌ی mobx-react

تا اینجا MobX را به صورت متکی به خود مورد بررسی قرار دادیم. اکنون قصد داریم آن‌را به یک برنامه‌ی React متصل کنیم. برای اینکار کتابخانه‌های زیادی وجود دارند که در این قسمت کلیات روش کار با کتابخانه‌ی mobx-react را در بین آن‌ها بررسی می‌کنیم.

نصب کتابخانه‌ی mobx-react

ابتدا نیاز است تا این کتابخانه را نصب کنیم:
 > npm install --save mobx mobx-react

تحت نظر قرار دادن کامپوننت‌ها

در ادامه پس از نصب کتابخانه‌ی mobx-react، نیاز است کامپوننت‌ها را تحت نظر MobX قرار دهیم که اینکار را می‌توان توسط تزئین کننده‌ی observer آن انجام داد. همانطور که عنوان شد، تزئین کننده‌ها را می‌توان به صورت معمولی observer@ به یک کلاس و یا به صورت فراخوانی تابع، برای مثال به یک کامپوننت تابعی اعمال کرد. برای نمونه کامپوننت‌های کلاسی را به نحو زیر می‌توان با observer@ مزین کرد:
import { observer } from "mobx-react";

@observer class Counter extends Component {
در این حالت هر زمانیکه یکی از اشیاء observable تغییر می‌کند، React را وادار به رندر مجدد UI خواهد کرد.

و یا کامپوننت‌های تابعی را می‌توان توسط متد observer به صورت زیر محصور کرد:
const Counter = observer(({ count }) => {
  return (
   // ...
  );
});
با تحت نظر قرار گرفته شدن یک کامپوننت (چه با تزئین کننده‌ی observer@ و یا با بکارگیری نگارش تابعی آن)، منطقی که در پشت صحنه مورد استفاده قرار می‌گیرد، یک چنین شکلی را خواهد داشت (و برای اینکار نیازی به کد نویسی نیست):
class ContainerComponent extends Component () {
   componentDidMount() {
     this.stopListening = autorun(() => this.render());
   }

   componentWillUnmount() {
     this.stopListening();
   }

   render() { … }
}
زمانیکه کار رندر اولیه‌ی کامپوننت در DOM به پایان رسید، متد autorun به تغییرات observableها در پشت صحنه گوش‌فرا داده و سبب فراخوانی متد رندر کامپوننت، با هر تغییر لازمی می‌شود. این کاری است که متد یا تزئین کننده‌ی observer کتابخانه‌ی mobx-react انجام می‌دهد.

تعریف مخزن و اتصال آن به کامپوننت‌ها

کار شیء Provider که بالاترین کامپوننت را در سلسله مراتب کامپوننت‌ها محصور می‌کند، ارائه‌ی store، به تمام کامپوننت‌های فرزند است. در Redux فقط یک store را داریم که  به شیء Provider آن ارسال می‌کنیم. اما در حین کار با MobX چنین محدودیتی وجود ندارد و می‌توان چندین store را تعریف کرد و در اختیار برنامه قرار داد که شبه‌کد نحوه‌ی تعریف آن به صورت زیر است:
import { Provider } from 'mobx-react';

import ItemStore from './store/ItemStore';
import Application from './components/Application';

const itemStore = new ItemStore();

ReactDOM.render(
   <Provider itemStore={itemStore}>
     <Application />
   </Provider>,
   document.getElementById('root'),
);
در حین کار با Redux، قسمتی از مراحل تعریف آن، کار اتصال خواص موجود در state مخزن redux، به props یک کامپوننت است و یا همچنین کار اتصال رویدادها به props. یک چنین کاری را در اینجا به سادگی با تزئین کننده‌ای به نام inject می‌توان انجام داد که مخزن مورد استفاده را مشخص می‌کند:
@inject('itemStore')
class NewItem extends Component {
// ...
و یا برای کامپوننت‌های تابعی می‌توان از نگارش تابعی inject استفاده کرد. در این حالت، store تزریقی را می‌توان به صورت props دریافت نمود:
const UnpackedItems = inject('itemStore')(
    observer(({ itemStore }) => (
    // ...
  )),
);


یک مثال: پیاده سازی مثال شمارشگر قسمت سوم این سری با mobx-react

در ادامه قصد داریم برنامه‌ی شمارشگر ارائه شده در قسمت سوم بررسی redux را با mobx پیاده سازی کنیم. به همین جهت یک پروژه‌ی جدید React را ایجاد می‌کنیم:
> create-react-app state-management-with-mobx-part2
> cd state-management-with-mobx-part2
> npm start
در ادامه کتابخانه‌ها‌ی mobx ، mobx-react و همچنین بوت استرپ را نصب می‌کنیم. برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save mobx mobx-react bootstrap
سپس برای افزودن فایل bootstrap.css به پروژه‌ی React خود، ابتدای فایل index.js را به نحو زیر ویرایش خواهیم کرد:
import "bootstrap/dist/css/bootstrap.css";

پس از آن فایل src\index.js را به صورت زیر تغییر می‌دهیم:
import "./index.css";
import "bootstrap/dist/css/bootstrap.css";

import { autorun, decorate, observable } from "mobx";
import React from "react";
import ReactDOM from "react-dom";

import Counter from "./components/Counter";
import * as serviceWorker from "./serviceWorker";

class Count {
  value = 0;

  increment = () => {
    this.value++;
  };

  decrement = () => {
    this.value--;
  };
}

decorate(Count, { value: observable });

const count = (window.count = new Count());
autorun(() => console.log("The count changed!", count.value));

ReactDOM.render(
  <main className="container">
    <Counter count={count} />
  </main>,
  document.getElementById("root")
);

serviceWorker.unregister();
توضیحات:
- در قسمت قبل، روش تحت نظر قرار دادن یک شیء متداول جاوا اسکریپتی را توسط متد observable مشاهده کردیم. در اینجا نگارش کلاسی آن مثال را بر اساس کلاس Count مشاهده می‌کنید. اگر نخواهیم از decorator ای مانند observable@ بر روی خاصیت value این کلاس استفاده کنیم، روش تابعی آن‌را با فراخوانی متد decorate و ذکر نوع کلاس و سپس خاصیتی که باید به صورت observable تحت نظر قرار گیرد، در اینجا مشاهده می‌کنید. این هم یک روش کار با mobx است.
- پس از ایجاد کلاس Count که در اینجا نقش store را نیز بازی می‌کند، یک وهله‌ی جدید را از آن ساخته و به متغیر count در این ماژول و همچنین window.count انتساب می‌دهیم. انتساب window.count سبب می‌شود تا بتوان در کنسول توسعه دهندگان مرورگر، با نوشتن count و سپس enter، به محتویات این متغیر دسترسی یافت و یا حتی آن‌را تغییر داد؛ مانند تصویر زیر که بلافاصله این تغییر، در UI برنامه نیز منعکس می‌شود:


- در اینجا تعریف شیء Provider را که پیشتر در مورد آن بحث کردیم، مشاهده نمی‌کنید؛ چون با تک کامپوننت Counter تعریف شده‌ی در این مثال، نیازی به آن نیست. می‌توان این شیء store را به صورت مستقیم به props کامپوننت Counter ارسال کرد.

اکنون تعریف کامپوننت شمارشگر واقع در فایل src\components\Counter.jsx به صورت زیر خواهد بود که این کامپوننت، count را به صورت props دریافت می‌کند:
import { observer } from "mobx-react";
import React from "react";

const Counter = observer(({ count }) => {
  return (
    <section className="card mt-5">
      <div className="card-body text-center">
        <span className="badge m-2 badge-primary">{count.value}</span>
      </div>
      <div className="card-footer">
        <div className="d-flex justify-content-center align-items-center">
          <button
            className="btn btn-secondary btn-sm"
            onClick={count.increment}
          >
            +
          </button>
          <button
            className="btn btn-secondary btn-sm m-2"
            onClick={count.decrement}
          >
            -
          </button>
        </div>
      </div>
    </section>
  );
});

export default Counter;
و سپس بر اساس count رسیده، در اینجا می‌توان مستقیما متدهای کلاس Count را فراخوانی کرد (مانند count.increment؛ که البته در اصل یک خاصیت است که با متدی مقدار دهی شده‌است) و یا مقدار خاصیتی از آن‌را مانند count.value، نمایش داد.
تا زمانیکه کامپوننت، با تابع observer محصور شده‌است، به props رسیده گوش فرا داده و خواص و اشیاء observable آن‌را تشخیص می‌دهد و سبب رندر مجدد کامپوننت، با تغییری در آن‌ها خواهد شد.

کدهای کامل این قسمت را می‌توانید از اینجا دریافت کنید: state-management-with-mobx-part2.zip
مطالب
جایگزین کردن jQuery با JavaScript خالص - قسمت سوم - تغییر شیوه‌نامه‌ی المان‌ها
در این قسمت روش جایگزین کردن متد css جی‌کوئری را با کدهای خالص جاوا اسکریپتی بررسی می‌کنیم.

کار با Inline Styles
  <h1>News</h1>
  <div>Welcome to our site!</div>

  <h2>World</h2>

  <h3>Title 1</h3>
  <div>description 1.</div>

  <h2>Science</h2>

  <h3>Title 2</h3>
  <div>description 2.</div>
در این مثال می‌خواهیم با استفاده از جاوا اسکریپت، المان‌های h2 و h3 را یافته و سپس h2ها را آبی و h3ها را سبز کنیم:
  var headings = document.querySelectorAll('h2, h3');
  for (var i = 0; i < headings.length; i++) {
    if (headings[i].tagName === 'H2') {
      headings[i].style.color = 'blue';
    }
    else {
      headings[i].style.color = 'green';
    }
  }
برای تغییر inline style المان‌ها، از خاصیت style آن‌ها استفاده می‌شود که در نهایت این شیوه‌نامه‌های جدید توسط ویژگی style به همان المان اضافه می‌شوند:
<h2 style="color: blue">….</h2>
<h3 style="color: green">….</h3>
خاصیت style جزو استاندارد DOM Level 2 است که در سال 2000 تصویب شده‌است (از زمان IE 8.0 به بعد در دسترس است). باید دقت داشت که از این روش بیشتر در کتابخانه‌های جاوا اسکریپتی برای شرایطی خاص، جهت تغییر پویای رابط کاربری استفاده می‌شود و هر تغییری که در اینجا اعمال شود، مقادیر قبلی موجود را بازنویسی می‌کند.
همچنین اگر بخواهیم به یک المان چندین شیوه‌نامه را انتساب دهیم، روش کار به صورت زیر است:
  <h2>World</h2>

  ...

  <h2>Science</h2>

 <script>
  var headings = document.querySelectorAll('h2');
  for (var i = 0; i < headings.length; i++) {
    headings[i].style.color = 'blue';
    headings[i].style.fontWeight = 'bold';
  }
 </script>
پس از یافتن المان‌های مدنظر، تنها کافی است نام شیوه‌نامه‌ی مدنظر را به خاصیت style اضافه و مقدار دهی کنیم. در اینجا نام شیوه‌نامه‌ای که «کبابی» باشد، مانند font-weight، به صورت camel case مانند fontWeight درج خواهد شد؛ هرچند از همان نام اصلی نیز می‌توان به صورت زیر استفاده کرد:
 headings[i].style['font-weight'] = 'bold';
روش دیگری نیز برای انجام این تغییرات چندتایی وجود دارد:
  var headings = document.querySelectorAll('h2');

  for (var i = 0; i < headings.length; i++) {
    headings[i].style.cssText = 'color: blue; font-weight: bold';
  }
خاصیت style یک المان، از نوع اینترفیس CSSStyleDeclaration است که دارای خاصیت استاندارد cssText نیز می‌باشد. توسط این خاصیت می‌توان چندین شیوه‌نامه را به صورت یکجا به عنصری انتساب داد و یا تمام آن‌ها را خواند.


کار با Style Sheets

Inline styles تنها روش کار با شیوه‌نامه‌ها نیست. روش صحیح و قابل مدیریت کار با شیوه‌نامه‌ها استفاده از فایل‌های style sheets است. برای مثال تغییرات قبل را می‌توان در فایلی به نام styles.css و با محتوای زیر ایجاد کرد:
h2 { color: blue; }
h3 { color: green; }
و سپس آن‌را به صفحه متصل نمود:
 <link href="styles.css" rel="style sheet">
و یا حتی می‌توان این شیوه نامه را به صورت inline نیز به ابتدای صفحه اضافه نمود:
<style>
  h2 { color: blue; }
  h3 { color: green; }
</style>
اما ممکن است در برنامه بخواهیم امکان تغییر پویای قالب را به کاربران بدهیم. در یک چنین حالتی اعمال این نوع شیوه‌نامه‌ها توسط جاوا اسکریپت مفهوم پیدا می‌کند:
var sheet = document.styleSheets[0];
sheet.insertRule('h2 { font-style: italic; }', sheet.cssRules.length - 1);
اولین سطر، اولین تگ style اضافه شده به صفحه را یافته (این style می‌تواند inline و یا لینک شده‌ی توسط یک فایل باشد) و سپس شیوه نامه‌ی جدیدی را توسط متد insertRule، در انتهای آن به صورت پویا درج می‌کند.


مخفی کردن و نمایش دادن المان‌ها در صفحه

جی‌کوئری به همراه متدهای hide و show است که کار مخفی کردن و یا نمایش دادن مجدد یک المان‌را انجام می‌دهند:
// hide an element
$element.hide();

// show it again
$element.show();
در کل روش‌های زیادی برای مخفی کردن یک المان وجود دارند. برای مثال می‌توان opacity آن‌را به صفر تنظیم کرد و یا position آن‌را به absolute و سپس آن‌را در مختصاتی خارج از صفحه قرار داد. اما عموما خاصیت display را به none تنظیم می‌کنند. همچنین در استاندارد W3C HTML5، خاصیت جدید hidden از نوع boolean نیز به المان‌ها اضافه شده‌اند که دقیقا برای همین‌منظور بکار می‌رود. مزیت مهم این خاصیت نه فقط استاندارد بودن آن، بلکه بالابردن دسترسی پذیری المان‌های صفحه توسط برنامه‌های «screen reader» مخصوص معلولین است. بنابراین با استفاده از جاوا اسکریپت خالص برای مخفی کردن یک المان می‌توان نوشت:
element.setAttribute('hidden', '');
این روش 25 بار سریعتر از متد hide جی‌کوئری است! از این جهت که jQuery در پشت صحنه مدام متد window.getComputedStyle را برای موارد خاص و بحرانی کار با شیوه‌نامه‌ها فراخوانی می‌کند (در تمام متدهایی که با CSS کار می‌کنند) و این متد تاثیر منفی بر روی کارآیی برنامه دارد.
و چون خاصیت hidden از نوع Boolean است، ذکر آن در یک المان یعنی تنظیم آن به true و حذف آن، یعنی تنظیم آن به false یا نمایش مجدد المان در اینجا:
element.removeAttribute('hidden');


اندازه‌گیری تاثیر شیوه‌نامه‌ها بر روی طول و عرض المان‌ها

CSS Box Model یک چنین تعریفی را دارد:


زمانیکه از متدهای ()width و ()height جی‌کوئری بر روی المانی استفاده می‌شود، صرفا طول و عرض قسمت «content» را دریافت خواهید کرد.
برای این منظور در جاوا اسکریپت خالص این خواص در اختیار ما است:
<style>
  .box {
    padding: 10px;
    margin: 5px;
    border: 3px solid;
    display: inline-block;
  }
</style>
<span class="box">a box</span>
روش اندازه گیری Content + Padding توسط جاوا اسکریپت خالص:
 // returns 38
var clientHeight = document.querySelector('.box').clientHeight;

// returns 55
var clientWidth = document.querySelector('.box').clientWidth;
این خواص هرچند اخیرا به استانداردهای CSS به صورت رسمی اضافه شده‌اند، اما از زمان IE 6.0 پشتیبانی می‌شده‌اند و با متدهای ()innerWidth و ()innerHeight جی‌کوئری قابل مقایسه هستند.

روش اندازه گیری Content + Padding + Border توسط جاوا اسکریپت خالص:
 // returns 44
var offsetHeight = document.querySelector('.box').offsetHeight;

// returns 61
var offsetWidth = document.querySelector('.box').offsetWidth;
این خواص از زمان IE 8.0 پشتیبانی می‌شده‌اند.
مطالب
استفاده از قابلیت پارتیشن بندی در آرشیو جداول بانک‌های اطلاعاتی SQL Server

1- مقدمه

پارتیشن بندی در بانک اطلاعاتی SQL Server، از ویژگی‌هایی است که از نسخه 2005، به این محصول اضافه شده است. بکارگیری این قابلیت که با Split کردن، محتوای یک جدول و قرار دادن آنها در چندین فایل، برای جداول حجیم، به  ویژه جداولی که داده‌های آن حاوی مقادیر تاریخچه‌ای است، بسیار سودمند است.
سادگی در مدیریت داده‌ها و شاخص‌های موجود یک جدول (از قبیل اندازه  فضای ذخیره سازی و استراتژی جدید Back up گیری)، اجرای سریعتر کوئری هایی که روی یک محدوده از داده‌ها کار  می‌کنند و سهولت در آرشیو داده‌های قدیمی یک جدول، از قابلیت‌هایی است که استفاده از این ویژگی بوجود می‌آورد.
محدوده استفاده از این ویژگی روی یک بانک اطلاعاتی و در یک Instance است. بنابراین مباحث مرتبط با معماری Scalability را پوشش نمی‌دهد و صرفاً Solution ایی است که در یک Instance بانک اطلاعاتی استفاده می‌شود.

2- Data File و Filegroup

هر بانک اطلاعاتی در حالت پیش فرض، شامل یک فایل داده‌ای (MDF.) و یک فایل ثبت تراکنشی (LDF.) می‌باشد. می‌توان جهت ذخیره سطر‌های داده‌ای از فایل‌های بیشتری تحت نام فایل‌های ثانویه (NDF.) استفاده نمود. به همان طریق که در فایل سیستم، فایل‌ها به پوشه‌ها تخصیص داده می‌شوند، می‌توان Data File را به Filegroup تخصیص داد. چنانچه چندین Data File به یک Filegroup تخصیص داده شوند، داده‌ها در تمامی Data File‌ها به طریق Round-Robin توزیع می‌شوند.

3- Partition Function

مطابق با مقادیر تعریف شده در بدنه دستور، محدوده داده‌ای (پارتیشن‌ها) با استفاده از Partition Function ایجاد می‌شود. با در نظر گرفتن ستونی که به عنوان Partition Key انتخاب شده، این تابع یک Data Type را به عنوان ورودی دریافت می‌کند. در هنگام تعریف محدوده برای پارتیشن‌ها، به منظور مشخص کردن محدوده هر پارتیشن از Right و Left استفاده می‌شود.
Left نمایش دهنده‌ی حد بالای هر محدوده است و به طور مشابه، Right برای مشخص کردن حد پائین آن محدوده استفاده می‌شود. به منظور درک بهتر، به شکل زیر توجه نمائید:
 

همانطور که مشاهده می‌شود، همواره نیاز به یک Filegroup اضافه‌تری از آنچه مورد نظرتان در تعریف تابع است، می‌باشد. بنابراین اگر Function دارای n مقدار باشد، به n+1 مقدار برای Filegroup نیاز است.
همچنین هیچ محدودیتی برای اولین و آخرین بازه در نظر گرفته نمی‌شود. بنابراین جهت محدود کردن مقادیری که در این بازه‌ها قرار می‌گیرند، می‌توان از Check Constraint استفاده نمود.

3-1- Right or Left

یک سوال متداول اینکه از کدام مورد استفاده شود؟ در پاسخ باید گفت، به چگونگی تعریف پارتیشن هایتان وابسته است. مطابق شکل، تنها تفاوت  این دو، در نقاط مرزی هر یک از پارتیشن‌ها می‌باشد. در بیشتر اوقات هنگام کار با داده‌های عددی می‌توان از Left استفاده نمود و بطور مشابه هنگامیکه نوع داده‌ها از جنس زمان است، می‌توان از Right استفاده کرد.

4- Partition Schema 

گام بعدی پس از ایجاد Partition Function، تعریف Partition Schema است، که به منظور قرار گرفتن هر یک از پارتیشن‌های تعریف شده توسط Function در Filegroup‌های مناسب آن استفاده می‌شود.

5- Partition Table

گام پایانی ایجاد یک جدول، استفاده از Partition Schema است، که داده‌ها را با توجه به رویه درون Partition Function مورد استفاده، ذخیره می‌کند. همانطور که می‌دانید هنگام ایجاد یک جدول، می‌توان مکان ذخیره شدن آنرا مشخص نمود.

 Create Table <name> (…) ON …
دستور بعد از بخش ON، مشخص کننده مکان ذخیره جدول می‌باشد.

در هنگام ایجاد یک جدول، معمولاً جدول در Filegroup پیش فرض که PRIMARY است، قرار می‌گیرد. می‌توان با نوشتن نام Partition Schema و همچنین Partition Key که پیشتر ذکر آن رفت، بعد از بخش ON، برای جدول مشخص نمائیم که داده‌های آن به چه ترتیبی ذخیره شوند. ارتباط این سه به شرح زیر است:

توجه شود زمانیکه یک Primary Key Constraint به یک جدول اضافه می‌شود، یک Unique Clustered Index نیز همراه با آن ساخته می‌شود. چنانچه Primary Key شامل یک Clustered Index باشد، جدول با استفاده از این ستون (ستون‌های) شاخص ذخیره خواهد شد، در حالیکه اگر Primary Key شامل یک Non Clustered Index باشد، یک ساختار ذخیره-سازی اضافی ایجاد خواهد شد که داده‌های جدول در آن قرار خواهند گرفت.

6- Index & Data Alignment

به عنوان یک Best Practice هنگام ایجاد یک Partition Table به منظور پارتیشن بندی، از ساختار Aligned Index استفاده شود. بدین ترتیب که تعریف Index، شامل Partition Key (ستونی که معیاری برای پارتیشن بندی است) باشد. چنانچه این عمل انجام شود، داده‌های ذخیره شده مرتبط با هر پارتیشن متناظر با همان شاخص، در فایل داده‌ای (NDF.) ذخیره خواهند شد. از این رو چنانچه کوئری درخواست شده از جدول روی یک محدوده باشد

 Where [OrderDate] Between …
تنها از شاخص متناظر با این داده استفاده می‌شود. بدین ترتیب بکارگیری آن برای Execution Plan بسیار سودمند خواهد بود. همچنین می‌توان استراتژی بازیافت سودمندی با Back up گیری از Filegroup ایجاد کرد. هنگامی که Index‌ها به صورت Aligned هستند می‌توان در کسری از ثانیه، محتوای یک Partition را به یک جدول دیگر منتقل نمود (تنها با تغییر در Meta Data آن).

بدین ترتیب برای بهرمندی از این مزایا، استفاده از Aligned Index توصیه شده است.

7- Operations

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

7-1- Split Partition

به منظور ایجاد یک محدوده جدید به پارتیشن‌ها استفاده می‌شود. یک نکته مهم مادامی که عملیات انتقال داده‌ها به پارتیشن جدید انجام می‌گیرد، روی جدول یک قفل انحصاری قرار می‌گیرد و بدین ترتیب عملیات ممکن است زمانبر باشد.
به عنوان یک Best Practice همواره یک Partition خالی را Split نمائید و پس از آن اقدام به بارگذاری داده در آن نمائید.
به یاد داشته باشید پیش از انجام عملیات splitting روی Partition Function با تغییر در Partition Schema (و بکارگیری Next Used) مشخص نمائید چه محدوده‌ای در این Filegroup جدید قرار خواهد گرفت.

7-2- Merge Partition

به منظور ادغام پارتیشن‌ها استفاده می‌شود، چنانچه پارتیشن خالی نیست، برای عملیات ادغام مسائل Performance به علت اینکه در طول عملیات از Lock (قفل انحصاری) استفاده می‌شود، در نظر گرفته شود.

7-3- Switch Partition

چنانچه جدول و شاخص‌های آن به صورت Aligned هستند، می‌توانید از Switch in و Switch out استفاده نمائید. عملیات بدین ترتیب انجام می‌شود که بلافاصله محتوای یک پارتیشن یا جدول (Source) در یک پارتیشن خالی جدولی دیگر و یا یک جدول خالی (Target) قرار می‌گیرد. عملیات تنها روی Meta Data انجام می‌گیرد و هیچ داده ای منتقل نمی‌شود.
محدودیت‌های بکارگیری به شرح زیر است:
- جدول یا پارتیشن Target باید حتماً خالی باشد.
- جداول Source و Target حتماً باید در یک Filegroup یکسان قرار داشته باشند.
- جدول Source باید حاوی Aligned Index‌های مورد نیاز Target و همچنین مطابقت در Filegroup را دارا باشد.
- چنانچه Target به عنوان یک پارتیشن است، اگر Source جدول است بایست دارای یک Check Constraint باشد در غیر این صورت چنانچه یک پارتیشن است باید محدوده آن در محدوده Target قرار گیرد.

8- بررسی یک سناریوی نمونه

در ابتدا یک بانک اطلاعاتی را به طریق زیر ایجاد می‌کنیم:
این بانک مطابق تصویر، شامل 3 عدد فایل گروپ (FG1، FG2 و FG3) و 3 عدد دیتا فایل (P1، P2 و P3) می‌باشد. Filegroup پیش فرض Primary است، که چنانچه در تعریف جداول به نام Partition Schema و Partition Key مرتبط اشاره نشود، به طور پیش فرض در Filegroup موسوم به Primary قرار می‌گیرد. چنانچه چک باکس Default انتخاب شود، همانطور که قابل حدس زدن است، آن Filegroup در صورت مشخص نکردن نام Filegroup در تعریف جدول، به عنوان مکان ذخیره سازی انتخاب می‌شود. چک باکس Read Only نیز همانطور که از نامش پیداست، چنانچه روی یک Filegroup تنظیم گردد، عملیات مربوط به Write روی داده‌های آن قابل انجام نیست و برای Filegroup هایی که جنبه نگهداری آرشیو را دارند، قابل استفاده است.
چنانچه Filegroup ای را از حالت Read Only دوباره خارج کنیم، می‌توان عملیات Write را دوباره برای آن انجام داد.

پس از ایجاد بانک اطلاعاتی، گام بعدی ایجاد یک Partition Function و پس از آن یک Partition Schema است. همانطور که مشاهده می‌کنید در Partition Function از سه مقدار استفاده شده، بنابراین در Partition Schema باید از چهار Filegroup استفاده شود، که در مثال ما از Filegroup پیش فرض که Primary است، استفاده شده است.

USE [PartitionDB]
GO
CREATE PARTITION FUNCTION pfOrderDateRange(DATETIME)
AS
RANGE LEFT FOR VALUES ('2010/12/31','2011/12/31','2012/12/31')
GO
CREATE PARTITION SCHEME psOrderDateRange
AS
PARTITION pfOrderDateRange TO (FG1,FG2,FG3,[PRIMARY])
GO

پس از طی گام‌های قبل، به ایجاد یک جدول به صورت Aligned Index مبادرت ورزیده می‌شود.

CREATE TABLE Orders
(
OrderID INT IDENTITY(1,1) NOT NULL,
OrderDate DATETIME NOT NULL,
OrderFreight MONEY NULL,
ProductID INT NULL,
CONSTRAINT PK_Orders PRIMARY KEY CLUSTERED (OrderID ASC, OrderDate ASC)
ON psOrderDateRange (OrderDate)
) ON psOrderDateRange (OrderDate)
GO

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

SET NOCOUNT ON
DECLARE @OrderDate DATETIME
DECLARE @X INT
SET @OrderDate = '2010/01/01'
SET @X = 0
WHILE @X < 300
BEGIN
INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID)
VALUES( @OrderDate + @X, @X + 10, @X)
 SET @X = @X + 1
END
GO
SET NOCOUNT ON
DECLARE @OrderDate DATETIME
DECLARE @X INT
SET @OrderDate = '2011/01/01'
SET @X = 0
WHILE @X < 300
BEGIN
INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID)
VALUES( @OrderDate + @X, @X + 10, @X)
 SET @X = @X + 1
END
GO
SET NOCOUNT ON
DECLARE @OrderDate DATETIME
DECLARE @X INT
SET @OrderDate = '2012/01/01'
SET @X = 0
WHILE @X < 300
BEGIN
INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID)
VALUES( @OrderDate + @X, @X + 10, @X)
 SET @X = @X + 1
END
GO

از طریق دستور Select زیر می‌توان نحوه توزیع داده‌ها را در جدول مشاهده کرد.

USE [PartitionDB]
GO
SELECT OBJECT_NAME(i.object_id) AS OBJECT_NAME,
p.partition_number, fg.NAME AS FILEGROUP_NAME, ROWS, au.total_pages,
CASE boundary_value_on_right
WHEN 1 THEN 'Less than'
ELSE 'Less or equal than' END AS 'Comparition',VALUE
FROM sys.partitions p JOIN sys.indexes i
ON p.object_id = i.object_id AND p.index_id = i.index_id
JOIN sys.partition_schemes ps ON ps.data_space_id = i.data_space_id
JOIN sys.partition_functions f ON f.function_id = ps.function_id
LEFT JOIN sys.partition_range_values rv
ON f.function_id = rv.function_id
AND p.partition_number = rv.boundary_id
JOIN sys.destination_data_spaces dds
ON dds.partition_scheme_id = ps.data_space_id
AND dds.destination_id = p.partition_number
JOIN sys.filegroups fg
ON dds.data_space_id = fg.data_space_id
JOIN (SELECT container_id, SUM(total_pages) AS total_pages
FROM sys.allocation_units
GROUP BY container_id) AS au
ON au.container_id = p.partition_id WHERE i.index_id < 2

خروجی دستور فوق به شرح زیر است:


در ادامه به ایجاد یک Filegroup جدید می‌پردازیم.

/* Query 2-3- Split a partition*/
-- Add FG4:
ALTER DATABASE PartitionDB ADD FILEGROUP FG4
Go
ALTER PARTITION SCHEME [psOrderDateRange] NEXT USED FG4
GO
ALTER PARTITION FUNCTION [pfOrderDateRange]() SPLIT RANGE('2013/12/31')
GO
-- Add Partition 4 (P4) to FG4:
GO
ALTER DATABASE PartitionDB ADD FILE
(
NAME = P4,
FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\P4.NDF'
, SIZE = 1024KB , MAXSIZE = UNLIMITED, FILEGROWTH = 10%)
 TO FILEGROUP [FG4]
--
GO

و در ادامه به درج اطلاعاتی برای بررسی نحوه توزیع داده‌ها در Filegroup هایمان می‌پردازیم.

SET NOCOUNT ON
DECLARE @OrderDate DATETIME
DECLARE @X INT
SET @OrderDate = '2013/01/01'
SET @X = 0
WHILE @X < 300
BEGIN
INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID)
VALUES( @OrderDate + @X, @X + 10, @X)
 SET @X = @X + 1
END
GO
SET NOCOUNT ON
DECLARE @OrderDate DATETIME
DECLARE @X INT
SET @OrderDate = '2012/01/01'
SET @X = 0
WHILE @X < 300
BEGIN
INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID)
VALUES( @OrderDate + @X, @X + 10, @X)
 SET @X = @X + 1
END
GO
خروجی کار تا این مرحله به شکل زیر است:

جهت ادغام پارتیشن‌ها به طریق زیر عمل می‌شود:

/* Query 2-4- Merge Partitions */
ALTER PARTITION FUNCTION [pfOrderDateRange]() MERGE RANGE('2010/12/31')
Go
پس از اجرای دستور فوق خروجی به شکل زیر خواهد بود:

به منظور آرشیو نمودن اطلاعات به طریق زیر از Switch استفاده می‌کنیم. ابتدا یک جدول موقتی برای ذخیره رکوردهایی که قصد آرشیو آنها را داریم، ایجاد می‌کنیم. همانگونه که در تعریف جدول مشاهده می‌کنید، نام Filegroup ای که برای ساخت این جدول استفاده می‌شود، با Filegroup ای که قصد آرشیو اطلاعات آنرا داریم، یکسان است.
در ادامه می‌توان مثلاً با ایجاد یک Temporary Table به انتقال این اطلاعات بدون توجه به Filegroup آنها پرداخت.

/* Query 2-5- Switch Partitions */
USE [PartitionDB]
GO
CREATE TABLE [dbo].[Orders_Temp](
[OrderID] [int] IDENTITY(1,1) NOT NULL,
[OrderDate] [datetime] NOT NULL,
[OrderFreight] [money] NULL,
[ProductID] [int] NULL,
 CONSTRAINT [PK_OrdersTemp] PRIMARY KEY CLUSTERED ([OrderID] ASC,[OrderDate] ASC)ON FG2
) ON FG2
GO
USE [tempdb]
GO
CREATE TABLE [dbo].[Orders_Hist](
[OrderID] [int] NOT NULL,
[OrderDate] [datetime] NOT NULL,
[OrderFreight] [money] NULL,
[ProductID] [int] NULL,
 CONSTRAINT [PK_OrdersTemp] PRIMARY KEY CLUSTERED ([OrderID] ASC,[OrderDate] ASC)
)
GO
USE [PartitionDB]
GO
ALTER TABLE [dbo].[Orders] SWITCH PARTITION 1 TO [dbo].[Orders_Temp]
GO
INSERT INTO [tempdb].[dbo].[Orders_Hist]
SELECT * FROM  [dbo].[Orders_Temp]
GO
DROP TABLE [dbo].[Orders_Temp]
GO
SELECT * FROM [tempdb].[dbo].[Orders_Hist]
پس از اجرای کامل این دستورات، توزیع داده در بانک اطلاعاتی مثال مورد بررسی به شکل زیر است.


 

مطالب
روش نصب NET SDK. بر روی لینوکس Ubuntu

اگر از آخرین نگارش Ubuntu استفاده می‌کنید، با توجه به همکاری مایکروسافت و شرکت پشتیبان آن، نصب دات‌نت به سادگی اجرای دستور زیر است:

$ sudo apt update && sudo apt install -y dotnet-sdk-8.0
$ dotnet --version

و اگر می‌خواهید بدانید که چه ‌نگارشی از دات‌نت، به‌همراه مخازن استاندارد Ubuntu است، دستور زیر را صادر کنید:

$ apt search dotnet-sdk*

که یک نمونه خروجی آن به صورت زیر است:

$ apt search dotnet-sdk*
Sorting... Done
Full Text Search... Done
dotnet-sdk-8.0/noble-updates,noble-security 8.0.107-0ubuntu1~24.04.1 amd64
  .NET 8.0 Software Development Kit

dotnet-sdk-8.0-source-built-artifacts/noble-updates,noble-security 8.0.107-0ubuntu1~24.04.1 amd64
  Internal package for building the .NET 8.0 Software Development Kit

dotnet-sdk-dbg-8.0/noble-updates,noble-security 8.0.107-0ubuntu1~24.04.1 amd64
  .NET SDK debug symbols.

نصب اسکریپتی آخرین نگارش دات‌نت بر روی تمام توزیع‌های لینوکسی

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

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

sudo apt update -q && sudo apt upgrade -y && reboot

سپس دستور زیر را صادر می‌کنیم تا اسکریپت نصاب مخصوص دات‌نت خود مایکروسافت را دریافت کنیم :

wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh
chmod +x ./dotnet-install.sh

توسط این دو دستور، فایل dotnet-install.sh دریافت شده و همچنین دسترسی اجرایی بودن آن نیز تنظیم می‌شود.

پس از آن، برای نصب آخرین نگارش دات‌نت SDK موجود، تنها کافی است دستور زیر را صادر کنیم:

./dotnet-install.sh --version latest

و یا اگر فقط می‌خواهید runtime آن‌را نصب کنید، پارامترهای نصب، به صورت زیر تغییر می‌کنند:

./dotnet-install.sh --version latest --runtime aspnetcore

همچنین اگر نگارش‌های پایین‌تر مدنظر شما هستند، می‌توانید کانال نصب را هم مشخص کنید:

./dotnet-install.sh --channel 7.0

که یک نمونه خروجی اجرای دستور dotnet-install.sh --version latest/. آن به صورت زیر است:

$ ./dotnet-install.sh --version latest
dotnet-install: Attempting to download using aka.ms link https://dotnetcli.azureedge.net/dotnet/Sdk/8.0.303/dotnet-sdk-8.0.303-linux-x64.tar.gz
dotnet-install: Remote file https://dotnetcli.azureedge.net/dotnet/Sdk/8.0.303/dotnet-sdk-8.0.303-linux-x64.tar.gz size is 223236164 bytes.
dotnet-install: Extracting archive from https://dotnetcli.azureedge.net/dotnet/Sdk/8.0.303/dotnet-sdk-8.0.303-linux-x64.tar.gz
dotnet-install: Downloaded file size is 223236164 bytes.
dotnet-install: The remote and local file sizes are equal.
dotnet-install: Installed version is 8.0.303
dotnet-install: Adding to current process PATH: `/home/vahid/.dotnet`. Note: This change will be visible only when sourcing script.
dotnet-install: Note that the script does not resolve dependencies during installation.
dotnet-install: To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the "Dependencies" section.
dotnet-install: Installation finished successfully.

همانطور که مشاهده می‌کنید، دات‌نت 8.0.303، نصب شده‌است.

پس از پایان نصب، دو دستور زیر را هم باید اجرا کنید تا بتوان در خط فرمان به NET CLI. دسترسی یافت:

$ export DOTNET_ROOT=$HOME/.dotnet
$ export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools

پس از این تنظیمات، امکان اجرای دو دستور آزمایشی زیر میسر می‌شوند:

$ dotnet --version
$ dotnet --list-sdks

نصب دات‌نت بر روی نگارش‌های قدیمی‌تر Ubuntu

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

ابتدا تمام وابستگی‌های احتمالی موجود را حذف می‌کنیم:

$ sudo apt remove 'dotnet*' 'aspnet*' 'netstandard*'

سپس فایل زیر را ایجاد کرده:

sudo nano touch /etc/apt/preferences

و آن‌را با محتوای زیر تکمیل می‌کنیم؛ تا فقط از مخازن خود مایکروسافت استفاده شود و سایر مخازن مرتبط در این حالت، اولویت کمتری داشته باشند:

Package: dotnet* aspnet* netstandard*
Pin: origin "archive.ubuntu.com"
Pin-Priority: -10

Package: dotnet* aspnet* netstandard*
Pin: origin "security.ubuntu.com"
Pin-Priority: -10

پس از آن، دستورات زیر، کار افزودن مخازن بسته‌های مایکروسافت را انجام می‌دهند:

# Get OS version info which adds the $ID and $VERSION_ID variables
source /etc/os-release

# Download Microsoft signing key and repository
wget https://packages.microsoft.com/config/$ID/$VERSION_ID/packages-microsoft-prod.deb -O packages-microsoft-prod.deb

# Install Microsoft signing key and repository
sudo dpkg -i packages-microsoft-prod.deb

# Clean up
rm packages-microsoft-prod.deb

# Update packages
sudo apt update
sudo apt upgrade -y

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

sudo apt-get install -y dotnet-sdk-8.0

و همچنین هربار هم که سیستم را با دستورات sudo apt update -q && sudo apt upgrade -y به روز کنیم، در صورت وجود به‌روز رسانی دات‌نتی جدیدی، آن‌را به صورت خودکار دریافت و نصب می‌کند.

نظرات اشتراک‌ها
معرفی کتابخانه‌ی DNTCaptcha.Core
- الان با فایرفاکس 64 آزمایش کردم و مشکلی نبود. اگر اخیرا دچار این مشکل شدید، شاید فایروالی اطلاعات آن‌را حذف می‌کند. شاید افزونه‌ای در محتوای صفحه تغییر ایجاد می‌کند (فایرفاکس را در حالت safe-mode بدون افزونه‌ها هم می‌توان اجرا کرد؛ از منوی help گزینه‌ی restart with add-ons disabled).
+ در آخرین نگارش آن بجای کوکی، از سشن و یا کش هم می‌توانید استفاده کنید.
اشتراک‌ها
بررسی نکات مخفی در NET Core 3.

You've likely heard about the headline features in .NET Core 3.0 including Blazor, gRPC, and Windows desktop app support, but what else is there? This is a big release so come and see David Fowler and Damian Edwards from the .NET Core team showcase their favorite new features you probably haven't heard about in this demo-packed session.


بررسی نکات مخفی در NET Core 3.