مطالب
برنامه نویسی پیشرفته JavaScript - قسمت 2 - ماهیت انواع داده‌ای و حوزه دسترسی به متغیرها

مقادیر پایه (Primitive Values) و ارجاعی (Reference Values)

در جاوا اسکریپت، متغیرها شامل داده‌هایی از نوع پایه و یا ارجاعی می‌باشند. مقادیر String ، Number ، Boolean ، Null و Undefined به عنوان مقادیر پایه محسوب می‌گردند. در این نوع متغیرها تغییرات، مستقیما بر روی مقدار ذخیره شده در متغیر اعمال می‌شوند. اشیاء نیز که از مجموعه‌ای از مقادیر پایه ساخته شده‌اند، مقادیر ارجاعی نامیده می‌شوند. در این نوع مقادیر، شما به اشاره‌گری از شیء دسترسی دارید. بنابراین تغییرات مستقیما بر روی داده‌های ذخیره شده اعمال نمی‌شوند. به مثال‌های زیر توجه کنید:

var num1 = 10;
var num2 = num1;
alert("Num1=" + num1 + ", Num2=" + num2);

num2 = 20;
alert("Num1=" + num1 + ", Num2=" + num2);

num1 = 30;
alert("Num1=" + num1 + ", Num2=" + num2);

خروجی :

"Num1=10, Num2=10"

"Num1=10, Num2=20"

"Num1=30, Num2=20"


همانطور که از خروجی مثال فوق پیداست، در انتساب num1 به num2 ، مقدار num1 در num2 کپی شده‌است. بنابراین تغییراتی که بر روی num1 یا num2 صورت می‌گیرد، مستقیما بر روی مقدار ذخیره شده در هر یک از این متغیرها تاثیر می‌گذارد. رفتار مقادیر پایه همیشه به همین صورت می‌باشد.

var obj1 = new Object();
obj1.num = 10;
var obj2 = obj1;
alert("Obj1.Num=" + obj1.num + ", Obj2.Num=" + obj2.num);

obj2.num = 20;
alert("Obj1.Num=" + obj1.num + ", Obj2.Num=" + obj2.num);

obj1.num = 30;
alert("Obj1.Num=" + obj1.num + ", Obj2.Num=" + obj2.num);

خروجی :

"Obj1.Num=10, Obj2.Num=10"

"Obj1.Num=20, Obj2.Num=20"

"Obj1.Num=30, Obj2.Num=30"


با استفاده از نوع ارجاعی Object می‌توانیم اشیاء جدیدی را ایجاد کنیم و ویژگی‌هایی را به صورت پویا به آن‌ها اختصاص دهیم. همانطور که قبلا گفته شد، اشیاء از نوع ارجاعی می‌باشند و حاوی اشاره‌گری به مقادیر ذخیره شده می‌باشند. بنابراین انتساب obj1 به obj2 به معنای انتساب اشاره‌گر obj1 به obj2 می‌باشد. به عبارتی دیگر obj2 به همانجایی اشاره می‌کند که obj1 نیز اشاره می‌نماید. پس هر تغییری که بر روی ویژگی‌های obj1 رخ دهد، obj2 نیز تاثیرات آن را می‌بیند و بالعکس. همانطور که در خروجی مشاهده می‌نمایید، در مرحله‌ی اول obj1 به obj2 نسبت داده شد، پس مقدار ویژگی num برای هر دو آنها یکسان میباشد. در مرحله‌ی دوم، مقدار ویژگی num را در obj2 تغییر دادیم؛ ولی مقدار این ویژگی، در obj1 نیز تغییر نمود. در مرحله‌ی سوم نیز همان اتفاقات مرحله‌ی دوم تکرار شد.

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

توجه:

به انواع پایه، انواع داده‌ای مقداری یا اولیه نیز گفته می‌شود.


فراخوانی با مقدار (Call by Value)

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

function primitive(a, b) {
a += 100;
b += 200;
alert("a=" + a + ", b=" + b);
}

var x = 300, y = 400;
primitive(x, y);
alert("x=" + x + ", y=" + y);

خروجی :

"a=400, b=600"

"x=300, y=400"


x و y دو متغیر پایه می‌باشند، بنابراین تابع فوق به صورت مقداری فراخوانی شده‌است. یعنی مقدار آرگومان‌های x و y در آرگومان‌های a و b کپی می‌شوند. پس هر تغییری که بر روی a و b رخ دهد، هیچ تاثیری بر روی x و y ندارد. همچنین با توجه به توضیحی که در مورد مقادیر پایه داده شد، تغییرات مستقیما بر روی داده‌ی ذخیره شده در متغیر اعمال میشود. بنابراین تغییراتی که در تابع فوق بر روی a و b رخ داد، مستقیما مقادیر a و b را تغییر داده‌است وهیچ ارتباطی به x و y ندارد. البته توجه داشته باشید که حتی اگر نام آرگومان‌های تابع با آرگومان‌های ارسالی یکسان بود و یا حتی اگر تابع مقداری را به عنوان خروجی بر می‌گرداند، هیچ تفاوتی را در خروجی برنامه فوق مشاهده نمی‌کردید.


فراخوانی با ارجاع (Call by Reference)

در این نوع فراخوانی، آرگومان‌ها از نوع مقادیر ارجاعی هستند. بنابراین هر تغییری که بر روی آرگومان‌ها در تابع رخ دهد، بر روی آرگومان‌های ارسالی در زمان فراخوانی تابع نیز تاثیر می‌گذارند. به مثال زیر توجه کنید:

function reference(obj) {
obj.a += 100;
obj.b += 200;
alert("obj.a=" + obj.a + ", obj.b=" + obj.b);
}

var calc = new Object();
calc.a = 300;
calc.b = 400;

reference(calc);
alert("calc.a=" + calc.a + ", calc.b=" + calc.b);

خروجی :

"obj.a=400, obj.b=600"

"calc.a=400, calc.b=600"


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


حوزه دسترسی به متغیرها (Variable Scope)

متغیر‌های محلی (Local Variables)

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

for (var i = 1; i <= 5; i++) {
var sqr = i * i;
alert(sqr);
}

alert(i);
alert(sqr);

خروجی :

1
4
9
16
25
6   // alert(i) out of for
25   // alert(sqr) out of for


متغیرهای i و sqr داخل حلقه‌ی for تعریف شده‌اند و منطقا نباید خارج از این حلقه قابل دسترسی باشند. ولی با توجه به خروجی فوق، مشاهده نمودید که متغیرهای i و sqr، نه تنها خارج از این حلقه قابل شناسایی می‌باشند، بلکه آخرین مقدار خود را نیز حفظ نموده‌اند. در جاوا اسکریپت، یک متغیر محلی زمانی مفهوم پیدا می‌کند که در داخل یک تابع تعریف شود. به مثال زیر توجه کنید:

function sqr(num) {
var sum = num * num;
return sum;
}

var n = 4;
alert(sqr(n));
alert(num); // Error: num is not defined
alert(sum); // Error: sum is not defined

خروجی :

16

Error: num is not defined

Error: sum is not defined

همانطور که مشاهده می‌کنید، متغیرهای num و sum به صورت محلی در تابع فوق تعریف شده‌اند؛ بنابراین خارج از تابع قابل دسترسی نمی‌باشند و موجب بروز خطا می‌گردند.


متغیرهای عمومی (Global Variables)

در جاوا اسکریپت، متغیرهایی که خارج از تابع تعریف می‌گردند، به شیء window نسبت داده می‌شوند و عمومی می‌باشند. به عبارتی دیگر این متغیرها به عنوان یک ویژگی از شیء window تعریف می‌شوند و در تمامی توابع و بلاک‌های کد قابل دسترسی می‌باشند. به مثال زیر توجه کنید:

