مطالب
روش‌هایی برای مدیریت بهتر عملگرهای RxJS در برنامه‌های Angular
کتابخانه‌ی RxJS، جزو پایه‌ای کار با برنامه‌های Angular است و ساده‌ترین روش کار با آن، تعریف یک سطر ذیل است:
 import {Observable} from 'rxjs';
به این ترتیب تمام عملگرهای RxJS مانند map ،do ،catch و غیره نیز import خواهند شد. اما این سادگی ... به قیمت افزوده شدن یک بسته‌ی 586 KB (غیرفشرده) به فایل‌های نهایی، تمام خواهد شد.


روش‌های مختلف import ویژگی‌های کتابخانه‌ی RxJS

الف) import همه چیز به صورت یکجا
 import Rx from "rxjs/Rx";
یک مثال
 Rx.Observable.of(1, 2, 3).map(i => i.toString());
این روش بسیار ساده‌است. اما سبب import کامل یک کتابخانه‌ی 586 KB نیز می‌شود.

ب) تنها import ویژگی‌های مورد نیاز
import { Observable } from "rxjs/Observable";
import "rxjs/add/observable/of";
import "rxjs/add/operator/map";
به این روش «patching the Observable prototype» نیز گفته می‌شود.
یک مثال
 Observable.of(1, 2, 3).map(i => i.toString());
مزیت این روش، کاهش قابل ملاحظه‌ی حجم نهایی برنامه است. فقط باید دقت داشت که متدهای مورد نیاز، حتما import شده باشند.

ج) فراخوانی مستقیم متدهای RxJS نه از طریق Observable
import { Observable } from "rxjs/Observable";
import { of } from "rxjs/observable/of";
import { map } from "rxjs/operator/map";
یک مثال
 const source = of(1, 2, 3);
const mapped = map.call(source, i => i.toString());
در این حالت کد بیشتری باید نوشته شود و مزیت اطلاعات نوع متد را نیز از دست می‌دهیم. از این جهت که این متدها زمانیکه با این روش import می‌شوند، any را بازگشت می‌دهند (Function.prototype.call خروجی از نوع any دارد).


مقید کردن برنامه به عدم استفاده از حالت «الف» و اجبار به استفاده از حالت «ب»

اگر به ریشه‌ی پوشه‌ی پروژه‌های مبتنی بر Angular CLI دقت کنید، فایل tslint.json نیز در آن‌ها قابل مشاهده است و اگر افزونه‌ی VSCode آن‌را نیز نصب کرده باشید، در حین کار با VSCode، خطاهای مرتبط را درون ادیتور مشاهده خواهید کرد. TSLint، قابلیت توسعه داشته و یک نمونه‌ی از این‌ها، بسته‌ی TSLint rules for RxJS است. برای نصب آن ابتدا دستور ذیل را صادر کنید:
 npm install rxjs-tslint-rules --save-dev