var color = "Red";

function setColor() {
color = "Blue";
}

alert(color);
setColor();
alert(color);

خروجی :

"Red"

"Blue"


در مثال فوق، متغیر color به صورت عمومی تعریف شده‌است. بنابراین در کل برنامه قابل دسترسی می‌باشد. در alert اول مقدار فعلی متغیر color یعنی “Red” نمایش می‌یابد. سپس با فراخوانی تابع، مقدار این متغیر تغییر می‌کند. در alert دوم مقدار تغییر یافته‌ی متغیر color نمایش خواهد یافت. حال به مثال زیر توجه کنید:

var color = "Red";

function getColor() {
var color = "Blue";
return color;
}

alert(color);
alert(getColor());
alert(color);

خروجی :

"Red"

"Blue"

"Red"


در مثال فوق، ابتدا یک متغیر color به صورت عمومی یا Global تعریف شده است. در تابع getColor نیز یک متغیر color به صورت local یا محلی تعریف شده است. زمانی که در alert تابع getColor فراخوانی می‌شود، متغیر color مقداردهی می‌گردد. این مقداردهی برای متغیر محلی صورت گرفته است و هیچ ربطی به متغیر color که به صورت عمومی تعریف شده است ندارد.

جهت تعریف متغیر در جاوا اسکریپت، از کلمه‌ی کلیدی var استفاده می‌شود. اما تعریف متغیر در جاوا اسکریپت اجباری نمی‌باشد و می‌توان یک متغیر را مقداردهی نمود بدون آنکه تعریف شده باشد. در صورتی که متغیر با var اعلان نشود، آن متغیر به شیء window نسبت داده می‌شود و ماهیت عمومی پیدا می‌کند. به مثال زیر توجه کنید:

function sum(a, b) {
c = a + b;
}

sum(20, 30);
alert(c);

خروجی :

50


همانطور که مشاهده می‌کنید، متغیر c بدون تعریف شدن مورد استفاده قرار گرفته است. با اینکه به صورت محلی مقداردهی گردیده است، ولی چون توسط var اعلان نشده است، به شیء window نسبت داده شده و ماهیت عمومی پیدا کرده است. 

مطالب
پیاده سازی پروژه‌های React با TypeScript - قسمت دوم - تعیین نوع‌های پیشرفته‌تر props
در قسمت قبل با معرفی نوع props توسط TypeScript، مجبور به تکمیل اجباری تک تک آن‌ها شدیم؛ اما در React می‌توان props را به صورت اختیاری و یا با مقادیری پیش‌فرض نیز تعریف کرد.


روش تعیین props پیش‌فرض توسط TypeScript

اگر بخواهیم توسط روش‌های خود React، مقادیر پیش‌فرض props را تعیین کنیم، می‌توان از defaultProps به صورت زیر با تعریف یک شیء جاوا اسکریپتی از پیش مقدار دهی شده، استفاده کرد:
Head.defaultProps = {
   title: "Hello",
   isActive: true
};
اما در حالت استفاده‌ی از TypeScript و یا حتی نگارش ES6 آن (React در حالت پیش‌فرض آن)، می‌توان مقادیر پیش‌فرض props را با مقدار دهی مستقیم متغیرهای حاصل از Object Destructuring آن، تعیین کرد:
type Props = {
  title: string;
  isActive: boolean;
};

export const Head = ({ title = "Hello", isActive = true }: Props) => {
در اینجا هر متغیری که با مقداری پیش‌فرض، مقدار دهی شده باشد، اختیاری در نظر گرفته شده و اگر دارای مقدار پیش‌فرضی نباشد، باید به صورت اجباری در حین تعریف المان این کامپوننت، ذکر شود.
در این حالت انتظار داریم که در حین استفاده و تعریف المان کامپوننت Head، اگر برای مثال ویژگی isActive را ذکر نکردیم، کامپایلر TypeScript خطایی را گزارش نکند؛ که اینطور نیست:


هنوز هم در اینجا می‌توان خطای عدم تعریف خاصیت isActive را مشاهده کرد. برای رفع این مشکل، به صورت زیر عمل می‌کنیم:
type Props = {
  title: string;
  isActive?: boolean;
};

export const Head = ({ title, isActive = true }: Props) => {
در حین تعریف یک type، اگر خاصیتی با علامت ? ذکر شود، به معنای اختیاری بودن آن است. همچنین در اینجا مقدار پیش‌فرض title را هم حذف کرده‌ایم تا تعریف آن اجباری شود. بنابراین در typeها، تمام خواص اجباری هستند؛ مگر اینکه توسط ? به صورت اختیاری تعریف شوند. این مورد هم مزیتی است که در ابتدای طراحی props یک کامپوننت، باید در مورد اختیاری و یا اجباری بودن آن‌ها بیشتر فکر کرد. همچنین نیازی به استفاده از روش‌های غیراستانداردی مانند Head.defaultProps خود React نیست. ذکر مقدار پیش‌فرض متغیرهای حاصل از Object Destructuring، جزئی از جاوااسکریپت استاندارد است و یا مشخص سازی خواص اختیاری در TypeScript، فقط مختص به پروژه‌های React نیست و در همه جا به همین شکل کاربرد دارد.
اکنون با تعریف isActive?: boolean، دیگر شاهد نمایش خطایی در حین تعریف المان Head، بدون ذکر خاصیت isActive، نخواهیم بود.


تعریف انواع و اقسام نوع‌های props

تا اینجا نوع‌های ساده‌ای مانند string و boolean و همچنین نحوه‌ی تعریف اجباری و اختیاری آن‌ها را بررسی کردیم. در ادامه یک نمونه‌ی کامل‌تر را مشاهده می‌کنید:
type User = {
  name: string;
};

type Props = {
  title: string;
  isActive?: boolean;
  count: number;
  options: string[];
  status: "loading" | "loaded";
  thing: {};
  thing2: {
    name: string;
  };
  user: User;
  func: () => void;
};
- در ابتدا نوع‌های متداولی مانند number و string ذکر شده‌اند.
- سپس نحوه‌ی تعریف آرایه‌ای از رشته‌ها را مشاهده می‌کنید.
- یا می‌توان مقدار یک خاصیت را تنها به مقادیری خاص محدود کرد؛ مانند خاصیت status در اینجا و اگر در حین مقدار دهی این خاصیت، از مقدار دیگری استفاده شود، تایپ‌اسکریپت، خطایی را صادر می‌کند.
- در ادامه سه روش تعریف اشیاء تو در تو را مشاهده می‌کنید؛ خاصیت thing از نوع یک شیء خالی تعریف شده‌است (بجای آن می‌توان از نوع object هم استفاده کرد). خاصیت thing2 از نوع یک شیء که دارای خاصیت رشته‌ای name است، تعریف شده و یا بهتر است این نوع تعاریف را به یک type مستقل دیگر مانند User منتقل کرد و سپس از آن جهت تعیین نوع خاصیتی مانند user استفاده نمود.
- در اینجا حتی می‌توان یک خاصیت را که از نوع یک تابع است، تعریف کرد. در این تعریف، void نوع خروجی آن است.


روش تعریف props تابعی در TypeScript

برای بررسی روش تعریف نوع توابع ارسالی از طریق props، ابتدا کامپوننت جدید src\components\Button.tsx را ایجاد می‌کنیم. سپس آن‌را به صورت زیر تکمیل خواهیم کرد:
import React from "react";

type Props = {
  // onClick(): string;  method returns string
  // onClick(): void  method returns nothing;
  // onClick(text: string): void; method with params
  // onClick: () => void; function returns nothing
  onClick: (text: string) => void; // function with params
};

export const Button = ({ onClick }: Props) => {
  return <button onClick={() => onClick("hi")}>Click Me</button>;
};
در این کامپوننت، متغیر onClick حاصل از Object Destructuring شیء props دریافتی، یک تابع است که قرار است با کلیک بر روی دکمه‌ای که در این کامپوننت قرار دارد، پیامی را به کامپوننت والد ارسال کند.
با توجه به تعریف { onClick }، در همان لحظه، خطای any بودن نوع آن از طرف TypeScript گزارش داده می‌شود. بنابراین نوع جدید Props را ایجاد کرده و برای onClick، نوع متناسبی را تعریف می‌کنیم. در اینجا 4 روش مختلف تعریف نوع function را در TypeScript مشاهده می‌کنید؛ دو حالت آن با ذکر پرانتزها و درج امضای متد انجام شده و دو حالت دیگر به کمک arrow functions پیاده سازی شده‌اند.
برای نمونه آخرین حالت تعریف شده از روش arrow functions استفاده می‌کند که متداول‌ترین روش تعریف نوع توابع است (چون عنوان می‌کند که نوع onClick، یک تابع است و آن‌را شبیه به یک متد معمولی نمایش نمی‌دهد) که در آن در ابتدا امضای پارامترهای این تابع مشخص شده‌اند و در ادامه پس از <=، نوع خروجی این تابع تعریف شده‌است که void می‌باشد (این تابع چیزی را بر نمی‌گرداند).

در آخر، تعریف المان آن‌را به صورت زیر به فایل src\App.tsx اضافه می‌کنیم که onClick آن یک مقدار را دریافت کرده و سپس آن‌را در کنسول نمایش می‌دهد.
البته خروجی از نوع void، در اینجا بسیار معمول است؛ چون هدف از این نوع توابع بیشتر ارسال مقادیری به کامپوننت در برگیرنده‌ی آن‌ها است (مانند value در اینجا) و اگر برای مثال خروجی رشته‌ای را داشته باشند، باید در حین درج و تعریف المان آن‌ها، برای نمونه یک "return "value1 را هم در انتهای کار قرار داد که عملا استفاده‌ای ندارد و بی‌معنا است:
import { Button } from "./components/Button";
import { Head } from "./components/Head";
// ...

function App() {
  return (
    <div className="App">
      <Head title="Hello" />
      <Button
        onClick={(value) => {
          console.log(value);
        }}
      />
  // ...
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 6 - سرویس‌ها و تزریق وابستگی‌ها
پیشنیازها (الزامی)

«بررسی مفاهیم معکوس سازی وابستگی‌ها و ابزارهای مرتبط با آن»
«اصول طراحی SOLID»
«مطالعه‌ی بیشتر»


تزریق وابستگی‌ها (یا Dependency injection = DI) به معنای ارسال نمونه‌ای/وهله‌ای از وابستگی (یک سرویس) به شیء وابسته‌ی به آن (یک کلاینت) است. در فرآیند تزریق وابستگی‌ها، یک کلاس، وهله‌های کلاس‌های دیگر مورد نیاز خودش را بجای وهله سازی مستقیم، از یک تزریق کننده دریافت می‌کند. بنابراین بجای نوشتن newها در کلاس جاری، آن‌ها را به صورت وابستگی‌هایی در سازنده‌ی کلاس تعریف می‌کنیم تا توسط یک IoC Container تامین شوند. در اینجا به فریم ورک‌هایی که کار وهله سازی این وابستگی‌ها را انجام می‌دهند، IoC Container و یا DI container می‌گوییم (IoC =  inversion of control ).
چندین نوع تزریق وابستگی‌ها وجود دارند که دو حالت زیر، عمومی‌ترین آن‌ها است:
الف) تزریق در سازنده‌ی کلاس: لیست وابستگی‌های یک کلاس، به عنوان پارامترهای سازنده‌ی آن ذکر می‌شوند.
ب) تزریق در خواص یا Setter injection: کلاینت خواصی get و set را به صورت public معرفی می‌کند و سپس IoC Container با وهله سازی آن‌ها، وابستگی‌های مورد نیاز را تامین خواهد کرد.


تزریق وابستگی‌ها در ASP.NET Core

برخلاف نگارش‌های قبلی ASP.NET، این نگارش جدید از ابتدا با دید پشتیبانی کامل از DI طراحی شده‌است و این مفهوم، در سراسر اجزای آن به صورت یکپارچه‌ای پشتیبانی می‌شود. همچنین به همراه یک minimalistic DI container توکار نیز هست .
این IoC Container توکار از 4 حالت طول عمر ذیل پشتیبانی می‌کند:
- instance: در هربار نیاز به یک وابستگی خاص، تنها یک وهله از آن در اختیار مصرف کننده قرار می‌گیرد و در اینجا شما هستید که مسئول تعریف نحوه‌ی وهله سازی این شیء خواهید بود (برای بار اول).
- transient: هربار که نیاز به وابستگی خاصی بود، یک وهله‌ی جدید از آن توسط IoC Container تولید و ارائه می‌شود.
- singleton: در این حالت تنها یک وهله از وابستگی درخواست شده در طول عمر برنامه تامین می‌شود.
- scoped: در طول عمر یک scope خاص، تنها یک وهله از وابستگی درخواست شده، در اختیار مصرف کننده‌ها قرار می‌گیرد. برای مثال مرسوم است که به ازای یک درخواست وب، تنها یک وهله از شیء‌ایی خاص در اختیار تمام مصرف کننده‌های آن قرار گیرد (single instance per web request).

طول عمر singleton، برای سرویس‌ها و کلاس‌های config مناسب هستند. به این ترتیب به کارآیی بالاتری خواهیم رسید و دیگر نیازی نخواهد بود تا هر بار این اطلاعات خوانده شوند. حالت scoped برای وهله سازی الگوی واحد کار و پیاده سازی تراکنش‌ها مناسب است. برای مثال در طی یک درخواست وب، یک تراکنش باید صورت گیرد.
حالت scoped در حقیقت نوع خاصی از حالت transient است. در حالت transient صرفنظر از هر حالتی، هربار که وابستگی ویژه‌ای درخواست شود، یک وهله‌ی جدید از آن تولید خواهد شد. اما در حالت scoped فقط یک وهله‌ی از وابستگی مورد نظر، در بین تمام اشیاء وابسته‌ی به آن، در طول عمر آن scope تولید می‌شود.
بنابراین در برنامه‌های وب دو نوع singleton برای معرفی کلاس‌های config و نوع scoped برای پیاده سازی تراکنش‌ها و همچنین بالابردن کارآیی برنامه در طی یک درخواست وب (با عدم وهله سازی بیش از اندازه‌ی از کلاس‌های مختلف مورد نیاز)، بیشتر از همه به کار برده می‌شوند.


یک مثال کاربردی: بررسی نحوه‌ی تزریق یک سرویس سفارشی به کمک IoC Container توکار ASP.NET Core


مثال جاری که بر اساس ASP.NET Core Web Application و با قالب خالی آن ایجاد شده‌است، دارای نام فرضی Core1RtmEmptyTest است. در همین پروژه بر روی پوشه‌ی src، کلیک راست کرده و گزینه‌ی Add new project را انتخاب کنید و سپس یک پروژه‌ی جدید از نوع NET Core -> Class library. را به آن، با نام Core1RtmEmptyTest.Services اضافه کنید (تصویر فوق).
در ادامه کلاس نمونه‌ی سرویس پیام‌ها را به همراه اینترفیس آن، با محتوای زیر به آن اضافه کنید:
namespace Core1RtmEmptyTest.Services
{
    public interface IMessagesService
    {
        string GetSiteName();
    }
 
    public class MessagesService : IMessagesService
    {
        public string GetSiteName()
        {
            return "DNT";
        }
    }
}
در ادامه به پروژه‌ی Core1RtmEmptyTest مراجعه کرده و بر روی گره references آن کلیک راست کنید. در اینجا گزینه‌ی add reference را انتخاب کرده و سپس Core1RtmEmptyTest.Services را انتخاب کنید، تا اسمبلی آن‌را بتوان در پروژه‌ی جاری استفاده کرد.


انجام اینکار معادل است با افزودن یک سطر ذیل به فایل project.json پروژه:
{
    "dependencies": {
        // same as before
        "Core1RtmEmptyTest.Services": "1.0.0-*"
    },
در ادامه قصد داریم این سرویس را به متد Configure کلاس Startup تزریق کرده و سپس خروجی رشته‌ای آن‌را توسط میان افزار Run آن نمایش دهیم. برای این منظور فایل Startup.cs را گشوده و امضای متد Configure را به نحو ذیل تغییر دهید:
public void Configure(
    IApplicationBuilder app,
    IHostingEnvironment env,
    IMessagesService messagesService)
همانطور که در قسمت قبل نیز عنوان شد، متد Configure دارای امضای ثابتی نیست و هر تعداد سرویسی را که نیاز است، می‌توان در اینجا اضافه کرد. یک سری از سرویس‌ها مانند IApplicationBuilder و IHostingEnvironment پیشتر توسط IoC Container توکار ASP.NET Core معرفی و ثبت شده‌اند. به همین جهت، همینقدر که در اینجا ذکر شوند، کار می‌کنند و نیازی به تنظیمات اضافه‌تری ندارند. اما سرویس IMessagesService ما هنوز به این IoC Container معرفی نشده‌است. بنابراین نمی‌داند که چگونه باید این اینترفیس را وهله سازی کند.
public void Configure(
    IApplicationBuilder app,
    IHostingEnvironment env,
    IMessagesService messagesService)
{ 
    app.Run(async context =>
    {
        var siteName = messagesService.GetSiteName();
        await context.Response.WriteAsync($"Hello {siteName}");
    });
}
در این حالت اگر برنامه را اجرا کنیم، به این خطا برخواهیم خورد:
 System.InvalidOperationException
No service for type 'Core1RtmEmptyTest.Services.IMessagesService' has been registered.
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.AspNetCore.Hosting.Internal.ConfigureBuilder.Invoke(object instance, IApplicationBuilder builder)

System.Exception
Could not resolve a service of type 'Core1RtmEmptyTest.Services.IMessagesService' for the parameter 'messagesService' of method 'Configure' on type 'Core1RtmEmptyTest.Startup'.
at Microsoft.AspNetCore.Hosting.Internal.ConfigureBuilder.Invoke(object instance, IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
برای رفع این مشکل، به متد ConfigureServices کلاس Startup مراجعه کرده و سیم کشی‌های مرتبط را انجام می‌دهیم. در اینجا باید اعلام کنیم که «هر زمانیکه به IMessagesService رسیدی، یک وهله‌ی جدید (transient) از کلاس MessagesService را به صورت خودکار تولید کن و سپس در اختیار مصرف کننده قرار بده»:
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<IMessagesService, MessagesService>();
    }
در اینجا نحوه‌ی ثبت یک سرویس را در IoC Containser توکار ASP.NET Core ملاحظه می‌کنید. تمام حالت‌های طول عمری که در ابتدای بحث عنوان شدند، یک متد ویژه‌ی خاص خود را در اینجا دارند. برای مثال حالت transient دارای متد ویژه‌ی AddTransient است و همینطور برای سایر حالت‌ها. این متدها به صورت جنریک تعریف شده‌اند و آرگومان اول آن‌ها، اینترفیس سرویس و آرگومان دوم، پیاده سازی آن‌ها است (سیم کشی اینترفیس، به کلاس پیاده سازی کننده‌ی آن).

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


و به این معنا است که اکنون IoC Cotanier توکار ASP.NET Core، می‌داند زمانیکه به IMessagesService رسید، چگونه باید آن‌را وهله سازی کند.


چه سرویس‌هایی به صورت پیش فرض در IoC Container توکار ASP.NET Core ثبت شده‌اند؟

در ابتدای متد ConfigureServices یک break point را قرار داده و برنامه را در حالت دیباگ اجرا کنید:


همانطور که ملاحظه می‌کنید، به صورت پیش فرض 16 سرویس در اینجا ثبت شده‌اند که تاکنون با دو مورد از آن‌ها کار کرده‌ایم.


امکان تزریق وابستگی‌ها در همه جا!

در مثال فوق، سرویس سفارشی خود را در متد Configure کلاس آغازین برنامه تزریق کردیم. نکته‌ی مهم اینجا است که برخلاف نگارش‌های قبلی ASP.NET MVC (یعنی بدون نیاز به تنظیمات خاصی برای قسمت‌های مختلف برنامه)، می‌توان این تزریق‌ها را در کنترلرها، در میان افزارها، در فیلترها در ... همه جا و تمام اجزای ASP.NET Core 1.0 انجام داد و دیگر اینبار نیازی نیست تا نکته‌ی ویژه‌ی نحوه‌ی تزریق وابستگی‌ها در فیلترها یا کنترلرهای ASP.NET MVC را یافته و سپس اعمال کنید. تمام این‌ها از روز اول کار می‌کنند. همینقدر که کار ثبت سرویس خود را در متد ConfigureServices انجام دادید، این سرویس در سراسر اکوسیستم ASP.NET Core، قابل دسترسی است.


نیاز به تعویض IoC Container توکار ASP.NET Core

قابلیت تزریق وابستگی‌های توکار ASP.NET Core صرفا جهت برآورده کردن نیازمندی‌های اصلی آن طراحی شده‌است و نه بیشتر. بنابراین توسط آن قابلیت‌های پیشرفته‌ای را که سایر IoC Containers ارائه می‌دهند، نخواهید یافت. برای مثال تعویض امکانات تزریق وابستگی‌های توکار ASP.NET Core با StructureMap این مزایا را به همراه خواهد داشت:
 • امکان ایجاد child/nested containers (پشتیبانی از سناریوهای چند مستاجری)
 • پشتیبانی از Setter Injection
 • امکان انتخاب سازنده‌ای خاص (اگر چندین سازنده تعریف شده باشند)
 • سیم کشی خودکار یا Conventional "Auto" Registration (برای مثال اتصال اینترفیس IName به کلاس Name به صورت خودکار و کاهش تعداد تعاریف ابتدای برنامه)
 • پشتیبانی توکار از Lazy و Func
 • امکان وهله سازی از نوع‌های concrete (یا همان کلاس‌های معمولی)
 • پشتیبانی از مفاهیمی مانند Interception و AOP
 • امکان اسکن اسمبلی‌های مختلف جهت یافتن اینترفیس‌ها و اتصال خودکار آن‌ها (طراحی‌های افزونه پذیر)


روش تعویض IoC Container توکار ASP.NET Core با StructureMap

جزئیات این جایگزین کردن را در مطلب «جایگزین کردن StructureMap با سیستم توکار تزریق وابستگی‌ها در ASP.NET Core 1.0» می‌توانید مطالعه کنید.
یا می‌توانید از روش فوق استفاده کنید و یا اکنون قسمتی از پروژه‌ی رسمی استراکچرمپ در آدرس https://github.com/structuremap/structuremap.dnx جهت کار با NET Core. طراحی شده‌است. برای کار با آن نیاز است این مراحل طی شوند:
الف) دریافت بسته‌ی نیوگت StructureMap.Dnx
برای این منظور بر روی گره references کلیک راست کرده و گزینه‌ی manage nuget packages را انتخاب کنید. سپس در برگه‌ی browse آن، StructureMap.Dnx را جستجو کرده و نصب نمائید (تیک مربوط به انتخاب pre releases هم باید انتخاب شده باشد):


انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
{
    "dependencies": {
        // same as before  
        "StructureMap.Dnx": "0.5.1-rc2-final"
    },
ب) جایگزین کردن Container استراکچرمپ با Container توکار ASP.NET Core
پس از نصب بسته‌ی StructureMap.Dnx، به کلاس آغازین برنامه مراجعه کرده و این تغییرات را اعمال کنید:
public class Startup
{
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddDirectoryBrowser();
 
        var container = new Container();
        container.Configure(config =>
        {
            config.Scan(_ =>
            {
                _.AssemblyContainingType<IMessagesService>();
                _.WithDefaultConventions();
            });
            //config.For<IMessagesService>().Use<MessagesService>();
 
            config.Populate(services);
        });
        container.Populate(services);
 
        return container.GetInstance<IServiceProvider>();
    }
در اینجا ابتدا خروجی متد ConfigureServices، به IServiceProvider تغییر کرده‌است تا استراکچرمپ این تامین کننده‌ی سرویس‌ها را ارائه دهد. سپس Container مربوط به استراکچرمپ، وهله سازی شده و همانند روال متداول آن، یک سرویس و کلاس پیاده سازی کننده‌ی آن معرفی شده‌اند (و یا هر تنظیم دیگری را که لازم بود باید در اینجا اضافه کنید). در پایان کار متد Configure آن و پس از این متد، نیاز است متدهای Populate فراخوانی شوند (اولی تعاریف را اضافه می‌کند و دومی کار تنظیمات را نهایی خواهد کرد).
سپس وهله‌ای از IServiceProvider، توسط استراکچرمپ تامین شده و بازگشت داده می‌شود تا بجای IoC Container توکار ASP.NET Core استفاده شود.
در این مثال چون در متد Scan، کار بررسی اسمبلی لایه سرویس برنامه با قراردادهای پیش فرض استراکچرمپ انجام شده‌است، دیگر نیازی به سطر تعریف config.For نیست. در اینجا هرگاه IName ایی یافت شد، به کلاس Name متصل می‌شود (name هر نامی می‌تواند باشد).
مطالب
استخراج تصاویر داخل یک فایل pdf با استفاده از کتابخانه iTextSharp
کتابخانه iTextSharp کتابخانه‌ای بسیار قوی و معروف، جهت کار با فایل‌های pdf می‌باشد. کارهایی از قبیل ساخت و ویرایش و تبدیل فایل‌های pdf با این کتابخانه قدرتمند به راحتی محیا است.
گاهی نیاز داریم تا اطلاعاتی را از داخل یک فایل pdf استخراج کنیم. ما الان می‌خواهیم کل تصاویر موجود در یک فایل pdf را استخراج کنیم.
ابتدا باید فایل pdf را باز کنیم. با استفاده از کلاس PdfReader اینکار امکان پذیر می‌شود. سپس باید تعداد کل صفحات را خوانده و بر روی آنها تکرار کنیم و اطلاعات مورد نیاز را پردازش کنیم. 
var pdfReader = new PdfReader(pdfFile);
for (var pageNumber = 1; pageNumber <= pdfReader.NumberOfPages; pageNumber++)
  {
      var pdf = new PdfReader(pdfFile);
      var pg = pdf.GetPageN(pageNumber);
       // process page
  }
اکنون ما در حلقه به صفحات دسترسی داریم و به ازای هر صفحه لیست منابع توکار آن صفحه را دریافت میکنیم: 
var pdf = new PdfReader(pdfFile);
var pg = pdf.GetPageN(pageNumber);
var res = (PdfDictionary)PdfReader.GetPdfObject(pg.Get(PdfName.RESOURCES));
var xobj = (PdfDictionary)PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));
if (xobj == null) continue;
foreach (var name in xobj.Keys)
 {
    var obj = xobj.Get(name);
     if (obj.IsIndirect())
    {
        // process obj ...
    }
}
حالا باید آبجکتهایی که تصویر هستند را به image تبدیل کنیم .
var tg = (PdfDictionary)PdfReader.GetPdfObject(obj);
var width = tg.Get(PdfName.WIDTH).ToString();
var height = tg.Get(PdfName.HEIGHT).ToString();
ImageRenderInfo  imgRi = ImageRenderInfo.CreateForXObject(new Matrix(float.Parse(width), float.Parse(height)), (PRIndirectReference)obj, tg);
حال یک ImageRenderInfo داریم که باید در دیسک ذخیره کنیم. این کار را در یک متد مجزا انجام می‌دهیم.
private  void RenderImage(ImageRenderInfo renderInfo, string imgPath)
    {
            var image = renderInfo.GetImage();
            using (var dotnetImg = image.GetDrawingImage())
            {
                if (dotnetImg == null) return;

                using (var ms = new MemoryStream())
                {
                    dotnetImg.Save(ms, ImageFormat.Jpeg);
                    var d = new Bitmap(dotnetImg);
                    d.Save(imgPath);
               }
       }
 }