سپس فایل tslint.json را گشوده و تغییرات ذیل را به آن اعمال نمائید:
{
  "rulesDirectory": [
    "node_modules/codelyzer"
  ],
  "extends": [
    "rxjs-tslint-rules"
  ],  
  "rules": {
    "rxjs-add": { "severity": "error" },
    "rxjs-no-patched": { "severity": "error" },
    "rxjs-no-unused-add": { "severity": "error" },
    "rxjs-no-wholesale": { "severity": "error" },
    "rxjs-no-subject-unsubscribe": { "severity": "error" },
rxjs-no-wholesale آن سبب منع درج حالت «الف» می‌شود و سایر حالات روش «ب» را تشویق می‌کنند.



مدیریت بهتر حالت «ب» یا «تنها import ویژگی‌های مورد نیاز»

زمانیکه از روش «ب» استفاده می‌کنیم، کلاس‌های سرویس برنامه پر خواهند شد از کدهای تکراری ذیل:
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
در اینجا هر متدی را که نیاز است باید یکبار import کرد و این‌کار را باید به ازای تک‌تک سرویس‌های برنامه نیز تکرار نمود.
برای مدیریت بهتر اینکار، فایل جدیدی را به نام src\app\shared\rxjs-operators.ts ایجاد می‌کنیم؛ با محتوای ذیل:
// define the rxjs operators needed by your app
// see node_module/rxjs/Rx.js for more

// statics
import "rxjs/add/observable/from";
import "rxjs/add/observable/throw";

// operators
import "rxjs/add/operator/catch";
import "rxjs/add/operator/combineLatest";
import "rxjs/add/operator/debounceTime";
import "rxjs/add/operator/delay";
import "rxjs/add/operator/distinctUntilChanged";
import "rxjs/add/operator/do";
import "rxjs/add/operator/filter";
import "rxjs/add/operator/finally";
import "rxjs/add/operator/first";
import "rxjs/add/operator/ignoreElements";
import "rxjs/add/operator/let";
import "rxjs/add/operator/map";
import "rxjs/add/operator/mapTo";
import "rxjs/add/operator/mergeMap";
import "rxjs/add/operator/startWith";
import "rxjs/add/operator/switchMap";
import "rxjs/add/operator/takeUntil";
import "rxjs/add/operator/withLatestFrom";
import "rxjs/add/operator/takeUntil";
import "rxjs/add/operator/take";
در اینجا تمام importهای مورد نیاز برنامه به صورت یکجا درج می‌شوند.
سپس کافی است به فایل src\app\app.module.ts مراجعه کرده و این فایل را import کنیم:
 // import RxJs needed operators only once
import "./shared/rxjs-operators";

//...
@NgModule({
//...
با اینکار سبب خواهیم شد تا دیگر در سرویس‌های برنامه (و تمام قسمت‌های آن) نیازی به تعریف‌های تکراری RxJS وجود نداشته باشد و تنها تعریفی که در آنجا نیاز است، یک مورد ذیل است:
 import { Observable } from "rxjs/Observable";
علت اینجا است، همانطور که عنوان شد، به این روش «patching the Observable prototype» نیز گفته می‌شود. به این معنا که این عملگرها و یا افزونه‌ها، جزئی از کلاس Observable می‌شوند و در تمام قسمت‌های برنامه، پس از import آن‌ها قابل دسترسی خواهند بود. بنابراین با یکبار import فایل rxjs-operators.ts، دیگر نیازی به تعریف مجدد آن نخواهد بود.
مطالب
ذخیره و بازیابی فایل در Mongodb (بخش سوم)
در قسمت‌های پیشین (^ ،^ ) در مورد عملیات CRUD در سطح دیتابیس و به طور کلی در مورد ایندکس گذاری صحبت کردیم. در این بخش قصد داریم یکی از موارد بسیار مهم، یعنی ذخیره‌ی فایل‌های باینری را در دیتابیس، مورد بررسی قرار دهیم. روش‌های مختلفی برای اینکار وجود دارند؛ ولی بعضی از این روش‌ها در حال حاضر منسوخ شده اعلام شده‌اند که در اینجا ما آخرین روش را که در حال حاضر هیچ ویژگی منسوخ شده‌ای ندارد، به کار می‌گیریم.
از آنجاکه نهایت اندازه‌ی یک سند BSON نمی‌تواند بیشتر از 16 مگابایت باشد، قابلیتی به نام GridFS ایجاد شده‌است تا بتوان فایل‌های باینری را در آن ذخیره کرد. GridFS شامل دو بخش مختلف برای ذخیره اطلاعات یک فایل باینری است:
- fs.chunks که برای ذخیره اطلاعات قطعه‌های یک فایل باینری به کار میرود.
- fs.files که برای ذخیره اطلاعات و متادیتاها به کار می‌رود.

قبل از هر چیزی باید بدانید که کتابخانه مربوط به GridFs در یک پکیج جداگانه عرضه شده است و باید آن از طریق nuget نصب کنیم:
install-package MongoDB.Driver.GridFS

برای آپلود یک فایل باینری به داخل سیستم از کد زیر استفاده میکنیم:
var client = new MongoClient();
var db = client.GetDatabase("publisher");
IGridFSBucket bucket = new GridFSBucket(db);
ابتدا یک شیء GridFsBucket را ایجاد میکنیم که از ما اطلاعات دیتابیس مورد نظر را برای ارسال فایل میخواهد و نتیجه‌ی آن یک کلاس از جنس اینترفیس IGridFSBucket می‌باشد. این باکت یا سطل در واقع همانند کالکشن رفتار میکند.
byte[] source=File.ReadAllBytes(@"D:\Untitled.png");
var options = new GridFSUploadOptions
            {
                ChunkSizeBytes = 64512, // 63KB
                Metadata = new BsonDocument
                {
                    { "CoverType", "Front" }, 
                    { "copyrighted", true }
                }
            };
در مرحله بعد فایل باینری را به صورت آرایه‌ای از بایت‌ها میخوانیم (البته حالت‌های مختلفی چون استریم را نیز پشتیبانی میکند). بعد از خواندن فایل، یک شیء از جنس کلاس GridFSUploadOptions را ایجاد و اطلاعات فایل آپلودی را مشخص می‌کنیم. به عنوان مثال اولین خصوصیتی که پر میکنیم خصوصیت تعیین سایز قطعات فایل باینری می‌باشد که به طور پیشفرض بر روی 64 مگابایت قرار گرفته است و عموما هم برای اکثر موارد، پاسخگوی نیاز‌ها است (در بخش بعدی مقالات، بیشتر این مورد را بررسی میکنیم).
مورد دومی که مقداردهی شده‌است، متادیتا‌ها هستند و این قابلیت را داریم که پرس و جوی خود را بر اساس آن‌ها نیز فیلتر کنیم. این خصوصیت مقدار دریافتی از جنس BsonDocument را دریافت میکند. ولی اگر شما برای فایل خود، کلاس اختصاصی برای متادیتاها در نظر گرفته‌اید میتوانید از یک Extension Method به نام ToBsonDocument استفاده کنید و شیء خود را به این نوع تبدیل کنید:
 var options = new GridFSUploadOptions
            {
                ChunkSizeBytes = 64512, // 63KB
                Metadata = metaData.ToBsonDocument()
            };


در نهایت آن را آپلود میکنیم:
 var id = bucket.UploadFromBytes("GoneWithTheWind", source, options);
پارامتر اول آن، نامی برای بسته آپلودی است. پارامتر دوم، خود فایل آپلودی میباشد و با پارمتر آخر هم تنظیماتی را که برای فایل مورد نظر توسط کلاس GridFSUploadOptions  تعیین کرده‌ایم، مشخص میکنیم. موقعیکه آپلود انجام شود، به ازای این کد، یک شناسه‌ی اختصاصی از جنس ObjectId را دریافت میکنیم که می‌توانیم آن را به یک خصوصیت در سند اصلی نسبت دهیم تا ارتباط بین سند و فایل هایش را داشته باشیم.

نکته: اگر در یک کلاس، چند فیلد از جنس ObjectId دارید، مونگو در بین تشخیص شناسه اصلی سند و شناسه تصاویر، با توجه به نام خصوصیت‌ها و غیره، تا حد زیادی هوشمند عمل میکند. ولی اگر خواستید صریحا شناسه اصلی را ذکر کنید و آن را متمایز از بقیه نشان دهید، می‌توانید از خصوصیت BsonId در بالای نام فیلد ID استفاده کنید:
[BsonId]
public ObjectId Id { get; set; }

جهت خواندن فایل آپلود شده، تنها کافی است از طریق شناسه‌ی دریافتی در مرحله‌ی آپلود، اقدام نماییم:
var bytes = bucket.DownloadAsBytes(id);

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


اگر قصد دارید بر اساس نام داده شده، فایل را دریافت کنید، ممکن است که چندین فایل، تحت یک نام ذخیره شده باشند که میتوانید در حالت‌های مختلفی این تصاویر را واکشی نمایید:
0 : فایل اصلی
1: اولین نسخه فایل
2: دومین نسخه فایل
و الی آخر...

1-: جدیدترین نسخه فایل (مقدار پیش فرض)
2-: نسخه ماقبل جدیدترین نسخه فایل
و الی آخر...


منظور از نسخه، فایل‌هایی با نامی موجود و از قبل ذخیره شده هستند که نسخه جدیدی از فایل قبلی بوده و فایل اول، فایل اصلی محسوب میشود.
برای درک بهتر مسئله، من تصاویر زیر را به ترتیب از سمت راست به سمت چپ، تحت یک نام، وارد سیستم میکنم:


اولین تصویر، تصویر اصلی محسوب می‌شود و بعد از آن، نسخه اول و نسخه دوم تصویر، وارد سیستم می‌شوند و تکه کد زیر از آنجاکه با مقدار پیش فرض پر شده‌است باید آخرین تصویر، تصویر سمت چپ را برای شما بر روی دیسک ذخیره کند:
var client = new MongoClient();
var db = client.GetDatabase("publisher");
IGridFSBucket bucket = new GridFSBucket(db);
var image=bucket.DownloadAsBytesByName("City of Glass-cover");
File.WriteAllBytes(@"D:\a.jpg",image);

برای مقداردهی خواص بالا به شکل زیر عمل میکنیم:
var client = new MongoClient();
var db = client.GetDatabase("publisher");
IGridFSBucket bucket = new GridFSBucket(db);
var options = new GridFSDownloadByNameOptions
            {
                Revision = 0
            };
var image=bucket.DownloadAsBytesByName("City of Glass-cover",options);
File.WriteAllBytes(@"D:\a.jpg",image);
با پاس کردن مقدار 0 به مونگو، اولین تصویر وارد شده، یعنی تصویر اصلی را دریافت می‌کنیم که اولین تصویر از سمت راست می‌شود. اگر مقادیر 1 یا 1- را پاس دهیم، چون تنها سه تصویر بیشتر نیست، در هر دو حالت تصویر دوم بازگردانده می‌شود.

برای بازگردانی تصاویر از طریق مقادیر موجود در متادیتا، باید از کلاس ویژه‌ای به نام GridFSFileInfo استفاده کنیم. در اینجا هم همانند روزهای اول، از کلاس بیلدر جهت ایجاد شرط استفاده میکنیم:
var client = new MongoClient();
var db = client.GetDatabase("publisher");
IGridFSBucket bucket = new GridFSBucket(db);
var filter = Builders<GridFSFileInfo>.Filter.Gte(x => x.Length , 600);
var sort = Builders<GridFSFileInfo>.Sort.Descending(x => x.UploadDateTime);
var options =new GridFSFindOptions()
            {
                Limit = 3,
                Sort = sort
            };
var cursor = bucket.Find(filter, options);
var list = cursor.ToList();
در اینجا ابتدا مشخص کرده‌ایم که فایل مورد نظر باید حجمی بیشتر از 600 بایت داشته باشد و مرتب سازی آن به صورت نزولی، بر اساس زمان آپلود باشد. در عبارت لامبدا تعریف شده، می‌توانید خصوصیت‌های مختلف یک فایل از قبیل نام، حجم (سایز) ، زمان آپلود و ... را ببینید. سپس مرتب سازی و تعداد رکورد برگشتی از ابتدای جدول را مشخص میکنیم و از متد Find یا FindAsync جهت جست‌وجو استفاده میکنیم. با شکل گرفتن کوئری درخواست، لیستی از آن را تهیه میکنیم. مقدار بازگشتی این شیء، در واقع اسنادی از تصاویر هستند که میتوانید از طریق Id یا نام آن‌ها، فایل اصلی را واکشی نمایید.
برای یافتن تصاویر بر اساس متادیتاهای تعریف شده، از کد زیر استفاده میکنیم:
var filter = Builders<GridFSFileInfo>.Filter.Eq("metadata.CoverType","Front");

تغییر نام تصاویر
جهت ویرایش یک نام فایل از طریق متدهای زیر اقدام می‌نماییم:
bucket.Rename(id, newFilename);

//یا در حالت غیرهمزمان
await bucket.RenameAsync(id, newFilename);

حذف تصویر
bucket.Delete(id);

//یا
await bucket.DeleteAsync(id);

برای حذف کل bucket از طریق کد زیر اقدام می‌نماییم:
bucket.Drop();

//یا
await bucket.DropAsync();
 
در بخش بعدی Chunk را مورد بررسی قرار می‌دهیم.
مطالب
SASS #2
در قسمت قبل، روش‌های مختلف کامپایل فایل‌های SASS را بررسی کردیم. در ادامه می‌خواهیم با syntax آن بیشتر آشنا شویم.

متغیرها (Variables)

متغیرها در SASS با استفاده از $ در ابتدای نام آن، به عنوان یک مقدار مورد استفاده‌ی در CSS تعریف می‌شوند. شما در SASS می‌توانید متغیرهایی را برای margin ،font-size و یا padding و غیره، تعریف کنید. استفاده از متغیرها این امکان را به شما می‌دهد که خیلی راحت‌تر از style‌های تعریف شده، مجدد استفاده کنید.

شما 6 نوع مختلف متغیر را می‌توانید با استفاده از SASS بکار ببرید.

  • Strings (مثال: ;"myString: "your text here$ )
  • Numbers (مثال: ;myNum: 16px$)
  • Colors (مثال: ;myColor: aqua$)
  • Booleans (مثال: ;myBool: true$)
  • Lists (مثال: ;myItemList: 1px solid red$)
  • Nulls (مثال: ;myVar: null$)

برای مثالی از استفاده‌ی از این متغیرها، یک فایل را با نام styles.scss ایجاد کرده و کدهای زیر را در آن وارد کنید:

$myColor: #FFF726;
$myBackColor: #2B14FF;
$myString: "I Love ";
$myFontSize: 13px;
$myMargin: 0px auto;
$myWidth: 300px;
 
h1 {
   color: $myColor;
   margin: 0;
   padding: 0;
}

h1:before{
  content: $myString;
}

#container {
   width: $myWidth;
   margin: $myMargin;
   background-color:$myBackColor;
   text-align:center;
}
پس از کامپایل فایل SASS و اجرای کد بالا، مرورگر شما باید خروجی را به این صورت داشته باشد:


ریاضی (Math)

برخلاف SASS ،CSS به ما امکان استفاده از عبارات ریاضی را می‌دهد. عملگرهای جمع + ، تفریق - ، تقسیم / ، ضرب * ، باقیمانده % ، مساوی == ، نامساوی =! را  SASS پشتیبانی می‌کند. در هنگام استفاده از عبارات ریاضی چند نکته وجود دارد که باید رعایت کنید:

نکته1: چون علامت / در CSS به عنوان یک کوتاه کننده استفاده می‌شود مانند font: 14px/16px، در صورتیکه بخواهید عمل تقسیم را بر روی مقدار ثابتی انجام دهید باید آنها را درون پرانتر قرار دهید.

$fontDiff: (14px/16px);
نکته2: شما نمی توانید واحدهای مختلف را با هم استفاده کنید.
$container-width: 100% - 20px;
مثال بالا کار نمی‌کند، در صورتیکه نیاز به چنین محاسبه‌ای داشتید می‌توانید از تابع calc در CSS استفاده کنید؛ چرا که نیاز به محاسبه‌ی در زمان اجرا را دارید.

حال می‌خواهیم براساس عرض container، ستون‌های پویایی را ایجاد کنیم:
$container-width: 100%;

.container {
  width: $container-width;
}

.col-4 {
  width: $container-width / 4;
}
که پس از کامپایل به صورت زیر تبدیل می‌شود:
.container {
   width: 100%;
}

.col-4 {
    width: 25%;
}

مشاهده‌ی پیاده سازی مثال بالا اینجا.

توابع (Functions)

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

برای نمونه برخی از توابع مربوط به کار با رنگ‌ها را توضیح می‌دهیم:

  • (darken ($color, $amount: این تابع برای تیره‌تر کردن یک کد رنگ می‌باشد. شما برای استفاده‌ی از این تابع باید دو مقدار را به آرگومان‌های ورودی این تابع که به ترتیب کد رنگ و میزان تیره‌تر شدن آن به صورت درصد از %0 تا %100 می‌باشند، ارسال کنید و خروجی آن کد رنگ تولید شده است. توجه داشته باشید نوع سیستم رنگی ارسال شده به عنوان پارامتر color$، خروجی این تابع نیز همان نوع می‌باشد.
darken(hsl(25, 100%, 80%), 30%) => hsl(25, 100%, 50%)
darken(#800, 20%) => #200
  • (lighten ($color, $amount: این تابع برای روشن‌تر کردن یک رنگ می‌باشد و دقیقا برعکس تابع darken عمل می‌کند.
lighten(hsl(0, 0%, 0%), 30%) => hsl(0, 0, 30)
lighten(#800, 20%) => #e00
  • (alpha ($color) / opacity($color: با استفاده از این دو تابع می‌توانید میزان شفافیت/کدری را مشخص کنید.
  • (mix ($color1, $color2, $weight:50% : با استفاده از این تابع می‌توانید دو رنگ را با هم ترکیب کنید. مقدار پیش فرض آرگومان weight$ برابر %50 می‌باشد و تعیین آن اختیاری است. محدوده‌ی پذیرش مقدار weight$ هرچه به %0 نزدیک‌تر باشد، باعث نزدیک‌تر بودن رنگ خروجی به رنگ دوم و هرچه به %100 نزدیکتر باشد رنگ خروجی به رنگ اول نزدیک‌تر می‌شود. توجه: میزان شفافیت/کدری را نیز می‌تواند تشخیص دهد.
mix(#f00, #00f) => #7f007f
mix(#f00, #00f, 25%) => #3f00bf
mix(rgba(255, 0, 0, 0.5), #00f) => rgba(63, 0, 191, 0.75)

تو در تو (Nesting)

SASS امکان تعریف استایل‌های تو در تو را به شما می‌دهد که باعث خواناتر شدن استایل‌های نوشته شده می‌شود. به عنوان مثال به کد CSS زیر توجه کنید:

#container {
    width: 500px;
    margin: 0 auto;
}

#container p {
   font-family: Arial;
   font-size: 13px;
}

#container h1 {
   font-family: Tahoma;
   font-size: 15px;
}

#container h2 {
   font-family: Helvetica;
   font-size: 14px;
}
حال نسخه‌ی SASS مثال بالا می‌شود:
$myFontsize1: 13px;
$myFontsize2: 18px;
$myFontsize3: 25px;
$myWidth: 500px;
$myMargin: 0px auto;

#container {
    width: $myWidth;
    margin: $myMargin;

    p {
        font-family: Arial;
        font-size: $myFontsize1;
    }

    h1 {
        font-family: Tahoma;
        font-size: $myFontsize3;
    }

    h2 {
        font-family: Helvetica;
        font-size: $myFontsize2;
    }
}
توجه کنید تمام element هایی که درون container# قرار دارند، بدون ذکر نام container# نوشته شده‌اند و این کار سبب کمتر شدن کدهای نوشته شده و خواناتر شدن آن می‌شود.
در صورتیکه نیاز به دسترسی به والد داشته باشید کافیست از علامت & استفاده کنید.
a.myAnchor {
    color: blue;

    &:hover {
        text-decoration: underline;
    }

    &:visited {
        color: purple;
    }
}
خوب تا اینجا شما با روش نوشتن استایل‌های به صورت تو در تو آشنا شدید. حال اگر بخواهید کد‌های نوشته شده‌ی تو در تو را به صورت غیر تو در تو کامپایل کنید، باید از کلمه‌ی کلیدی at-root@ قبل استایلی که می‌خواهید به صورت تو در تو تعریف نشود، استفاده کنید.
.first-component {
    .text { font-size: 1.4em; }
    .button { font-size: 1.7em; }

    .second-component {
        .text { font-size: 1.2em; }
        .button { font-size: 1.4em; }
    }
}
مثال بالا در حالت تو در تو، پس از کامپایل به صورت زیر تبدیل می‌شود:
.first-component .text {
  font-size: 1.4em;
}
.first-component .button {
  font-size: 1.7em;
}
.first-component .second-component .text {
  font-size: 1.2em;
}
.first-component .second-component .button {
  font-size: 1.4em;
}
حال با استفاده از at-root@ به صورت زیر می‌شود:
.first-component .text {
  font-size: 1.4em;
}
.first-component .button {
  font-size: 1.7em;
}
.second-component .text {
  font-size: 1.2em;
}
.second-component .button {
  font-size: 1.4em;
}
مطالب
افزودن هدرهای Content Security Policy به برنامه‌های ASP.NET
مرورگرهای جدید تحت زیر مجموعه‌ای به نام Content Security Policy، قابلیت‌های توکاری را اضافه کرده‌اند تا حملاتی مانند XSS را حتی در برنامه‌ی وبی که برای این نوع حملات تمهیداتی را درنظر نگرفته‌است، خنثی کنند. این قابلیت‌ها به صورت پیش فرض فعال نبوده و نیاز است برنامه نویس صراحتا درخواست فعال شدن آن‌ها را از طریق افزودن تعدادی هدر مشخص به Response، ارائه دهد. در ادامه این هدرها را بررسی خواهیم کرد.


غیرفعال کردن اجرای اسکریپت‌های inline

عمده‌ی حملات XSS زمانی قابلیت اجرا پیدا می‌کنند که مهاجم بتواند به طریقی (ورودی‌های اعتبارسنجی نشده)، اسکریپتی را به درون صفحه‌ی جاری تزریق کند. بنابراین اگر ما به مرورگر اعلام کنیم که دیگر اسکریپت‌های inline را پردازش نکن، سایت را تا حد زیادی در مقابل حملات XSS مقاوم کرده‌ایم. این قابلیت به صورت پیش فرض خاموش است؛ چون به طور قطع فعال سازی آن بسیاری از سایت‌هایی را که عادت کرده‌اند اسکریپت‌های خود را داخل صفحات وب مدفون کنند، از کار می‌اندازد. این نوع سایت‌ها باید به روز شده و اسکریپت‌ها را از طریق فایل‌های خارجی js، به سایت و صفحات خود الحاق کنند.
برای فعال سازی این قابلیت، فقط کافی است هدرهای زیر به Response اضافه شوند:
 Content-Security-Policy: script-src 'self'
X-WebKit-CSP: script-src 'self'
X-Content-Security-Policy: script-src 'self'
سطر اول به زودی تبدیل به یک استاندارد W3 خواهد شد؛ اما فعلا فقط توسط کروم 25 به بعد پشتیبانی می‌شود. سطر دوم توسط مرورگرهایی که از موتور WebKit استفاده می‌کنند، پشتیبانی می‌شود و سطر سوم مخصوص فایرفاکس است و IE 10 به بعد.
بعد از فعال شدن این قابلیت، فقط اسکریپت‌هایی که از طریق دومین شما به صفحه الحاق شده‌اند، قابلیت اجرا را خواهند یافت و کلیه اسکریپت‌های مدفون شده داخل صفحات، دیگر اجرا نخواهد شد. در این حالت اگر از CDN برای الحاق اسکریپتی استفاده می‌کنید، مثلا مانند الحاق jQuery به صفحه، نیاز است مسیر آن‌را صراحتا در این هدر ذکر کنید:
 Content-Security-Policy: script-src 'self' https://youcdn.com
X-WebKit-CSP: script-src 'self' https://yourcdn.com
X-Content-Security-Policy: script-src 'self' https://yourcdn.com
علاوه بر آن حتی می‌شود پردازش تمام منابع مورد استفاده را نیز مانند تصاویر، شیوه‌نامه‌ها، فایل‌های فلش و غیره، به دومین جاری محدود کرد:
 Content-Security-Policy: default-src 'self' https://youcdn.com
X-WebKit-CSP: default-src 'self' https://yourcdn.com
X-Content-Security-Policy: default-src 'self' https://yourcdn.com
بدیهی است پس از آشنایی با این مورد، احتمالا در پروژه‌های جدید خود از آن استفاده کنید (چون inline scriptهای فعلی شما را کاملا از کار می‌اندازد).


نحوه‌ی اضافه کردن هدرهای Content Security Policy به برنامه‌های ASP.NET

روشی که با هر دو برنامه‌های وب فرم و MVC کار می‌کند، تهیه یک HTTP module است؛ به شرح ذیل:
using System;
using System.Web;

namespace AntiXssHeaders
{
    public class SecurityHeadersConstants
    {
        public static readonly string XXssProtectionHeader = "X-XSS-Protection";
        public static readonly string XFrameOptionsHeader = "X-Frame-Options";
        public static readonly string XWebKitCspHeader = "X-WebKit-CSP";
        public static readonly string XContentSecurityPolicyHeader = "X-Content-Security-Policy";
        public static readonly string ContentSecurityPolicyHeader = "Content-Security-Policy";
        public static readonly string XContentTypeOptionsHeader = "X-Content-Type-Options";
    }

    public class ContentSecurityPolicyModule : IHttpModule
    {
        public void Dispose()
        { }

        public void Init(HttpApplication app)
        {
            app.BeginRequest += AppBeginRequest;
        }

        void AppBeginRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            var response = app.Context.Response;
            setHeaders(response);
        }

        private static void setHeaders(HttpResponse response)
        {
            response.Headers.Set(SecurityHeadersConstants.XFrameOptionsHeader, "SameOrigin");

            // For IE 8+
            response.Headers.Set(SecurityHeadersConstants.XXssProtectionHeader, "1; mode=block");
            response.Headers.Set(SecurityHeadersConstants.XContentTypeOptionsHeader, "nosniff");

            //todo: Add /Home/Report --> public JsonResult Report() { return Json(true); }

            const string cspValue = "default-src 'self';";
            // For Chrome 16+
            response.Headers.Set(SecurityHeadersConstants.XWebKitCspHeader, cspValue);

            // For Firefox 4+
            response.Headers.Set(SecurityHeadersConstants.XContentSecurityPolicyHeader, cspValue);
            response.Headers.Set(SecurityHeadersConstants.ContentSecurityPolicyHeader, cspValue);
        }
    }
}
و یا در برنامه‌های ASP.NET MVC می‌توان یک فیلتر جدید را تعریف کرد و سپس آن‌را به صورت عمومی معرفی نمود:
//// RegisterGlobalFilters -> filters.Add(new ContentSecurityPolicyFilterAttribute());
public class ContentSecurityPolicyFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var response = filterContext.HttpContext.Response;
        response.AddHeader("Content-Security-Policy", "script-src 'self'");
        // the rest ...
        base.OnActionExecuting(filterContext);
    }
}
در ماژول تهیه شده چند مورد دیگر را نیز مشاهده می‌کنید:
الف) X-XSS-Protection مربوط است به IE 8 به بعد
ب)  تنظیم هدر X-Frame-Options به SameOrigin سبب می‌شود تا صفحات سایت شما دیگر توسط Iframeها در سایت‌های دیگر قابل نمایش نباشد و فقط در سایت جاری بتوان صفحه‌ای را از همان دومین در صورت نیاز توسط Iframeها نمایش داد.
ج) تنظیم X-Content-Type-Options به nosniff سبب می‌شود تا IE سعی نکند با اجرای یک محتوا سعی در تشخیص mime-type آن کند و به این ترتیب امنیت دسترسی و مشاهده اشیاء قرار گرفته در صفحه (و یا تزریق شده توسط مهاجمین) به شدت بالا خواهد رفت.


برای مطالعه بیشتر
Security through HTTP response headers


پروژه‌ی کاملی مخصوص افزودن هدرهای یاد شده
https://nwebsec.codeplex.com/


یک نکته تکمیلی
توصیه شده‌است تا دیگر از روال رویدادگردان PreSendRequestHeaders برای ارسال هدرها استفاده نکنید؛ چون با پردازش‌های غیرهمزمان تداخل ایجاد می‌کند.

 
مطالب
مفاهیم برنامه نویسی ـ مروری بر کلاس و شیء
من قصد دارم در قالب چند مطلب برخی از مفاهیم پایه و مهم برنامه نویسی را که پیش نیازی برای درک اکثر مطالب موجود در وب سایت است به زبان ساده بیان کنم تا دایره افرادی که می‌توانند از مطالب ارزشمند این وب سایت استفاده کنند وسعت بیشتری پیدا کند. لازم به توضیح است از آنجا که علاقه ندارم اینجا تبدیل به نسخه فارسی MSDN یا کتاب آنلاین آموزش برنامه نویسی شود این سری آموزش‌ها بیشتر شامل مفاهیم کلیدی خواهند بود.
این مطلب به عنوان اولین بخش از این سری مطالب منتشر می‌شود.

هدف این نوشته بررسی جزییات برنامه نویسی در رابطه با کلاس و شیء نیست. بلکه دریافتن چگونگی شکل گرفتن ایده شیء گرایی و علت مفید بودن آن است.

مشاهده مفاهیم شیء گرایی در پیرامون خود

حتماً در دنیای برنامه نویسی شیء گرا بارها با کلمات کلاس و شیء روبرو شده اید. درک صحیح از این مفاهیم بسیار مهم و البته بسیار ساده است. کار را با یک مثال شروع می‌کنیم. به تصویر زیر نگاه کنید.
 



در سمت راست بخشی از نقشه یک ساختمان و در سمت چپ ساختمان ساخته شده بر اساس این نقشه را می‌بینید. ساختمان همان شیء است. و نقشه ساختمان کلاس آن است چراکه امکان ایجاد اشیائی که تحت عنوان ساختمان طبقه بندی (کلاس بندی) می‌شوند را فراهم می‌کند. به همین سادگی. کلاس‌ها طرح اولیه، نقشه یا قالبی هستند که جزییات یک شی را توصیف می‌کنند.
حتماً با من موافق هستید اگر بگویم:
  • در نقشه ساختمان نمی‌توانید زندگی کنید اما در خود ساختمان می‌توانید.
  • از روی یک نقشه می‌توان به تعداد دلخواه ساختمان ساخت.
  • هنگامی که در یک ساختمان زندگی می‌کنید نیازی نیست تا دقیقاً بدانید چگونه ساخته شده و مثلاً سیم کشی یا لوله کشی‌های آن چگونه است! تنها کافیست بدانید برای روشن شدن لامپ باید کلید آن را بزنید.
  • ساختمان دارای ویژگی هایی مانند متراژ، ضخامت دیوار، تعداد پنجره و ابعاد هر یک و ... است که در هنگام ساخت و بر اساس اطلاعات موجود در نقشه تعیین شده اند.
  • ساختمان دارای کارکرد هایی است. مانند بالا و پایین رفتن آسانسور و یا باز و بسته شدن درب پارکینگ. هر یک از این کارکرد‌ها نیز بر اساس اطلاعات موجود در نقشه پیاده سازی و ساخته شده اند.
  • ساختمان تمام اجزای لازم برای اینکه از آن بتوانیم استفاده کنیم و به عبارتی در آن بتوانیم زندگی کنیم را در خود دارد.
در محیط پیرامون ما تقریباً هر چیزی را می‌توان در یک دیدگاه شیء تصور کرد. به عبارتی هر چیزی که بتوانید به صورت مستقل در ذهن بیاورید و سپس برخی ویژگی‌ها و رفتارها یا کارکردهای آن‌را برشمارید تا آن چیز را قابل شناسایی کند شیء است. مثلاً من به شما می‌گویم موجودی چهار پا دارد، مو... مو... می‌کند و شیر می‌دهد و ... . شما خواهید گفت گاو! و نمی‌گویید گربه. چرا؟ چون توانستید در ذهن خود موجودیتی را به صورت مستقل تصور کنید و از روی ویژگی‌ها و رفتارش آن‌را دقیقاً شناسایی کنید.
سوال: کلاس یا نقشه ایجاد گاو چیست؟ اگر از من بپرسید خواهم گفت طرح اولیه گاو هم ممکن است وجود داشته باشد البته در اختیار خداوند و با سطح دسترسی ملکوت!
اتومبیل، تلویزیون و ... همگی مثال هایی از اشیاء پیرامون ما در دنیای واقعی هستند که حتماً می‌توانید کلاس یا نقشه ایجاد آن‌ها را نیز بدست آورید و یا ویژگی‌ها و کارکرد‌های آن‌ها را برشمارید.

مفاهیم شیء گرایی در مهندسی نرم افزار

مفاهیمی که تاکنون در مورد دنیای واقعی مرور کردیم همان چیزی است که در دنیای برنامه نویسی ـ به عقیده من دنیای واقعی‌تر از دنیای واقعی ـ با آن سر و کار داریم. علت این امر آن است که اصولاً ایده روش برنامه نویسی شیء گرا با مشاهده محیط پیرامون ما به وجود آمده است.
برای نوشتن برنامه جهت حل یک مسئله بزرگ باید بتوان آن مسئله را به بخش‌های کوچکتری تقسیم نمود. در این رابطه مفهوم شیء و کلاس با همان کیفیتی که در محیط پیرامون ما وجود دارد به صورت مناسبی امکان تقسیم یه مسئله بزرگ به بخش‌های کوچکتر را فراهم می‌کند. و سبب می‌شود هماهنگی و تقارن و تناظر خاصی بین اشیاء برنامه و دنیای واقعی بوجود آید که یکی از مزایای اصلی روش شیء گراست.
از آنجا که در یک برنامه اصولاً همه چیز و همه مفاهیم در قالب کدها و دستورات برنامه معنا دارد، کلاس و شیء نیز چیزی بیش از قطعاتی کد نیستند. قطعه کد هایی که بسته بندی شده اند تا تمام کار مربوط به هدفی که برای آن‌ها در نظر گرفته شده است را انجام دهند.
همان طور که در هر زبان برنامه نویسی دستوراتی برای کارهای مختلف مانند تعریف یک متغیر یا ایجاد یک حلقه و ... در نظر گرفته شده است، در زبان‌های برنامه نویسی شیء گرا نیز دستوراتی وجود دارد تا بتوان قطعه کدی را بر اساس مفهوم کلاس بسته بندی کرد.
به طور مثال قطعه کد زیر را در زبان برنامه نویسی سی شارپ در نظر بگیرید.
class Player
{
   public string Name;
   public int Age;
   public void Walk()
   {
      // کدهای مربوط به پیاده سازی راه رفتن
   }
   public void Run()
   {
      // کدهای مربوط به پیاده سازی دویدن
   }
}
در این قطعه کد با استفاده از کلمه کلیدی class در زبان سی شارپ کلاسی ایجاد شده است که دارای دو ویژگی نام و سن و دو رفتار راه رفتن و دویدن است.
این کلاس به چه دردی می‌خورد؟ کجا می‌توانیم از این کلاس استفاده کنیم؟
پاسخ این است که این کلاس ممکن است برای ما هیچ سودی نداشته باشد و هیچ کجا نتوانیم از آن استفاده کنیم. اما بیایید فرض کنیم برنامه نویسی هستیم که قصد داریم یک بازی فوتبال بنویسیم. به جای آنکه قطعات کد مجزایی برای هر یک از بازیکنان و کنترل رفتار و ویژگی‌های آنان بنویسیم با اندکی تفکر به این نکته پی می‌بریم که همه بازیکنان مشترکات بسیاری دارند و به عبارتی در یک گروه یا کلاس قابل دسته بندی هستند. پس سعی می‌کنیم نقشه یا قالبی برای بازیکن‌ها ایجاد کنیم که دربردارنده ویژگی‌ها و رفتارهای آن‌ها باشد.
همان طور که در نقشه ساختمان نمی‌توانیم زندگی کنیم این کلاس هم هنوز آماده انجام کارهای واقعی نیست. چراکه برخی مقادیر هنوز برای آن تنظیم نشده است. مانند نام بازیکن و سن و ....
و همان طور که برای سکونت لازم است ابتدا یک ساختمان از روی نقشه ساختمان بسازیم برای استفاده واقعی از کلاس یاد شده نیز باید از روی آن شیء بسازیم. به این فرآیند وهله سازی یا نمونه سازی نیز می‌گویند. یک زبان برنامه نویسی شیء گرا دستوراتی را برای وهله سازی نیز در نظر گرفته است. در C# کلمه کلیدی new این وظیفه را به عهده دارد.
Player objPlayer = new Player();
objPlayer.Name = “Ali Karimi”;
objPlayer.Age = 30;
objPlayer.Run();
وقتی فرآیند وهله سازی صورت می‌گیرد یک نمونه یا شیء از آن کلاس در حافظه ساخته می‌شود که در حقیقت می‌توانید آنرا همان کدهای کلاس تصور کنید با این تفاوت که مقداردهی‌های لازم صورت گرفته است. به دلیل تعیین مقادیر لازم، حال شیء تولید شده می‌تواند به خوبی اعمال پیش بینی شده را انجام دهد. توجه نمایید در اینجا پیاده سازی داخلی رفتار دویدن و اینکه مثلاً در هنگام فراخوانی آن چه کدی باید اجرا شود تا تصویر یک بازیکن در حال دویدن در بازی نمایش یابد مد نظر و موضوع بحث ما نیست. بحث ما چگونگی سازماندهی کد‌ها توسط مفهوم کلاس و شیء است. همان طور که مشاهده می‌کنید ما تمام جزییات بازیکن‌ها را یکبار در کلاس پیاده سازی کرده ایم اما به تعداد دلخواه می‌توانیم از روی آن بازیکن‌های مختلف را ایجاد کنیم. همچنین به راحتی رفتار دویدن یک بازیکن را فراخوانی میکنیم بدون آنکه پیاده سازی کامل آن در اختیار و جلوی چشم ما باشد.
تمام آنچه که بازیکن برای انجام امور مربوط به خود نیاز دارد در کلاس بازیکن کپسوله می‌شود. بدیهی است در یک برنامه واقعی ویژگی‌ها و رفتارهای بسیار بیشتری باید برای کلاس بازیکن در نظر گرفته شود. مانند پاس دادن، شوت زدن و غیره.
به این ترتیب ما برای هر برنامه می‌توانیم مسئله اصلی را به تعدادی مسئله کوچکتر تقسیم کنیم و وظیفه حل هر یک از مسائل کوچک را به یک شیء واگذار کنیم. و بر اساس اشیاء تشخیص داده شده کلاس‌های مربوطه را بنویسیم. برنامه نویسی شیء گرا سبب می‌شود تا مسئله توسط تعدادی شیء که دارای نمونه‌های متناظری در دنیای واقعی هستند حل شود که این امر زیبایی و خوانایی و قابلیت نگهداری و توسعه برنامه را بهبود می‌دهد.
احتمالاً تاکنون متوجه شده اید که برای نگهداری ویژگی‌های اشیاء از متغیر‌ها و برای پیاده سازی رفتارها یا کارکرد‌های اشیاء از توابع استفاده میکنیم.
با توجه به این که هدف این مطلب بررسی مفهوم شیء گرائی بود و نه جزییات برنامه نویسی، بنابراین بیان برخی مفاهیم در این رابطه را که بیشتر در مهندسی نرم افزار معنا دارند تا در دنیای واقعی در مطالب بعدی بررسی می‌کنیم.
مطالب
Best Practice هایی برای ASP.NET MVC - قسمت اول
در سایت جاری مطالب زیادی درباره ASP.NET MVC نوشته شده است. این مطلب و قسمت بعدی آن مروری خواهد داشت بر Best Practice‌ها در ASP.NET MVC.

استفاده از NuGet Package Manager برای مدیریت وابستگی‌ها

درباره اهمیت NuGet برای مصرف کنندگان قبلا این مطلب نوشته شده است.
بجای صرف وقت برای اینکه بررسی کنیم آیا این نسخه‌ی جدید کتابخانه‌ی X یا اسکریپت jQuery آمده است یا خیر، می‌توان این وظیفه را به NuGet سپرد. علاوه بر این NuGet مزیت‌های دیگری هم دارد؛ مثلا تیم‌های برنامه نویسی می‌توانند کتاب خانه‌های مشترک خودشان را در مخزن‌های سفارشی NuGet قرار دهند و توزیع و Versioning آن‌را به NuGet بسپارند.


تکیه بر Abstraction (انتزاع)

Abstraction در طراحی سیستم‌ها منجر به تولید نرم افزار هایی Loosely coupled با قابلیت نگهداری بالا و همچنین فراهم شدن زمینه برای نوشتن Unit Test می‌شود.
اگر به مطالب قبلی وب سایت برگردیم در مطلب چرا ASP.NET MVC گفته شد که :
2) دستیابی به کنترل بیشتر بر روی اجزای فریم ورک :
در طراحی ASP.NET MVC همه‌جا interface‌ها قابل مشاهد هستند. همین مساله به معنای افزونه پذیری اکثر قطعات تشکیل دهنده ASP.NET MVC است؛ برخلاف ASP.NET web forms. برای مثال تابحال چندین view engine، routing engine و غیره توسط برنامه نویس‌های مستقل برای ASP.NET MVC طراحی شده‌اند که هیچکدام با ASP.NET web forms میسر نیست. برای مثال از view engine پیش فرض آن خوشتان نمی‌آید؟
عوضش کنید! سیستم اعتبار سنجی توکار آن‌را دوست ندارید؟ آن‌را با یک نمونه بهتر تعویض کنید و الی آخر ...
به علاوه طراحی بر اساس interface‌ها یک مزیت دیگر را هم به همراه دارد و آن هم ساده سازی
mocking (تقلید) آن‌ها است جهت ساده سازی نوشتن آزمون‌های واحد.


از کلمه‌ی کلیدی New استفاده نکنید

هر جا ممکن است کار وهله سازی اشیاء را به لایه و حتی Framework دیگری بسپارید. هر زمان اشیاء نرم افزار خودتان را با کلمه‌ی new وهله سازی می‌کنید اصل Abstraction را فراموش کرده اید. هر زمان اشیاء نرم افزار را مستقیم وهله سازی می‌کنید در نظر داشته باشید می‌توانید آنها را به صورت وابستگی تزریق کنید.
در همین رابطه مطالب زیر را از دست ندهید :


از HttpContext مستقیما استفاده نکنید (از HttpContextBase استفاده کنید)

از .NET 4 به بعد فضای نامی تعریف شده که در بر دارنده‌ی کلاس‌های انتزاعی (Abstraction) خیلی از قسمت‌های اصلی ASP.NET است.  یکی از مواردی که در توسعه‌ی ASP.NET معمولا زیاد استفاده می‌شود، شیء HttpContext است . استفاده از HttpContextBase را به استفاده از HttpContext ترجیح دهید. اهمیت این موضوع در راستای اهمیت انتزاع (Abstraction) می‌باشد.


از "رشته‌های جادویی" اجتناب کنید

استفاده از رشته‌های جادویی در خیلی از جاها کار را ساده می‌کند؛ بعضی وقت‌ها هم به آنها نیاز است اما مشکلات زیادی دارند :
  1. رشته‌ها معنای باطنی ندارند (مثلا : دشوار است که از روی نام یک ID مشخص کنم این ID چطور به ID دیگری مرتبط است و یا اصلا ربط دارد یا خیر)
  2. با اشتباهات املایی یا عدم رعایت حروف بزرگ و کوچک ایجاد مشکل می‌کنند.
  3. به Refactoring واکنش خوبی نشان نمی‌دهند. (برای درک بهتر این مطلب را بخوانید.)
برای درک بهتر 2، یک مثال بررسی می‌شود؛ اولی از رشته‌های جادویی برای دسترسی به داده در ViewData استفاده می‌کند و دومی refactor شده‌ی مثال اول است که از strongly type مدل برای دسترسی به همان داده استفاده می‌کند.
<p>
    <label for="FirstName">First Name:</label>
    <span id="FirstName">@ViewData["FirstName"]</span>
</p>
مثال دوم :
<p>
    <label for="FirstName">First Name:</label>
    <span id="FirstName">@Model.FirstName</span>
</p>
مثلا مثال دوم مشکلات رشته‌های جادویی را ندارد.
در رابطه با Magic strings این مطلب را مطالعه بفرمایید.


از نوشتن HTML در کدهای "Backend" خودداری کنید

با توجه به اصل جداسازی وابستگی‌ها (Separation of Concerns) وظیفه‌ی کنترلر‌ها و دیگر کدهای backend رندر کردن HTML نیست. (ساده سازی کنترلر ها) البته در نظر داشته باشید که قطعا تولید HTML در متد‌های کمکی کلاس هایی که "تنها" وظیفه‌ی آنها کمک به View‌ها جهت تولید کد هست ایرادی ندارد. این کلاس‌ها بخشی از View در نظر گرفته می‌شوند نه کدهای "backend".


در View‌ها "Business logic" انجام ندهید

معکوس بند قبلی هم کاملا صدق می‌کند ، منظور این است که View‌ها تا جایی که ممکن است باید حاوی کمترین Business logic ممکن باشند. در واقع تمرکز View‌ها باید استفاده و نحوه‌ی نمایش داده ای که برای آن‌ها فراهم شده باشد نه انجام عملیات روی آن.


استفاده از Presentation Model را به استفاده مستقیم از Business Object‌ها ترجیح دهید

در مطالب مختلف وب سایت اشاره به اهمین ViewModel‌ها شده است. برای اطلاعات بیشتر بند ج آموزش 11 از سری آموزش‌های ASP.NET MVC را مطالعه بفرمایید.


If‌های شرطی را در View‌ها را در متد‌های کمکی کپسوله کنید

استفاده از شرط‌ها در View کار توسعه دهنده را برای یک سری اعمال ساده می‌کند اما می‌تواند باعث کمی کثیف کاری هم شود. مثلا:
@if(Model.IsAnonymousUser) {
    <img src="@Url.Content("~/content/images/anonymous.jpg")" />
} else if(Model.IsAdministrator) {
    <img src="@Url.Content("~/content/images/administrator.jpg")" />
} else if(Model.Membership == Membership.Standard) {
    <img src="@Url.Content("~/content/images/member.jpg")" />
} else if(Model.Membership == Membership.Preferred) {
    <img src="@Url.Content("~/content/images/preferred_member.jpg")" />
}
می‌توان این کد که تا حدودی شامل منطق تجاری هم هست را در یک متد کمکی کپسوله کرد :
public static string UserAvatar(this HtmlHelper<User> helper)
{
    var user = helper.ViewData.Model;
    string avatarFilename = "anonymous.jpg";
    if (user.IsAnonymousUser)
    {
        avatarFilename = "anonymous.jpg";
    }
    else if (user.IsAdministrator)
    {
        avatarFilename = "administrator.jpg";
    }
    else if (user.Membership == Membership.Standard)
    {
        avatarFilename = "member.jpg";
    }
    else if (user.Membership == Membership.Preferred)
    {
        avatarFilename = "preferred_member.jpg";
    }
    var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
    var contentPath = string.Format("~/content/images/{0}", avatarFilename)
    string imageUrl = urlHelper.Content(contentPath);
    return string.Format("<img src='{0}' />", imageUrl);
}
اکنون برای نمایش آواتار کاربر به سادگی می‌توان  نوشت :
@Html.UserAvatar()
به این ترتیب کد ما تمیز‌تر شده ، قابلیت نگهداری آن بالاتر رفته ، منطق تجاری یک بار و در یک قسمت نوشته شده از این کد در جاهای مختلف سایت می‌توان استفاده کرد و اگر لازم به تغییر باشد با تغییر در یک قسمت همه جا اعمال می‌شود.

منتظر قسمت بعدی باشید.
 
مطالب
ساخت یک گزارش ساز به کمک iTextSharp و Open Office

iTextSharp پایه کار با فایل‌های PDF را ارائه می‌دهد اما ابزاری را جهت ساده‌تر سازی تولید فایل‌های PDF به همراه ندارد؛ هر چند مثلا امکان تبدیل HTML به PDF را دارا است اما باید گفت: «تا حدودی البته». اگر نیاز باشد جدولی را ایجاد کنیم باید کد نویسی کرد، اگر نیاز باشد تصویری اضافه شود به همین ترتیب و الی آخر. البته این را هم باید در نظر داشت که کد نویسی انعطاف قابل توجهی را در اختیار برنامه نویس قرار می‌دهد؛ شاید به همین دلیل این روزها مباحث «Code first» بیشتر مورد توجه برنامه نویس‌ها است، تا مباحث «Wizard first» یک دهه قبل!
اما باز هم داشتن یک طراح بد نیست و می‌تواند در کاهش مدت زمان تولید نهایی یک فایل PDF مؤثر باشد. برای این منظور می‌توان از برنامه‌ی رایگان و معروف Open office استفاده کرد. توسط آن می‌توان یک فرم PDF را طراحی و سپس فیلدهای آن‌را (این قالب تهیه شده را) با iTextSharp پر کرد. این مورد می‌تواند برای تهیه گزارش‌هایی که تهیه آن‌ها با ابزارهای متداول گزارش سازی عموما میسر نیست،‌ بسیار مناسب باشد.


طراحی یک فرم PDF با استفاده از برنامه Open Office

آخرین نگارش برنامه Open office را از اینجا می‌توانید دریافت کنید و آنچنان حجمی هم ندارد؛ حدودا 154 مگابایت است.
پس از نصب و اجرای برنامه، حداقل به دو طریق می‌توان یک فرم جدید را شروع کرد:
الف) آغاز یک XML Form document جدید در Open office سبب خواهد شد که نوارهای ابزار طراحی فرم، مانند قرار دادن TextBox ، CheckBox و غیره به صورت خودکار ظاهر شوند.
ب) و یا آغاز یک سند معمولی و سپس مراجعه به منوی View->Toolbars->Form Controls هم همان حالت را به همراه خواهد داشت.


در اینجا برای طراحی یک گزارش یا فرم جدید تنها کافی است همانند روش‌های متداول تهیه یک سند معمولی رفتار کنیم و مواردی را که قرار است توسط iTextSharp مقدار دهی کنیم، با کنترل‌های نوار ابزار Form آن بر روی صفحه قرار دهیم که نمونه‌ی ساده آن‌را در شکل زیر ملاحظه می‌کنید:


برای گزارش‌های فارسی بهتر است Alignment یک کنترل به Right تنظیم شود و Border به حالت Without frame مقدار دهی گردد. نام این کنترل را هم بخاطر بسپارید و یا تغییر دهید. از این نام‌ها در iTextSharp استفاده خواهیم کرد. (صفحه خواص فوق با دوبار کلیک بر روی یک کنترل قرار گرفته بر روی فرم ظاهر می‌شود)

مرحله بعد، تبدیل این فرم به فایل PDF است. کلیک بر روی دکمه تهیه خروجی به صورت PDF در نوار ابزار اصلی آن برای اینکار کفایت می‌کند. این گزینه در منوی File نیز موجود است.


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


استفاده از iTextSharp جهت مقدار دهی فیلدهای یک فرم PDF

در ادامه می‌خواهیم این قالب گزارشی را که تهیه کردیم با کمک iTextSharp پر کرده و یک فایل PDF جدید تهیه کنیم. سورس کامل اینکار را در ذیل مشاهده می‌کنید:


using System;
using System.Diagnostics;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace PdfForm
{
class Program
{
//روش صحیح ثبت و معرفی فونت در این کتابخانه
public static iTextSharp.text.Font GetTahoma()
{
var fontName = "Tahoma";
if (!FontFactory.IsRegistered(fontName))
{
var fontPath = Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\tahoma.ttf";
FontFactory.Register(fontPath);
}
return FontFactory.GetFont(fontName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
}

static void Main(string[] args)
{
string fileNameExisting = @"form.pdf";
string fileNameNew = @"newform.pdf";

using (var existingFileStream = new FileStream(fileNameExisting, FileMode.Open))
using (var newFileStream = new FileStream(fileNameNew, FileMode.Create))
{
var pdfReader = new PdfReader(existingFileStream);
using (var stamper = new PdfStamper(pdfReader, newFileStream))
{
//نکته مهم جهت کار با اطلاعات فارسی
//در غیراینصورت شاهد ثبت اطلاعات نخواهید بود
stamper.AcroFields.AddSubstitutionFont(GetTahoma().BaseFont);

//form.Fields.Keys = تمام فیلدهای موجود در فرم
var form = stamper.AcroFields;

//مقدار دهی فیلدهای فرم
form.SetField("TextBox1", "مقدار1");
form.SetField("TextBox2", "مقدار2");

// "Yes" and "Off" are valid values here
form.SetField("Check Box 1", "Yes");

// "" and "Off" are valid values here
form.SetField("Option Button 1", "");

// نحوه مقدار دهی لیست
form.SetListOption("ListBox1", new[] { "1مقدار یک", "مقدار دو1" }, null);
form.SetField("ListBox1", null);

// به این ترتیب فرم دیگر توسط کاربر قابل ویرایش نخواهد بود
//stamper.PartialFormFlattening --> جهت غیرقابل ویرایش نمودن فیلدی مشخص
stamper.FormFlattening = true;

stamper.Close();
pdfReader.Close();
}
}

Process.Start("newform.pdf");
}
}
}

توضیحات:
چون در اینجا فایل PDF، از پیش تهیه شده است، پس باید از اشیاء PdfReader و PdfStamper جهت خواندن و نوشتن اطلاعات در آن‌ها استفاده کرد. سپس توسط شیء stamper.AcroFields می‌توان به این فیلدها یا همان کنترل‌هایی که در برنامه‌ی Open office بر روی فرم قرار دادیم، دسترسی پیدا کنیم.
در ابتدا نیاز است فونت این فیلدها توسط متد AddSubstitutionFont مقدار دهی شود. این مورد برای گزارش‌های فارسی الزامی است؛ در غیراینصورت متنی را در خروجی مشاهده نخواهید کرد.
ادامه کار هم مشخص است. توسط متد form.SetField مقداری را به کنترل‌های قرار گرفته بر روی فرم نسبت می‌دهیم. آرگومان اول آن نام کنترل است و آرگومان دوم، مقدار مورد نظر می‌باشد. اگر کنترل CheckBox را بر روی صفحه قرار دادید، تنها مقدارهای Yes و Off را می‌پذیرد (آن هم با توجه به اینکه به کوچکی و بزرگی حروف حساس است). اگر یک Radio button یا در اینجا Option button را بر روی فرم قرار دادید، تنها مقدارهای خالی و Off را قبول خواهد کرد. نحوه‌ی مقدار دهی یک لیست هم در اینجا ذکر شده است.
در پایان چون نمی‌خواهیم کاربر نهایی قادر به ویرایش اطلاعات باشد، FormFlattening را true خواهیم کرد و به این ترتیب، کنترل‌ها فقط خواندنی خواهند شد. البته اگر همانطور که ذکر شد، border کنترل‌ها را در حین طراحی حذف کنید، PDF نهایی تولیدی یکپارچه و یک دست به نظر می‌رسد و اصلا مشخص نخواهد بود که این فایل پیشتر یک فرم قابل پر کردن بوده است.

مطالب
استفاده از EF در اپلیکیشن های N-Tier : قسمت دوم
در قسمت قبل معماری اپلیکیشن‌های N-Tier و بروز رسانی موجودیت‌های منفصل توسط Web API را بررسی کردیم. در این قسمت بروز رسانی موجودیت‌های منفصل توسط WCF را بررسی می‌کنیم.

بروز رسانی موجودیت‌های منفصل توسط WCF

سناریویی را در نظر بگیرید که در آن عملیات CRUD توسط WCF پیاده سازی شده اند و دسترسی داده‌ها با مدل Code-First انجام می‌شود. فرض کنید مدل اپلیکیشن مانند تصویر زیر است.

همانطور که می‌بینید مدل ما متشکل از پست‌ها و نظرات کاربران است. برای ساده نگاه داشتن مثال جاری، اکثر فیلدها حذف شده اند. مثلا متن پست ها، نویسنده، تاریخ و زمان انتشار و غیره. می‌خواهیم تمام کد دسترسی داده‌ها را در یک سرویس WCF پیاده سازی کنیم تا کلاینت‌ها بتوانند عملیات CRUD را توسط آن انجام دهند. برای ساختن این سرویس مراحل زیر را دنبال کنید.

  • در ویژوال استودیو پروژه جدیدی از نوع Class Library بسازید و نام آن را به Recipe2 تغییر دهید.
  • با استفاده از NuGet Package Manager کتابخانه Entity Framework 6 را به پروژه اضافه کنید.
  • سه کلاس با نام‌های Post, Comment و Recipe2Context به پروژه اضافه کنید. کلاس‌های Post و Comment موجودیت‌های مدل ما هستند که به جداول متناظرشان نگاشت می‌شوند. کلاس Recipe2Context آبجکت DbContext ما خواهد بود که بعنوان درگاه عملیاتی EF عمل می‌کند. دقت کنید که خاصیت‌های لازم WCF یعنی DataContract و DataMember در کلاس‌های موجودیت‌ها بدرستی استفاده می‌شوند. لیست زیر کد این کلاس‌ها را نشان می‌دهد.
[DataContract(IsReference = true)]
public class Post
{
    public Post()
    {
        comments = new HashSet<Comments>();
    }
    
    [DataMember]
    public int PostId { get; set; }
    [DataMember]
    public string Title { get; set; }
    [DataMember]
    public virtual ICollection<Comment> Comments { get; set; }
}

[DataContract(IsReference=true)]
public class Comment
{
    [DataMember]
    public int CommentId { get; set; }
    [DataMember]
    public int PostId { get; set; }
    [DataMember]
    public string CommentText { get; set; }
    [DataMember]
    public virtual Post Post { get; set; }
}

public class EFRecipesEntities : DbContext
{
    public EFRecipesEntities() : base("name=EFRecipesEntities") {}

    public DbSet<Post> posts;
    public DbSet<Comment> comments;
}
  • یک فایل App.config به پروژه اضافه کنید و رشته اتصال زیر را به آن اضافه نمایید.
<connectionStrings>
  <add name="Recipe2ConnectionString"
    connectionString="Data Source=.;
    Initial Catalog=EFRecipes;
    Integrated Security=True;
    MultipleActiveResultSets=True"
    providerName="System.Data.SqlClient" />
</connectionStrings>
  • حال یک پروژه WCF به Solution جاری اضافه کنید. برای ساده نگاه داشتن مثال جاری، نام پیش فرض Service1 را بپذیرید. فایل IService1.cs را باز کنید و کد زیر را با محتوای آن جایگزین نمایید.
[ServiceContract]
public interface IService1
{
    [OperationContract]
    void Cleanup();
    [OperationContract]
    Post GetPostByTitle(string title);
    [OperationContract]
    Post SubmitPost(Post post);
    [OperationContract]
    Comment SubmitComment(Comment comment);
    [OperationContract]
    void DeleteComment(Comment comment);
}
  • فایل Service1.svc.cs را باز کنید و کد زیر را با محتوای آن جایگزین نمایید. بیاد داشته باشید که پروژه Recipe2 را ارجاع کنید و فضای نام آن را وارد نمایید. همچنین کتابخانه EF 6 را باید به پروژه اضافه کنید.
public class Service1 : IService
{
    public void Cleanup()
    {
        using (var context = new EFRecipesEntities())
        {
            context.Database.ExecuteSqlCommand("delete from [comments]");
            context. Database.ExecuteSqlCommand ("delete from [posts]");
        }
    }

    public Post GetPostByTitle(string title)
    {
        using (var context = new EFRecipesEntities())
        {
            context.Configuration.ProxyCreationEnabled = false;
            var post = context.Posts.Include(p => p.Comments).Single(p => p.Title == title);
            return post;
        }
    }

    public Post SubmitPost(Post post)
    {
        context.Entry(post).State =
            // if Id equal to 0, must be insert; otherwise, it's an update
            post.PostId == 0 ? EntityState.Added : EntityState.Modified;
        context.SaveChanges();
        return post;
    }

    public Comment SubmitComment(Comment comment)
    {
        using (var context = new EFRecipesEntities())
        {
            context.Comments.Attach(comment);
            if (comment.CommentId == 0)
            {
                // this is an insert
                context.Entry(comment).State = EntityState.Added);
            }
            else
            {
                // set single property to modified, which sets state of entity to modified, but
                // only updates the single property – not the entire entity
                context.entry(comment).Property(x => x.CommentText).IsModified = true;
            }
            context.SaveChanges();
            return comment;
        }
    }

    public void DeleteComment(Comment comment)
    {
        using (var context = new EFRecipesEntities())
        {
            context.Entry(comment).State = EntityState.Deleted;
            context.SaveChanges();
        }
    }
}


  • در آخر پروژه جدیدی از نوع Windows Console Application به Solution جاری اضافه کنید. از این اپلیکیشن بعنوان کلاینتی برای تست سرویس WCF استفاده خواهیم کرد. فایل program.cs را باز کنید و کد زیر را با محتوای آن جایگزین نمایید. روی نام پروژه کلیک راست کرده و گزینه Add Service Reference را انتخاب کنید، سپس ارجاعی به سرویس Service1 اضافه کنید. رفرنسی هم به کتابخانه کلاس‌ها که در ابتدای مراحل ساختید باید اضافه کنید.
class Program
{
    static void Main(string[] args)
    {
        using (var client = new ServiceReference2.Service1Client())
        {
            // cleanup previous data
            client.Cleanup();
            // insert a post
            var post = new Post { Title = "POCO Proxies" };
            post = client.SubmitPost(post);
            // update the post
            post.Title = "Change Tracking Proxies";
            client.SubmitPost(post);
            // add a comment
            var comment1 = new Comment { CommentText = "Virtual Properties are cool!", PostId = post.PostId };
            var comment2 = new Comment { CommentText = "I use ICollection<T> all the time", PostId = post.PostId };
            comment1 = client.SubmitComment(comment1);
            comment2 = client.SubmitComment(comment2);
            // update a comment
            comment1.CommentText = "How do I use ICollection<T>?";
            client.SubmitComment(comment1);
            // delete comment 1
            client.DeleteComment(comment1);
            // get posts with comments
            var p = client.GetPostByTitle("Change Tracking Proxies");
            Console.WriteLine("Comments for post: {0}", p.Title);
            foreach (var comment in p.Comments)
            {
                Console.WriteLine("\tComment: {0}", comment.CommentText);
            }
        }
    }
}
اگر اپلیکیشن کلاینت (برنامه کنسول) را اجرا کنید با خروجی زیر مواجه می‌شوید.

Comments for post: Change Tracking Proxies
Comment: I use ICollection<T> all the time


شرح مثال جاری

ابتدا با اپلیکیشن کنسول شروع می‌کنیم، که کلاینت سرویس ما است. نخست در یک بلاک {} using وهله ای از کلاینت سرویس مان ایجاد می‌کنیم. درست همانطور که وهله ای از یک EF Context می‌سازیم. استفاده از بلوک‌های using توصیه می‌شود چرا که متد Dispose بصورت خودکار فراخوانی خواهد شد، چه بصورت عادی چه هنگام بروز خطا. پس از آنکه وهله ای از کلاینت سرویس را در اختیار داشتیم، متد Cleanup را صدا می‌زنیم. با فراخوانی این متد تمام داده‌های تست پیشین را حذف می‌کنیم. در چند خط بعدی، متد SubmitPost را روی سرویس فراخوانی می‌کنیم. در پیاده سازی فعلی شناسه پست را بررسی می‌کنیم. اگر مقدار شناسه صفر باشد، خاصیت State موجودیت را به Added تغییر می‌دهید تا رکورد جدیدی ثبت کنیم. در غیر اینصورت فرض بر این است که چنین موجودیتی وجود دارد و قصد ویرایش آن را داریم، بنابراین خاصیت State را به Modified تغییر می‌دهیم. از آنجا که مقدار متغیرهای int بصورت پیش فرض صفر است، با این روش می‌توانیم وضعیت پست‌ها را مشخص کنیم. یعنی تعیین کنیم رکورد جدیدی باید ثبت شود یا رکوردی موجود بروز رسانی گردد. رویکردی بهتر آن است که پارامتری اضافی به متد پاس دهیم، یا متدی مجزا برای ثبت رکوردهای جدید تعریف کنیم. مثلا رکوردی با نام InsertPost. در هر حال، بهترین روش بستگی به ساختار اپلیکیشن شما دارد.

اگر پست جدیدی ثبت شود، خاصیت PostId با مقدار مناسب جدید بروز رسانی می‌شود و وهله پست را باز می‌گردانیم. ایجاد و بروز رسانی نظرات کاربران مشابه ایجاد و بروز رسانی پست‌ها است، اما با یک تفاوت اساسی: بعنوان یک قانون، هنگام بروز رسانی نظرات کاربران تنها فیلد متن نظر باید بروز رسانی شود. بنابراین با فیلدهای دیگری مانند تاریخ انتشار و غیره اصلا کاری نخواهیم داشت. بدین منظور تنها خاصیت CommentText را بعنوان Modified علامت گذاری می‌کنیم. این امر منجر می‌شود که Entity Framework عبارتی برای بروز رسانی تولید کند که تنها این فیلد را در بر می‌گیرد. توجه داشته باشید که این روش تنها در صورتی کار می‌کند که بخواهید یک فیلد واحد را بروز رسانی کنید. اگر می‌خواستیم فیلدهای بیشتری را در موجودیت Comment بروز رسانی کنیم، باید مکانیزمی برای ردیابی تغییرات در سمت کلاینت در نظر می‌گرفتیم. در مواقعی که خاصیت‌های متعددی می‌توانند تغییر کنند، معمولا بهتر است کل موجودیت بروز رسانی شود تا اینکه مکانیزمی پیچیده برای ردیابی تغییرات در سمت کلاینت پیاده گردد. بروز رسانی کل موجودیت بهینه‌تر خواهد بود.

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

در آخر متد GetPostByTitle یک پست را بر اساس عنوان پیدا کرده و تمام نظرات کاربران مربوط به آن را هم بارگذاری می‌کند. از آنجا که ما کلاس‌های POCO را پیاده سازی کرده ایم، Entity Framework آبجکتی را بر می‌گرداند که Dynamic Proxy نامیده می‌شود. این آبجکت پست و نظرات مربوط به آن را در بر خواهد گرفت. متاسفانه WCF نمی‌تواند آبجکت‌های پروکسی را مرتب سازی (serialize) کند. اما با غیرفعال کردن قابلیت ایجاد پروکسی‌ها (ProxyCreationEnabled=false) ما به Entity Framework می‌گوییم که خود آبجکت‌های اصلی را بازگرداند. اگر سعی کنید آبجکت پروکسی را سریال کنید با پیغام خطای زیر مواجه خواهید شد:

The underlying connection was closed: The connection was closed unexpectedly 

می توانیم غیرفعال کردن تولید پروکسی را به متد سازنده کلاس سرویس منتقل کنیم تا روی تمام متدهای سرویس اعمال شود.

در این قسمت دیدیم چگونه می‌توانیم از آبجکت‌های POCO برای مدیریت عملیات CRUD توسط WCF استفاده کنیم. از آنجا که هیچ اطلاعاتی درباره وضعیت موجودیت‌ها روی کلاینت ذخیره نمی‌شود، متدهایی مجزا برای عملیات CRUD ساختیم. در قسمت‌های بعدی خواهیم دید چگونه می‌توان تعداد متدهایی که سرویس مان باید پیاده سازی کند را کاهش داد و چگونه ارتباطات بین کلاینت و سرور را ساده‌تر کنیم.

مطالب
استفاده از گرافیک برداری در iTextSharp


در مورد «ترسیم اشکال گرافیکی با iTextSharp» مطلب مفصلی را در اینجا می‌توانید مطالعه کنید؛ که قصد تکرار مجدد آن‌را ندارم. فقط این روش‌ها یک مشکل مهم دارند : «کار من ترسیم این نوع اشکال گرافیکی نیست!». مثلا من الان نیاز دارم در گزارشی، بجای ستون Boolean آن در مواردی که مقدار ردیف true هست، مثلا یک «چک مارک» را بجای true/false یا بله/خیر نمایش دهم. می‌شود اینکار را با یک تصویر معمولی هم انجام داد. فقط حجم فایل حاصل، بیش از اندازه بالا می‌رود و همچنین نتیجه استفاده از یک bitmap، به زیبایی بکارگیری گرافیک برداری با قابلیت تغییر ابعاد بدون نگرانی در مورد از دست دادن کیفیت آن، نیست.

خوشبختانه هستند سایت‌هایی که این نوع تصاویر برداری را به رایگان ارائه دهند؛ برای مثال: سایت Openclipart، تعداد قابل توجهی فایل با فرمت SVG دارد. فایل‌های SVG را مستقیما نمی‌توان توسط iTextSharp استفاده کرد؛ اما یک سری برنامه‌ی کمکی برای تبدیل فرمت SVG به مثلا XAML (قابل توجه برنامه نویس‌های WPF و Silverlight) یا WMF و غیره وجود دارد. برای نمونه iTextSharp امکان خواندن فایل‌های WMF را داشته (توسط همان متد معروف Image.GetInstance آن) و اینبار این Image حاصل، یک تصویر برداری است و نه یک Bitmap.
در بین این برنامه‌های تبدیل کننده‌ فرمت‌های برداری، برنامه‌ی معروف و سورس باز Inkscape، در صدر محبوبیت قرار دارد. تنها کافی است فایل SVG خود را در آن گشوده و سپس به انواع و اقسام فرمت‌های دیگر تبدیل (Save As) کنید:



یکی از فرمت‌های جالب خروجی آن، Tex است (مربوط به یک برنامه ادیتور، به نام LaTeX است). فرض کنید یکی از این «چک مارک»های سایت Openclipart را در برنامه Inkscape باز کرده‌ و سپس با فرمت Tex ذخیره کرده‌ایم. خروجی فایل متنی آن مثلا به شکل زیر خواهد بود:

%LaTeX with PSTricks extensions
%%Creator: 0.48.0
%%Please note this file requires PSTricks extensions
\psset{xunit=.5pt,yunit=.5pt,runit=.5pt}
\begin{pspicture}(190,190)
{
\newrgbcolor{curcolor}{0 0 0}
\pscustom[linestyle=none,fillstyle=solid,fillcolor=curcolor]
{
\newpath
\moveto(52.73079005,101.89500456)
\curveto(31.29686559,101.89500456)(13.84575258,84.04652127)(13.8457479,62.12456369)
\curveto(13.8457479,40.20259605)(31.29686559,22.35412714)(52.73079005,22.35412235)
\curveto(74.16470983,22.35412235)(91.6158322,40.20259605)(91.61582751,62.12456369)
\curveto(91.61582751,71.60188248)(88.48023622,80.07729424)(83.15553076,87.02034164)
\lineto(79.49425309,82.58209245)
\curveto(84.13622847,76.73639073)(85.95313131,70.24630402)(85.95313131,62.12456369)
\curveto(85.95313131,43.33817595)(71.09893654,28.1547277)(52.73079005,28.1547277)
\curveto(34.36263419,28.15473249)(19.50844879,43.33817595)(19.50844879,62.12456369)
\curveto(19.50844879,80.91094185)(34.36264355,96.10336589)(52.73079005,96.10336589)
\curveto(58.55122776,96.10336589)(62.90459266,95.2476225)(67.65721002,92.5630926)
\lineto(71.13570481,97.23509821)
\curveto(65.57113223,100.3782653)(59.52269945,101.89500456)(52.73079005,101.89500456)
\closepath
}
}
{
\newrgbcolor{curcolor}{0 0 0}
\pscustom[linestyle=none,fillstyle=solid,fillcolor=curcolor]
{
\newpath
\moveto(38.33889376,67.35513328)
\curveto(39.90689547,67.35509017)(41.09296342,66.03921993)(41.89711165,63.40748424)
\curveto(43.50531445,58.47289182)(44.65118131,56.00562195)(45.33470755,56.0056459)
\curveto(45.85735449,56.00562195)(46.40013944,56.41682961)(46.96305772,57.23928802)
\curveto(58.2608517,75.74384316)(68.7143666,90.71198997)(78.32362116,102.14379168)
\curveto(80.81631349,105.10443984)(84.77658911,106.58480942)(90.20445269,106.58489085)
\curveto(91.49097185,106.58480942)(92.35539361,106.46145048)(92.79773204,106.21480444)
\curveto(93.23991593,105.96799555)(93.4610547,105.65958382)(93.46113432,105.28956447)
\curveto(93.4610547,104.71379041)(92.7976618,103.58294901)(91.47094155,101.89705463)
\curveto(75.95141033,82.81670149)(61.55772504,62.66726353)(48.28984822,41.44869669)
\curveto(47.36506862,39.96831273)(45.47540199,39.22812555)(42.62081088,39.22813992)
\curveto(39.72597184,39.22812555)(38.0172148,39.35149407)(37.49457722,39.5982407)
\curveto(36.12755286,40.2150402)(34.51931728,43.36081778)(32.66987047,49.03557823)
\curveto(30.57914689,55.32711903)(29.53378743,59.27475848)(29.53381085,60.87852533)
\curveto(29.53378743,62.60558406)(30.94099884,64.27099685)(33.75542165,65.87476369)
\curveto(35.48425582,66.86164481)(37.01207517,67.35509017)(38.33889376,67.35513328)
}
}

\end{pspicture}


استفاده از این خروجی در iTextSharp بسیار ساده است. برای مثال:

using System.Diagnostics;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace HtmlToPdf
{
class Program
{
static void Main(string[] args)
{
using (var pdfDoc = new Document(PageSize.A4))
{
var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream("Test.pdf", FileMode.Create));
pdfDoc.Open();

var cb = pdfWriter.DirectContent;

cb.MoveTo(52.73079005f, 101.89500456f);
cb.CurveTo(31.29686559f, 101.89500456f, 13.84575258f, 84.04652127f, 13.8457479f, 62.12456369f);
cb.CurveTo(13.8457479f, 40.20259605f, 31.29686559f, 22.35412714f, 52.73079005f, 22.35412235f);
cb.CurveTo(74.16470983f, 22.35412235f, 91.6158322f, 40.20259605f, 91.61582751f, 62.12456369f);
cb.CurveTo(91.61582751f, 71.60188248f, 88.48023622f, 80.07729424f, 83.15553076f, 87.02034164f);
cb.LineTo(79.49425309f, 82.58209245f);
cb.CurveTo(84.13622847f, 76.73639073f, 85.95313131f, 70.24630402f, 85.95313131f, 62.12456369f);
cb.CurveTo(85.95313131f, 43.33817595f, 71.09893654f, 28.1547277f, 52.73079005f, 28.1547277f);
cb.CurveTo(34.36263419f, 28.15473249f, 19.50844879f, 43.33817595f, 19.50844879f, 62.12456369f);
cb.CurveTo(19.50844879f, 80.91094185f, 34.36264355f, 96.10336589f, 52.73079005f, 96.10336589f);
cb.CurveTo(58.55122776f, 96.10336589f, 62.90459266f, 95.2476225f, 67.65721002f, 92.5630926f);
cb.LineTo(71.13570481f, 97.23509821f);
cb.CurveTo(65.57113223f, 100.3782653f, 59.52269945f, 101.89500456f, 52.73079005f, 101.89500456f);

cb.MoveTo(38.33889376f, 67.35513328f);
cb.CurveTo(39.90689547f, 67.35509017f, 41.09296342f, 66.03921993f, 41.89711165f, 63.40748424f);
cb.CurveTo(43.50531445f, 58.47289182f, 44.65118131f, 56.00562195f, 45.33470755f, 56.0056459f);
cb.CurveTo(45.85735449f, 56.00562195f, 46.40013944f, 56.41682961f, 46.96305772f, 57.23928802f);
cb.CurveTo(58.2608517f, 75.74384316f, 68.7143666f, 90.71198997f, 78.32362116f, 102.14379168f);
cb.CurveTo(80.81631349f, 105.10443984f, 84.77658911f, 106.58480942f, 90.20445269f, 106.58489085f);
cb.CurveTo(91.49097185f, 106.58480942f, 92.35539361f, 106.46145048f, 92.79773204f, 106.21480444f);
cb.CurveTo(93.23991593f, 105.96799555f, 93.4610547f, 105.65958382f, 93.46113432f, 105.28956447f);
cb.CurveTo(93.4610547f, 104.71379041f, 92.7976618f, 103.58294901f, 91.47094155f, 101.89705463f);
cb.CurveTo(75.95141033f, 82.81670149f, 61.55772504f, 62.66726353f, 48.28984822f, 41.44869669f);
cb.CurveTo(47.36506862f, 39.96831273f, 45.47540199f, 39.22812555f, 42.62081088f, 39.22813992f);
cb.CurveTo(39.72597184f, 39.22812555f, 38.0172148f, 39.35149407f, 37.49457722f, 39.5982407f);
cb.CurveTo(36.12755286f, 40.2150402f, 34.51931728f, 43.36081778f, 32.66987047f, 49.03557823f);
cb.CurveTo(30.57914689f, 55.32711903f, 29.53378743f, 59.27475848f, 29.53381085f, 60.87852533f);
cb.CurveTo(29.53378743f, 62.60558406f, 30.94099884f, 64.27099685f, 33.75542165f, 65.87476369f);
cb.CurveTo(35.48425582f, 66.86164481f, 37.01207517f, 67.35509017f, 38.33889376f, 67.35513328f);

cb.SetRGBColorFill(0, 0, 0);
cb.Fill();
}

Process.Start("Test.pdf");
}
}
}

در اینجا، pdfWriter.DirectContent یک Canvas را جهت ترسیمات گرافیکی در اختیار ما قرار می‌دهد. سپس مابقی هم آن مشخص است و یک تناظر یک به یک را می‌شود بین خروجی Tex و متدهای فراخوانی شده، مشاهده کرد. PDF خروجی هم به شکل زیر است:



تا اینجا یک مرحله پیشرفت است. مشکل از اینجا شروع می‌شود که خوب! من که یک «چک مارک» این اندازه‌ای لازم ندارم! آن هم قرار گرفته در پایین صفحه. یک راه حل این مشکل استفاده از متد Transform شیء cb فوق است. این متد یک System.Drawing.Drawing2D.Matrix را دریافت می‌کند و سپس می‌شود توسط آن، اعمال تغییر اندازه (Scale)، تغییر مکان (Translate) و غیره را اعمال کرد. راه دیگر تعریف یک Template از دستورات فوق است. سپس متد Image.GetInstance کتابخانه iTextSharp ورودی از نوع Template را هم قبول می‌کند. خروجی حاصل یک تصویر برداری خواهد بود که اکنون با اکثر اشیاء iTextSharp سازگار است. برای مثال متد سازنده PdfPCell، آرگومان از نوع Image را هم قبول می‌کند. به علاوه شیء Image در اینجا متدهای تغییر اندازه و امثال آن‌را نیز به همراه دارد:

using System.Diagnostics;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace HtmlToPdf
{
class Program
{
static void Main(string[] args)
{
using (var pdfDoc = new Document(PageSize.A4))
{
var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream("Test.pdf", FileMode.Create));
pdfDoc.Open();

var cb = pdfWriter.DirectContent;
var template = createCheckMark(cb);

var image = Image.GetInstance(template);
image.ScaleAbsolute(40, 40);

var table = new PdfPTable(3);
var cell = new PdfPCell(image)
{
HorizontalAlignment = Element.ALIGN_CENTER
};

for (int i = 0; i < 9; i++)
table.AddCell(cell);

pdfDoc.Add(table);
}

Process.Start("Test.pdf");
}

private static PdfTemplate createCheckMark(PdfContentByte cb)
{
var template = cb.CreateTemplate(140, 140);

template.MoveTo(52.73079005f, 101.89500456f);
template.CurveTo(31.29686559f, 101.89500456f, 13.84575258f, 84.04652127f, 13.8457479f, 62.12456369f);
template.CurveTo(13.8457479f, 40.20259605f, 31.29686559f, 22.35412714f, 52.73079005f, 22.35412235f);
template.CurveTo(74.16470983f, 22.35412235f, 91.6158322f, 40.20259605f, 91.61582751f, 62.12456369f);
template.CurveTo(91.61582751f, 71.60188248f, 88.48023622f, 80.07729424f, 83.15553076f, 87.02034164f);
template.LineTo(79.49425309f, 82.58209245f);
template.CurveTo(84.13622847f, 76.73639073f, 85.95313131f, 70.24630402f, 85.95313131f, 62.12456369f);
template.CurveTo(85.95313131f, 43.33817595f, 71.09893654f, 28.1547277f, 52.73079005f, 28.1547277f);
template.CurveTo(34.36263419f, 28.15473249f, 19.50844879f, 43.33817595f, 19.50844879f, 62.12456369f);
template.CurveTo(19.50844879f, 80.91094185f, 34.36264355f, 96.10336589f, 52.73079005f, 96.10336589f);
template.CurveTo(58.55122776f, 96.10336589f, 62.90459266f, 95.2476225f, 67.65721002f, 92.5630926f);
template.LineTo(71.13570481f, 97.23509821f);
template.CurveTo(65.57113223f, 100.3782653f, 59.52269945f, 101.89500456f, 52.73079005f, 101.89500456f);

template.MoveTo(38.33889376f, 67.35513328f);
template.CurveTo(39.90689547f, 67.35509017f, 41.09296342f, 66.03921993f, 41.89711165f, 63.40748424f);
template.CurveTo(43.50531445f, 58.47289182f, 44.65118131f, 56.00562195f, 45.33470755f, 56.0056459f);
template.CurveTo(45.85735449f, 56.00562195f, 46.40013944f, 56.41682961f, 46.96305772f, 57.23928802f);
template.CurveTo(58.2608517f, 75.74384316f, 68.7143666f, 90.71198997f, 78.32362116f, 102.14379168f);
template.CurveTo(80.81631349f, 105.10443984f, 84.77658911f, 106.58480942f, 90.20445269f, 106.58489085f);
template.CurveTo(91.49097185f, 106.58480942f, 92.35539361f, 106.46145048f, 92.79773204f, 106.21480444f);
template.CurveTo(93.23991593f, 105.96799555f, 93.4610547f, 105.65958382f, 93.46113432f, 105.28956447f);
template.CurveTo(93.4610547f, 104.71379041f, 92.7976618f, 103.58294901f, 91.47094155f, 101.89705463f);
template.CurveTo(75.95141033f, 82.81670149f, 61.55772504f, 62.66726353f, 48.28984822f, 41.44869669f);
template.CurveTo(47.36506862f, 39.96831273f, 45.47540199f, 39.22812555f, 42.62081088f, 39.22813992f);
template.CurveTo(39.72597184f, 39.22812555f, 38.0172148f, 39.35149407f, 37.49457722f, 39.5982407f);
template.CurveTo(36.12755286f, 40.2150402f, 34.51931728f, 43.36081778f, 32.66987047f, 49.03557823f);
template.CurveTo(30.57914689f, 55.32711903f, 29.53378743f, 59.27475848f, 29.53381085f, 60.87852533f);
template.CurveTo(29.53378743f, 62.60558406f, 30.94099884f, 64.27099685f, 33.75542165f, 65.87476369f);
template.CurveTo(35.48425582f, 66.86164481f, 37.01207517f, 67.35509017f, 38.33889376f, 67.35513328f);

template.SetRGBColorFill(0, 0, 0);
template.Fill();

return template;
}
}
}