کد نهایی به صورت زیر می‌باشد:
private void ExtractImage(string pdfFile, string imgPath)
{
    var fileCounter = 0;
    var pdfReader = new PdfReader(pdfFile);
    for (var pageNumber = 1; pageNumber <= pdfReader.NumberOfPages; pageNumber++)
    {
        var pdf = new PdfReader(pdfFile);
        var pg = pdf.GetPageN(pageNumber);
        var res = (PdfDictionary)PdfReader.GetPdfObject(pg.Get(PdfName.RESOURCES));
        var xobj = (PdfDictionary)PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));
        if (xobj == null) continue;
            foreach (var name in xobj.Keys)
            {
                var obj = xobj.Get(name);
                if (obj.IsIndirect())
                {
                    var tg = (PdfDictionary)PdfReader.GetPdfObject(obj);
                    var width = tg.Get(PdfName.WIDTH).ToString();
                    var height = tg.Get(PdfName.HEIGHT).ToString();
                    var imgRi = ImageRenderInfo.CreateForXObject(new Matrix(float.Parse(width), float.Parse(height)), (PRIndirectReference)obj, tg);
                    fileCounter++;
                    RenderImage(imgRi, imgPath + fileCounter + ".jpeg");
                }
            }
    }
}
و به شکل زیر آن را فراخوانی میکنیم.
var path = @"C:\";
var fileName = "1.pdf";

var outPath = path + fileName + @"_extractedImgs\";
Directory.CreateDirectory(outPath);

ExtractImage(path + fileName, outPath);
مطالب
آپلود فایل توسط فرم‌های پویای jqGrid
پیشنیازها
Ajax.BeginForm و ارسال فایل به سرور در ASP.NET MVC
فعال سازی و پردازش صفحات پویای افزودن، ویرایش و حذف رکوردهای jqGrid در ASP.NET MVC
فرمت کردن اطلاعات نمایش داده شده به کمک jqGrid در ASP.NET MVC
استفاده ازExpressionها جهت ایجاد Strongly typed view در ASP.NET MVC


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


تغییرات فایل Layout برنامه

در اینجا دو فایل جدید ajaxfileupload.js و jquery.blockUI.js به مجموعه‌ی فایل‌های تعریف شده اضافه شده‌اند:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>

    <link href="~/Content/themes/base/jquery.ui.all.css" rel="stylesheet" />
    <link href="~/Content/jquery.jqGrid/ui.jqgrid.css" rel="stylesheet" />
    <link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <div>
        @RenderBody()
    </div>

    <script src="~/Scripts/jquery-1.7.2.min.js"></script>
    <script src="~/Scripts/jquery-ui-1.8.11.min.js"></script>
    <script src="~/Scripts/i18n/grid.locale-fa.js"></script>
    <script src="~/Scripts/jquery.jqGrid.src.js"></script>
    <script src="~/Scripts/ajaxfileupload.js"></script>
    <script src="~/Scripts/jquery.blockUI.js"></script>

    @RenderSection("Scripts", required: false)