در این مثال، با کمک متد CreateTemplate مرتبط با Canvas دریافتی، یک قالب جدید ایجاد و سپس روی آن نقاشی خواهیم کرد. اکنون می‌توان از این قالب تهیه شده، یک Image دریافت کرده و سپس مثلا در سلول‌های یک جدول نمایش داد. اینبار خروجی نهایی ما به شکل زیر خواهد بود:



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


اهداف سیستم‌های توزیع شده

1- Connecting resources and users: مشخص‌ترین و اصلی‌ترین هدف سیستم‌های توزیع شده، اتصال کاربران به منابع و اشتراک منابع بصورت کنترل شده با کارآیی بالا برای کاربران است. به‌صورتیکه کاربران  به آسانی به منابع دسترسی داشته باشند. منظور از منابع، هرچیزی در سیستمهای توزیع شده می‌تواند باشد. منابعی مانند داده‌ها، سخت‌افزارها، فایلها، Componentها، زیرسیستمها و هر چیز دیگری که کاربران می‌توانند بصورت مستقیم و غیر مستقیم به آنها دسترسی داشته باشند. یکی از مهمترین دلایل دسترسی به این هدف، مسائل اقتصادی است. بطور مثال ممکن است ما سیستم خودمان را طوری طراحی کنیم که پردازش‌های بسیار مهم و پیچیده در تعدادی از سخت افزار‌های بسیار پر هزینه انجام شوند. به این صورت ما این سخت افزارها را برای سایر قسمت‌های سیستم، به اشتراک میگزاریم و از طریق دیگر قسمتهای سیستم، دسترسی آن را به کاربران می‌دهیم. در این حالت دیگر نیازی نیست هر قسمت جداگانه در سیستم، سخت افزاری بسیار قوی را برای خودش نیاز داشته باشد. یا زمانیکه ما یک زیرسیستم را یکبار پیاده سازی می‌کنیم و دسترسی آن را به سایر قسمت‌ها میدهیم، سایر سیستمها، دیگر نیازی نیست آن زیرسیستم را برای خودشان پیاده سازی کنند و تنها از زیرسیستم موجود استفاده می‌کنند. دسترسی به این هدف باعث می‌شود تا کاربران به راحتی به تمام منابع موجود در سیستم‌های توزیع شده دسترسی داشته باشند.