</body>
</html>
از فایل jquery.blockUI.js برای نمایش صفحه‌ی منتظر بمانید تا فایل آپلود شود، استفاده خواهیم کرد.
 PM> Install-Package jQuery.BlockUI



نکته‌ای در مورد واکنشگرا کردن jqGrid

اگر می‌خواهید عرض jqGrid به تغییرات اندازه‌ی مرورگر پاسخ دهد، تنها کافی است تغییرات ذیل را اعمال کنید:
<div dir="rtl" id="grid1" style="width:100%;" align="center">
    <div id="rsperror"></div>
    <table id="list" cellpadding="0" cellspacing="0"></table>
    <div id="pager" style="text-align:center;"></div>
</div>


    <script type="text/javascript">
        $(document).ready(function () {

            // Responsive jqGrid
            $(window).bind('resize', function () {
                var targetContainer = "#grid1";
                var targetGrid = "#list";

                $(targetGrid).setGridWidth($(targetContainer).width() - 2, true);
            }).trigger('resize');


            $('#list').jqGrid({
                caption: "آزمایش هفتم",
                /// .....
            }).navGrid(
                /// .....
            ).jqGrid('gridResize', { minWidth: 400, minHeight: 150 });
        });
    </script>
در اینجا به تغییرات resize صفحه گوش فرا داده شده و سپس به کمک متد توکار setGridWidth، به صورت پویا اندازه‌ی عرض jqGrid تغییر خواهد کرد.
همچنین اگر می‌خواهید کاربر بتواند اندازه‌ی گرید را دستی تغییر دهد، به انتهای تعاریف گرید، تعریف متد gridResize را نیز اضافه کنید.




نحوه‌ی تعریف ستونی که قرار است فایل آپلود کند

                colModel: [
                    {
                        name: '@(StronglyTyped.PropertyName<Product>(x=>x.ImageName))',
                        index: '@(StronglyTyped.PropertyName<Product>(x => x.ImageName))',
                        align: 'center', width: 220,
                        editable: true,
                        edittype: 'file',
                        formatter: function (cellvalue, options, rowObject) {
                            return "<img src='/images/" + cellvalue + "?rnd=" + new Date().getTime() + "' />";
                        },
                        unformat: function (cellvalue, options, cell) {
                            return $('img', cell).attr('src').replace('/images/', '');
                        }
                    }
                ],
edittype ستونی که قرار است فایل آپلود کند، باید به file تنظیم شود. همچنین چون در اینجا این فایل آپلودی، تصویر یک محصول است، از formatter برای تبدیل مسیر فایل به تصویر و از unformat برای بازگشت این مسیر به مقدر اصلی آن استفاده خواهیم کرد. از unformat برای حالت ویرایش اطلاعات استفاده می‌شود. از formatter برای تغییر اطلاعات دریافتی از سرور به فرمت دلخواهی در سمت کلاینت می‌توان کمک گرفت.
Rnd اضافه شده به انتهای آدرس تصویر، جهت جلوگیری از کش شدن آن تعریف شده‌است.



کتابخانه‌ی JqGridHelper

در قسمت‌های قبل مطالب بررسی jqGrid یک سری کلاس مانند JqGridData برای بازگشت اطلاعات مخصوص jqGrid و یا JqGridRequest برای دریافت پارامترهای ارسالی توسط آن به سرور، تهیه کردیم؛ به همراه کلاس‌هایی مانند جستجو و مرتب سازی پویای اطلاعات.
اگر این کلاس‌ها را از پروژه‌ها و مثال‌های ارائه شده خارج کنیم، می‌توان به کتابخانه‌ی JqGridHelper رسید که فایل‌های آن در پروژه‌ی پیوست موجود هستند.
همچنین در این پروژه، کلاسی به نام StronglyTyped با متد PropertyName جهت دریافت نام رشته‌ای یک خاصیت تعریف شده‌است. گاهی از اوقات این تنها چیزی است که کدهای سمت کلاینت، جهت سازگار شدن با Refactoring و Strongly typed تعریف شدن نیاز دارند و نه ... محصور کننده‌هایی طویل و عریض که هیچگاه نمی‌توانند تمام قابلیت‌های یک کتابخانه‌ی غنی جاوا اسکریپتی را به همراه داشته باشند.
با کمی جستجو، برای jqGrid نیز می‌توانید از این دست محصور کننده‌هارا پیدا کنید اما ... هیچکدام کامل نیستند و دست آخر مجبور خواهید شد در بسیاری از موارد مستقیما JavaScript نویسی کنید.



یکپارچه سازی افزونه‌ی AjaxFileUpload با فرم‌های jqGrid

پس از این مقدمات، ستون ویژه‌ی actions که inline edit را فعال می‌کند، چنین تعریفی را پیدا خواهد کرد:
                colModel: [
                    {
                        name: 'myac', width: 80, fixed: true, sortable: false,
                        resize: false, formatter: 'actions',
                        formatoptions: {
                            keys: true,
                            afterSave: function (rowid, response) {
                                doInlineUpload(response, rowid);
                            },
                            delbutton: true,
                            delOptions: {
                                url: "@Url.Action("DeleteProduct","Home")"
                            }
                        }
                    }
                ],
در اینجا afterSave اضافه شده‌است تا کار ارسال فایل به سرور را در حالت ویرایش inline فعال کند.
و ویژگی‌های قسمت‌های edit، add و delete فرم‌های پویای jqGrid باید به نحو ذیل تغییر کنند:
            $('#list').jqGrid({
                caption: "آزمایش هفتم",
                // ....
            }).navGrid(
                '#pager',
                //enabling buttons
                { add: true, del: true, edit: true, search: false },
                //edit option
                {
                    width: 'auto',
                    reloadAfterSubmit: true, checkOnUpdate: true, checkOnSubmit: true,
                    beforeShowForm: function (form) {
                        centerDialog(form, $('#list'));
                    },
                    afterSubmit: doFormUpload,
                    closeAfterEdit: true
                },
                //add options
                {
                    width: 'auto', url: '@Url.Action("AddProduct","Home")',
                    reloadAfterSubmit: true, checkOnUpdate: true, checkOnSubmit: true,
                    beforeShowForm: function (form) {
                        centerDialog(form, $('#list'));
                    },
                    afterSubmit: doFormUpload,
                    closeAfterAdd: true
                },
                //delete options
                {
                    url: '@Url.Action("DeleteProduct","Home")',
                    reloadAfterSubmit: true
                }).jqGrid('gridResize', { minWidth: 400, minHeight: 150 });
با اکثر این تنظیمات در مطلب «فعال سازی و پردازش صفحات پویای افزودن، ویرایش و حذف رکوردهای jqGrid در ASP.NET MVC» آشنا شده‌اید. تنها قسمت جدید آن شامل رویدادگردان afterSubmit است. در اینجا است که افزونه‌ی AjaxFileUpload فعال شده و سپس اطلاعات المان فایل را به سرور ارسال می‌کند.
افزونه‌ی AjaxFileUpload پس از ارسال اطلاعات عناصر غیر فایلی فرم، باید فعال شود. به همین جهت است که از رویداد afterSubmit در حالت نمایش فرم‌های پویا و رویداد afterSave در حالت ویرایش inline استفاده کرده‌ایم.
در ادامه تعاریف متدهای doInlineUpload و doUpload بکار گرفته شده در رویداد afterSubmit را مشاهده می‌کنید:
        function doInlineUpload(response, rowId) {
            return doUpload(response, null, rowId);
        }

        function doFormUpload(response, postdata) {
            return doUpload(response, postdata, null);
        }

        function doUpload(response, postdata, rowId) {
            // دریافت خروجی متد ثبت اطلاعات از سرور
            // و استفاده از آی دی رکورد ثبت شده برای انتساب فایل آپلودی به آن رکورد
            var result = $.parseJSON(response.responseText);
            if (result.success === false)
                return [false, "عملیات ثبت موفقیت آمیز نبود", result.id];

            var fileElementId = '@(StronglyTyped.PropertyName<Product>(x=>x.ImageName))';
            if (rowId) {
                fileElementId = rowId + "_" + fileElementId;
            }

            var val = $("#" + fileElementId).val();
            if (val == '' || val === undefined) {
                // فایلی انتخاب نشده
                return [false, "لطفا فایلی را انتخاب کنید", result.id];
            }

            $('#grid1').block({ message: '<h4>در حال ارسال فایل به سرور</h4>' });
            $.ajaxFileUpload({
                url: "@Url.Action("UploadFiles", "Home")", // مسیری که باید فایل به آن ارسال شود
                secureuri: false,
                fileElementId: fileElementId, // آی دی المان ورودی فایل
                dataType: 'json',
                data: { id: result.id }, // اطلاعات اضافی در صورت نیاز
                complete: function () {
                    $('#grid1').unblock();
                },
                success: function (data, status) {
                    $("#list").trigger("reloadGrid");
                },
                error: function (data, status, e) {
                    alert(e);
                }
            });

            return [true, "با تشکر!", result.id];
        }
امضای رویدادگردان‌های afterSubmit و afterSave یکی نیست. به همین جهت دو متد اضافی به جای یک متد doUpload مورد استفاده قرار گرفته‌اند.
متد doUpload توسط پارامتر response، اطلاعات بازگشتی پس از ذخیره سازی متداول اطلاعات فرم را دریافت می‌کند. برای مثال ابتدا اطلاعات معمولی یک محصول در بانک اطلاعاتی ذخیره شده و سپس id آن به همراه یک خاصیت به نام success از طرف سرور بازگشت داده می‌شوند.
اگر success مساوی true بود، ادامه‌ی کار آپلود فایل انجام خواهد شد. در اینجا ابتدا بررسی می‌شود که آیا فایلی از طرف کاربر انتخاب شده‌است یا خیر؟ اگر خیر، یک پیام اعتبارسنجی سفارشی به او نمایش داده خواهد شد.
خروجی متد doUpload حتما باید به شکل یک آرایه سه عضوی باشد. عضو اول آن true و false است؛ به معنای موفقیت یا عدم موفقیت عملیات. عضو دوم پیام اعتبارسنجی سفارشی است و عضو سوم، Id ردیف.
در ادامه افزونه‌ی jQuery.BlockUI فعال می‌شود تا ارسال فایل به سرور را به کاربر گوشزد کند.
سپس فراخوانی متداول افزونه‌ی ajaxFileUpload را مشاهده می‌کنید. تنها نکته‌ی مهم آن فراخوانی متد reloadGrid در حالت success است. به این ترتیب گرید را وادار می‌کنیم تا اطلاعات ذخیره شده در سمت سرور را دریافت کرده و سپس تصویر را به نحو صحیحی نمایش دهد.



کدهای سمت سرور آپلود فایل

        [HttpPost]
        public ActionResult AddProduct(Product postData)
        {
            // ...
            return Json(new { id = postData.Id, success = true }, JsonRequestBehavior.AllowGet);
        }

        [HttpPost]
        public ActionResult EditProduct(Product postData)
        {
            // ...
            return Json(new { id = postData.Id, success = true }, JsonRequestBehavior.AllowGet);
        }


        // todo: change `imageName` according to the form's file element name
        [HttpPost]
        public ActionResult UploadFiles(HttpPostedFileBase imageName, int id)
        {
            // ....
            return Json(new { FileName = product.ImageName }, "text/html", JsonRequestBehavior.AllowGet);
        }
در اینجا تنها نکته‌ی مهم، خروجی‌های JSON این متدها هستند.
در حالت‌های Add و Edit، نیاز است id رکورد ثبت شده بازگشت داده شود. این id در سمت کلاینت توسط پارامتر response دریافت می‌شود. از آن در افزونه‌ی ارسال فایل به سرور استفاده خواهیم کرد. اگر به متد UploadFiles دقت کنید، این id را دریافت می‌کند. بنابراین می‌توان یک ربط منطقی را بین فایل ارسالی و رکورد متناظر با آن برقرار کرد.
Content type مقدار بازگشتی از متد UploadFiles حتما باید text/html باشد (افزونه‌ی ارسال فایل‌ها، اینگونه کار می‌کند).


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید:
jqGrid07.zip
اشتراک‌ها
پیشنهاد اضافه شدن Nullable reference types به C# 7
در حال حاضر reference types در زبان #C نال پذیر هستند. جهت بالا بردن میزان امنیت زبان، پیشنهاد شده‌است که حالت پیش فرض reference types به غیرنال پذیر تغییر یابد و اگر علاقمند بودید که نال پذیر شوند، همانند nullable value types فعلی مانند int? x، نوع ?T را تعریف کنید. البته این مورد یک پیشنهاد از طرف اعضای تیم سی‌شارپ است و عده‌ای با آن موافق هستند (جهت بالا بردن ضریب امنیت و کاهش null reference exceptions) و عده‌ای خیر (گیج کننده‌است و کدهای فعلی را با مشکل مواجه می‌کند؛ یا خطاهای زیادی را توسط کامپایلر گزارش خواهد کرد).
پیشنهاد اضافه شدن Nullable reference types به C# 7
نظرات مطالب
مدیریت سفارشی سطوح دسترسی کاربران در MVC
- راه حل‌های مبتنی بر سشن، از Classics ASP دهه نود به ارث رسیده‌اند. عملا با پیشرفت‌هایی که حاصل شده نیازی به بسیاری از آن‌ها نیست. مصرف حافظه بالایی دارند و همچنین با ری‌استارت شدن برنامه در سرور، تمام سشن‌ها از بین خواهند رفت. این مشکلات در Forms Authentication وجود ندارند.
- قدمت Forms Authentication به ASP.NET 1.x بر می‌گردد. می‌توانید در این مورد در سایت‌های دیگر نیز بیشتر تحقیق کنید که آیا مشکل حادی از سال 2001 تا الان گزارش شده یا خیر.
- کلید رمزنگاری این کوکی‌ها در سمت سرور قرار دارد و تنها یک راه برای دسترسی به آن‌ها هست؛ دسترسی به سرور. در این حالت عملا کل سیستم مورد حمله قرار گرفته و یک کوکی شاید اهمیت خاصی نداشته باشد.
- ضمنا طول مدت زمان معتبر بودن اطلاعات Forms Authentication و دائمی بودن و نبودن کوکی‌های آن قابل تنظیم است (بحث شده در مطلب فوق).
نظرات مطالب
طراحی یک گرید با Angular و ASP.NET Core - قسمت دوم - پیاده سازی سمت کلاینت
سلام
در صورتی که نیاز به ایجاد ستون‌های جدول توسط ngFor از روی لیست GridColumn در html  باشد چه ایده ای پیشنهاد میکنید.
(این مورد برای ایجاد امکان جابجایی و عدم نمایش ستون‌ها توسط کاربر مورد استفاده قرار میگیرد)
نظرات مطالب
روشی جهت یافتن فیلدهای استفاده شده درون Stored Procedure ، Function و View
اون افزونه رایگانی که مهندس نصیری معرفی کردن
از هر دو استفاده می‌کنه
یعنی هم متن درون view  و Function , Procedure رو جستجو می‌کنه
که اگر رمز شده باشند دیگه نتیجه ای نداره
و هم عناوین ستون‌های اون شئ رو جستجو میکنه 
مطالب
Action و Function در OData
استفاده از OData تنها به عملیات CRUD معطوف نمیشود و در عمل شما این قابلیت را دارید که متد‌های سفارشی و کاملا مجزایی را از همدیگر در سرویس‌های خود داشته باشید.
هرچند در بعضی از سناریو‌ها نیازی به استفاده‌ی بیشتر از CRUD مربوط به آن entity وجود ندارد، اما در اکثر موارد نیاز به رفتاری دارید که به راحتی با استفاده از CRUD معمولی قابلیت پیاده سازی را ندارد. در اینگونه موارد Action‌ها و Function‌ها هستند که به راحتی با استفاده از آنها، قابلیت طراحی ماژول‌های سفارشی فراهم شده است.
Actions و Functions در عمل رفتاری شبیه به هم را دارند؛ اما تفاوت‌های آنها در این است که:
1) Action‌ها برای درخواست‌های از نوع post هستند؛ اما به عکس Function‌ها از نوع get میباشند.
2) Action‌ها اثرات جانبی دارند اما Function‌ها خیر.
3) Function‌ها حتما باید خروجی داشته باشند؛ اما این الزام برای Action‌ها وجود ندارد.
4) هر دو امکان داشتن هر تعداد پارامتری را دارند (یا بدون پارامتر).