2- Distribution transparency: بدلیل پیچیدگی‌های بسیار زیاد در سیستم‌های توزیع شده، شفافیت، کمک بسیار بزرگی به سادگی نحوه تعامل کاربر با سیستم‌های توزیع شده می‌کند. شفافیت در سیستم‌های توزیع شده بدین معنا است که سیستم باید تمام پیچیدگی‌های خود را از دید کاربر مخفی کند و پیاده سازی سیستم بصورت توزیع شده نباید هیچ پیچیدگی را در نحوه تعامل کاربر با سیستم بوجود بیاورد.
درواقع در سیستمهای توزیع شده، درخواست دریافت شده، در بین منابع سیستم توزیع می‌شود. تمام منابع با همکاری که با یکدیگر انجام می‌دهند، درخواست مورد نظر را پردازش کرده و پاسخ لازم را به کاربر ارائه می‌دهند. در این بین ممکن است منابع موجود در سیستم، رفتارهای متفاوتی را داشته باشند؛ مثلا هر زیرسیستم در سخت افزار و سیستم عامل جداگانه‌ای اجرا شود که باعث می‌شود حتی نحوه دستیابی به فایل‌ها یا داده‌های هر زیرسیستم نیز با سایر زیرسیستمها متفاوت باشد. شفافیت در سیستمهای توزیع شده بدین معنا است که یک سیستم توزیع شده باید خود را به‌صورت یک سیستم واحد که در یک سخت افزار ارائه می‌شود، ارائه دهد تا کاربران هیچ نگرانی در نحوه تعامل با سیستم نداشته باشند. شفافیت در سیستمهای توزیع شده جنبه‌های مختلفی دارد که در این قسمت آنها را بررسی می‌کنیم.


جنبه‌های مختلف شفافیت در سیستم‌های توزیع شده

1- Access Transparency: شفافیت در دسترسی به منابع یا مخفی کردن پیچیدگی‌ها و روشهای مختلف دسترسی به منابع. زیر سیستم‌های متفاوت ممکن است در سیستم عامل‌های متفاوتی نیز اجرا شوند و همانطور که می‌دانید هر سیستم عامل ممکن است نحوه دسترسی به منابعش با سایر سیستم عامل‌ها متفاوت باشد. در اینجا ما باید زیر سیستم‌های خود را طوری طراحی کنیم تا این تفاوت‌های در نحوه دسترسی به منابع را از دید کاربر مخفی کنند و این حس را به کاربر بدهد که سیستم، یک روش واحدی را برای دسترسی به منابع دارد.

2- Location Transparency: شفافیت در مکان منابع و مخفی کردن اینکه منابع در سطح شبکه توزیع شده‌اند. این جنبه بیان می‌کند که کاربران نمی‌توانند بگویند که منابع بصورت فیزیکی در سخت افزار‌های متفاوتی توزیع شده‌اند.کاربران هیچ درکی از اینکه ممکن است منابع حتی بصورت جغرافیایی در مکان‌های بسیار دوری از یکدیگر قرار گرفته‌اند، ندارند.