اضافه کردن Action
فرض کنید مدل زیر را در اختیار داریم
namespace ProductService.Models
{
    public class ProductRating
    {
        public int ID { get; set; }
        public int Rating { get; set; }
        public int ProductID { get; set; }
        public virtual Product Product { get; set; }  
    }
}
حال نیاز داریم که آن را به EDM اضافه نماییم. در کانفیگ OData، نوع Conventional را استفاده کرده و ابتدا Namespaceی را تعریف کرده و سپس Action خود را تعریف مینماییم؛ به صورت زیر:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");

builder.Namespace = "ProductService";
builder.EntityType<Product>()
    .Action("Rate")
    .Parameter<int>("Rating");
در اینجا یک متد اکشن را به نام Rate و پارامتری را از نوع integer به نام Rating تعریف مینماییم.

اضافه کردن متد اکشن در کنترلر
[HttpPost]
public async Task<IHttpActionResult> Rate([FromODataUri] int key, ODataActionParameters parameters)
{
    if (!ModelState.IsValid)
    {
        return BadRequest();
    }

    int rating = (int)parameters["Rating"];
    db.Ratings.Add(new ProductRating
    {
        ProductID = key,
        Rating = rating
    });

    await db.SaveChangesAsync();
    
    return StatusCode(HttpStatusCode.NoContent);
}
توجه کنید که نام متد نوشته شده، همنام اکشنی است که قبلا تعریف کرده‌ایم. کار ODataActionParameters گرفتن پارامتر‌های ارسالی از سمت کلاینت میباشد. دقت کنید که نام پارامتر را نیز تعیین کرده بودیم.
ذکر [HttpPost] برای تعیین کردن post بودن این متد است. برای فراخوانی این اکشن از سمت کلاینت، درخواستی را از نوع post، بدین شکل ارسال مینماییم:
POST http://localhost/Products(1)/ProductService.Rate HTTP/1.1
Content-Type: application/json
Content-Length: 12

{"Rating":5}
توجه داشته باشید که ProductService همان Nampespaceی است که در کانفیگ تعیین کرده بودیم.
و البته برای فراخوانی این درخواست توسط یک کلاینت C#ی، اینگونه رفتار میکنیم (در مقاله‌های آتی از C# Odata client برای فراخوانی درخواست‌ها به صورت strongly typed استفاده خواهیم نمود)
HttpClient client = new HttpClient();
var response = client.PostAsync(postUrl, new StringContent(JsonConvert.SerializeObject(new { Rating = 5 }), Encoding.UTF8, "application/json")).Result;

اضافه کردن متد Function
برای اضافه کردن متد فانکشن نیز ابتدا باید آن را در کانفیگ OData معرفی نماییم؛ به صورت زیر:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
builder.EntitySet<Supplier>("Suppliers");

builder.Namespace = "ProductService";
builder.EntityType<Product>().Collection
    .Function("MostExpensive")
    .Returns<double>();
در اینجا یک متد function را به نام MostExpensive، بدون پارامتر و با نوع بازگشتی double، تعریف نموده‌ایم.

نکته:
 برای اینکه نوع بازگشتی از نوع EntitySet باشد:
ReturnsFromEntitySet<Product>("Products")
و یا نوع بازگشتی، لیستی از EntitySet‌ها باشد:
ReturnsCollectionFromEntitySet<Product>("Products");
و یا نوع بازگشتی بطور مثال لیستی از stringها باشد:
ReturnsCollection<string>();

برای فراخوانی این متد میتوان از آدرس زیر استفاده نمود:
GET http://localhost/Products/ProductService.MostExpensive

حال فقط کافیست که متد آن را در کنترلر مربوطه پیاده سازی نماییم:
public class ProductsController : ODataController
{
    [HttpGet]
    public IHttpActionResult MostExpensive()
    {
        var product = db.Products.Max(x => x.Price);
        return Ok(product);
    }

    // Other controller methods not shown.
}
اگر مقاله‌های قبلی را دنبال کرده باشید، این قسمت برای شما آشنا خواهد بود.
نوع response بازگشتی درخواست فوق چیزی شبیه به این خواهد بود:
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
Date: Sat, 28 Jun 2016 00:44:07 GMT
Content-Length: 85

{
  "@odata.context":"http://localhost:38479/$metadata#Edm.Decimal","value":50.00
}

اضافه کردن Unbound Function
در مثال قبلی یک function bound نوشتیم که مربوط به یک EntitySet خاص بود. اما اکنون میخواهیم یک متد Unbound تعریف نماییم، به صورت زیر:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");

builder.Function("GetSalesTaxRate")
    .Returns<double>()
    .Parameter<int>("PostalCode");
توجه کنید اینجا ما متد را به صورت مستقیم از ODataModelBuilder تهیه نمود‌ه‌ایم؛ به جای Entity type یا collection مربوطه. این تنظیم به model builder میگوید که متدی unbound میباشد.
متد آن را نیز اینگونه پیاده سازی مینماییم:
[HttpGet]
[ODataRoute("GetSalesTaxRate(PostalCode={postalCode})")]
public IHttpActionResult GetSalesTaxRate([FromODataUri] int postalCode)
{
    double rate = 5.6;  // Use a fake number for the sample.
    return Ok(rate);
}
اهمیتی ندارد که این متد را در چه Controllerی پیاده سازی نمایید. ذکر [ODataRoute] نیز برای تعریف URl این function میباشد.

برای فراخوانی آن نیز از درخواست زیر استفاده مینماییم:
GET http://localhost/GetSalesTaxRate(PostalCode=10) HTTP/1.1
یک مثال از نوع response درخواست فوق
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
Date: Sat, 28 Jun 2016 01:05:32 GMT
Content-Length: 82

{
  "@odata.context":"http://localhost:38479/$metadata#Edm.Double","value":5.6
}

شاید سؤالی برایتان پیش بیاید که آیا برای تعریف هر متد، این همه مراحل کانفیگ لازم است؟! در واقع باید عرض کنم، این نوع استفاده از OData، ابتدایی‌ترین نوع طراحی آن میباشد و قطعا در یک برنامه‌ی واقعی این همه کد نویسی برای نوشتن فقط یک متد، شاید منطقی به نظر نمیرسد. از آنجاییکه این مقاله فقط جنبه‌ی آموزشی خیلی ساده از این پروتکل را دارد، فعلا به همین اندازه بسنده میکنیم. اما در مقاله‌های بعدی راه‌حل‌هایی برای بینهایت ساده کردن کانفیگ OData را شرح خواهیم داد.