3- Migration Transparency: مخفی کردن اینکه ممکن است مکان منابع تغییر یابند. در یک سیستم توزیع شده ممکن است به هر دلیلی، مکان منابع تغییر کند. بطور مثال ممکن است با افزایش داده‌ها نیاز به افزودن Node جدیدی به سیستم باشد تا قسمتی از داده‌های سیستم از این پس از طریق این Node در دسترس باشند. این جابجایی منابع نباید هیچ تاثیری در نحوه‌ی تعامل کاربر داشته باشد.

4- Relocation Transparency: مخفی کردن اینکه منابع ممکن است در زمان استفاده، تغییر مکان دهند. در این حالت دقیقا در زمانیکه شخص در حال استفاده از منابع است ممکن است منابع جابجا شوند که این جابجایی در زمان استفاده از منابع نباید هیچ تاثیری در نحوه تعامل کاربر با سیستم داشته باشد. بطور مثال زمانیکه دو کاربر از طریق تلفن همراه در حال ارسال اطلاعات برای یکدیگر هستند، جابجایی هر یک از این دو کاربر، نباید تاثیری در جریان ارتباطی آنها داشته باشد.

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

6- Concurrency Transparency: مخفی کردن اینکه ممکن است منابع، بین چند کاربر مشترک باشند. بدلیل بالا بودن تعداد کاربران در سیستمهای توزیع شده، همزمانی در دسترسی به منابع مشترک، بسیار بیشتر اتفاق می‌افتد و این مهم است که هیچ یک از کاربران ندانند که کاربر دیگری بصورت همزمان در حال استفاده از آن داده است.

7- Failure Transparency: مخفی کردن خرابی و بازیابی منابع یک سیستم توزیع شده، از کاربر. در یک سیستم توزیع شده ممکن است منابع به هر دلیلی از دسترس خارج شوند. در این صورت نباید از دسترس خارج شدن منابع و بازیابی آنها هیچ تاثیری در جریان تعامل کاربر با سیستم داشته باشد.


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



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

4- Scalability: مقیاس پذیری سیستم‌های توزیع شده بدین معنی است که با رشد مواردی مانند تعداد پردازش و درخواست یا موقعیت جغرافیایی کاربران، سیستم قادر باشد بدون تاثیر بر جریان تعامل کاربر با سیستم، آنها را پوشش دهد. یعنی بطور مثال زمانیکه تعداد درخواست‌های کاربران سیستم افزایش می‌یابد، با Replicate قسمتهای موجود سیستم در سخت افزار‌های جدید می‌توانیم بار پردازشی را بین تعداد بیشتری از Node‌ها تقسیم کنیم. به این صورت سیستم می‌تواند بدون از دسترس خارج شدن، تعداد بیشتری از درخواستهای کاربران را پوشش دهد. یا زمانیکه قرار است موقعیت‌های جدید جغرافیایی را سیستم پوشش دهد، با اضافه کردن منابع مورد نیاز، در آن موقعیت جغرافیایی، سیستم قادر است نیاز کاربران آن مکان جغرافیایی را برآورده کند.


 تا به این قسمت از سری مقالات سیستم‌های توزیع شده، هدف من این بوده که با چرایی وجود این نوع از سیستم‌ها و نحوه‌ی انتخاب آنها و اهداف سیستم‌های توزیع شده آشنا شوید. در بخش‌های بعد، روشهای مختلف طراحی و پیاده سازی سیستم‌های توزیع شده را مورد بررسی قرار می‌دهیم و می‌بینیم که چه ابزارهایی برای پیاده سازی سیستم‌های توزیع شده در NET. وجود دارند و با توجه به نوع کارآیی هر یک از این ابزارها، آنها را بصورت جداگانه مورد استفاده قرار می‌دهیم تا با مزایا و معایب هریک آشنا شویم. البته این را نیز ذکر کنم که با توجه به تعاریف سیستم‌های توزیع شده، انواع مختلفی از سیستم‌های توزیع شده وجود دارند که ما از قبل با برخی از آنها آشنا هستیم؛ معماری‌هایی مانند Client/Server یا N-Tier  نمونه‌هایی از سیستم‌های توزیع شده هستند که در آنها وظایف، در سخت افزارهای متفاوتی تقسیم شده و ارتباط هر قسمت از طریق سرویس‌هایی که ما پیاده سازی می‌کنیم، صورت می‌پذیرد و به دلیل اینکه مطمئنا همه شما با نحوه طراحی و پیاده سازی آنها و نحوه ارتباط قسمتهای مختلف آنها آشنا هستید، در سری مقالات سیستمهای توزیع شده در NET.، دیگر نیازی به توضیحی در مورد آنها نمی‌باشد. هدف من از قسمت‌های مرتبط با پیاده سازی سیستم‌های توزیع شده، چگونگی تکامل معماری‌هایی مانند N-Tier بوسیله ابزارهایی است که با هدف دستیابی به خصوصیات و اهداف سیستم‌های توزیع شده بوجود آمده‌اند.