اشتراک‌ها
نصب و پیکربندی Microsoft Team Foundation Server 2015
در کار تیمی بر روی پروژه‌های بزرگ ، نرم افزار‌هایی همچون Microsoft Team Foundation Server و ... باعث شده که خیلی از دغدغه‌های تیم‌های توسعه دهنده  نرم افزار برطرف شود . 

برای نصب و پیکربندی :
روش اول : اگر دسترسی به سرور ندارید ، مایکروسافت  برای توسعه دهندگان Net. ،  روی سرور‌های خود Microsoft Team Foundation Server را پیاده کرده است و در این روش ما به ویژوال استودیو  و به یک حساب کاربری مایکروسافت نیاز داریم و با مراجعه به سایت www.visualstudio.com وارد حساب کاربری مایکروسافت خود شده و بر روی Create Account کلیک کرده و اطلاعات مورد نیاز را وارد می‌کنیم .در مرحله بعد طبق تصویر زیر اطلاعات پروژه ای که قرار است توسعه داده شود را وارد می‌کنیم .

که وارد قسمت مدیریتی می‌شویم طبق تصویر زیر :

 در مرحله بعد با مراجعه به ویژوال استودیو پروژه خود را با ویژوال استودیو ایجاد می‌کنیم و پروژه خود را به Team Foundation Serve مایکروسافت متصل خواهیم کرد . با کلیک بر روی Team از منوی ویژوال استودیو Manage Connections را انتخاب کرده و طبق تصویر زیر اطلاعات در خواستی را وارد می‌کنیم و بر روی Connect کلیک می‌کنیم


در مرحله بعد بر روی Solution پروژه راست کلیک کرده و Add Solution to Source Control را انتخاب میکنیم و با مشاهده پنجره Add Solution to Source Control بر روی ok کلیک کرده و با مراجعه به Solution Explorer پروژه مشاهده می‌کنیم که علامت + سبز رنگ در کنار Item‌های پروژه مشاهد می‌شود و این نشان دهنده این می‌باشد که پروژه ما  هنوز به Team Foundation Server مایکروسافت   منتقل نشده است . و برای انتقال بر روی Solution  پروژه راست کلیلک کرده و ..Check In را انتخاب می‌کنیم و در قسمت Comment می‌توانیم توضیحاتی را اضافه کرده و دوباره بر روی Check کلیک کرده و Item‌های پروژه ما منتقل سرور مایکروسافت می‌شود . 

روش دوم : 

در این روش ما نیاز به Windows Server 2012 R2  و  نرم افزار 2015 Microsoft Team Foundation Server  داریم و مراحل را در این روش طبق مقاله زیر جلو می‌بریم که می‌توانید به مراجعه به لینک زیر به مقاله دسترسی داشته باشید . 

Benday TFS 2015 Install guide v1.0

Team Foundation Server 2015 (TFS2015) Installation Guide

  
نصب و پیکربندی Microsoft Team Foundation Server 2015
مطالب
الگوریتم‌های داده کاوی در SQL Server Data Tools یا SSDT - قسمت سوم - الگوریتم‌های Decision trees و Linear Regression

در قسمت قبل با الگوریتم Naive Bayes به عنوان الگوریتمی جهت شروع امر داده کاوی آشنا شدیم. در این قسمت به الگوریتم‌های Decision trees و Linear Regression می‌پردازیم.


مقدمه 

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


چرا الگوریتم درخت تصمیم؟

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


تفسیر الگوریتم

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


شرح نوار ابزار


  • کمبوی مربوط به ،Tree شامل درخت‌های تصمیم مربوط به خروجی‌ها (ویژگی‌هایی که می‌خواهیم پیش بینی کنیم) می‌باشد.
  • Default Expansion اندازه درخت را مشخص می‌کند. به عبارتی مشخص می‌کند که درخت چند سطحی باشد.
  • هیستوگرام تعداد حالات ویژگی قابل پیش بینی را مشخص می‌کند که از طریق آن می‌توان در یک نگاه با توجه به رنگ حالت مورد نظر در هر نود، یک مسیر مشخص را در درخت طی کرد. برای مثال فرض کنید که یک ویژگی دارای 10 حالت باشد که برای شما 5 حالت از این 10 حالت مهمتر است. بنابراین تعداد را روی 5 تنظیم می‌کنیم. مابقی حالات در یک گروه قرار گرفته به رنگ خاکستری نشان داده می‌شوند.
  • کمبوی Background جهت کنترل رنگ پیش زمینه نود‌ها می‌باشد. در حالت پیش فرض، این کمبو تمامی حالات ویژگی مورد پیش بینی را در نظر می‌گیرد. در این حالت رنگ تیره‌تر نود نشان دهنده تعداد موردها در آن نود می‌باشد. هرچه این رنگ تیره‌تر باشد، یعنی موارد بیشتری در آن دسته قرار می‌گیرند. شما همچنین می‌توانید یک حالت خاص از ویژگی مورد پیش بینی را انتخاب کنید. در این حالت رنگ پس زمینه هر نود احتمال پیش بینی با توجه به حالت انتخاب شده را نشان می‌دهد. نود با پس زمینه پر رنگ‌تر احتمال بالاتری با توجه به حالت انتخاب شده دارد. 


آموزش بیش از اندازه

این الگوریتم، درخت را به صورت بازگشتی رشد می‌دهد. درنتیجه گاهی اوقات ممکن است که با یک درخت بزرگ مواجه شوید. این درخت می‌تواند شامل سطح‌ها و شاخه‌های زیادی باشد. بنابراین شامل قوانین زیادی هم خواهد بود. اما در نظر داشته باشید که ارتباط مستقیمی بین کیفیت پیش بینی و اندازه درخت وجود ندارد. حقیقت امر این است، هرگاه که درخت بیش از اندازه عمیق شود، بجای اینکه تعمیم قوانین صورت گیرد، آموزش حالات مختلف نشان داده می‌شود و این خوب نیست. الگوریتم درخت تصمیم مایکروسافت ویژگی دارد به نام forward pruning که رشد درخت را با استفاده از امتیاز بایزین کنترل می‌کند. به عبارتی زمانیکه اطلاعات کافی برای بخش کردن یک نود وجود نداشته باشد، از این امر جلوگیری می‌کند. این کار توسط پارامتر Complexity_Penalty انجام می‌گردد که مقداری اعشاری بین 0 و 1 را می‌گیرد. هرچه مقدار بالاتری به این پارامتر اختصاص داده شود، محدودیت بیشتری برای تقسیم درخت درنظر گرفته می‌شود و بنابراین سایز درخت کوچکتر می‌گردد.


پارامترهای الگوریتم درخت تصمیم

دسترسی به این پارامترها از طریق تب mining models  امکان پذیر می‌باشد. با کلیک بر روی الگوریتم پنجره، properties  آن نمایش داده خواهد شد حال می‌توان به بخش Algorithm Parameters  رفت و پارامترها را مقداردهی کرد. 

Complexity_Penalty : که توضیح آن در بخش "آموزش بیش از اندازه" آورده شد.

Minimum_Support : جهت تعیین مینیمم اندازه هر نود به کار می‌رود. برای مثال اگر مقدار 20 را به آن بدهیم، آنگاه هر تقسیم بندی که منجر به تولید نودهای فرزندی با اندازه کمتر از 20 شود، انجام نمی‌گردد. اغلب در مواردی که مجموعه داده دارای حالات گوناگون زیادی است، می‌توان مقدار این متغیر را بالا برد تا از آموزش بیش از اندازه جلوگیری کرد. پیش فرض این پارامتر 10 می‌باشد.

Score_Method : این پارامتر مشخص می‌کند که از کدام روش برای محاسبه امتیاز جهت بخش بندی درخت استفاده کنیم. سه مقدار 1، 3 و 4 را می‌گیرد. 1 از امتیاز انتروپی استفاده می‌کند، 3 از بایزین k2 و 4 از بایزین Dirichlet equivalent .

Split_Method : سه مقدار 1 تا 3 را می‌گیرد. فرض کنید که وضعیت تحصیل در یک مجموعه داده سه حالت را دارد: دیپلم، لیسانس، فوق لیسانس. اگر مقدار 1 را برای این پارامتر تعیین نماییم آنگاه حالت دودویی برای تقسیم نودها درخت درنظر گرفته می‌شود. یعنی دو حالت دیپلم و غیر دیپلم. حال اگر مقدار 2 را نظر بگیریم آنگاه تقسیم نودها براساس تمامی حالات درنظر گرفته می‌شود؛ در اینجا سه تا. مقدار 3 که مقدار پیش فرض نیز می‌باشد، انتخاب حالت 1 یا 2 را به عهده الگوریتم می‌گذارد.

Maximum_Input_Attributes : ماکزیمم ورودی را می‌توان از این طریق تعیین کرد. اگر تعداد ورودی‌ها بیشتر از این مقدار باشد، آنگاه فقط ورودی‌های مهم درنظر گرفته شده و مابقی نادیده گرفته می‌شوند.


Linear Regression:

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

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

برای شناسایی ایرادات در کد و بهبود کیفیت کدها می‌توانید از ابزارهای دسته‌ی lint استفاده کنید که تعدادی از معروفترین این ابزارها (jslint ,cpplint ,eslint ,nodelint و ...) هستند.
برای نصب چنین ابزاری مثل lint می‌توانید به شکل زیر آن را نصب کنید:
npm install -g eslint
فلگ g در بالا به معنای global بوده و باعث می‌شود که eslint نصب شده، بر روی همه پروژه‌های node.js اجرا شود.
برای اولین بار نیاز است که آماده سازی ابتدایی برای این کتابخانه صورت بگیرد که با دستور زیر قابل انجام است:
eslint --init
سپس تعدادی سوال شخصی از شما پرسیده می‌شود که باید پاسخ داده شود که نمونه‌ای از آن‌را در زیر می‌بینید:
? How would you like to configure ESLint? Answer questions about your style
? Are you using ECMAScript 6 features? Yes
? Are you using ES6 modules? Yes
? Where will your code run? Browser, Node
? Do you use CommonJS? No
? Do you use JSX? No
? What style of indentation do you use? Spaces
? What quotes do you use for strings? Double
? What line endings do you use? Windows
? Do you require semicolons? Yes
? What format do you want your config file to be in? JSON
Successfully created .eslintrc.json file in D:\ali\electron
از این پس با دستور زیر، قادرید کیفیت کدهای خود را بهبود بخشید
eslint .
یا
eslint index.js

می‌توانید این دستور را در در خصوصیت test، در بخش اسکریپت، به شکل زیر وارد کنید:
{
  "name": "electron",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "electron .",
    "test":"eslint ."
  },
  "author": "",
  "license": "ISC"
}
تا از این پس با دستور زیر قابل اجرا باشد:
npm test
بعد از اجرای دستور و طبق سوالات پاسخ داده شده، بهبود کیفیت کد به شکل زیر پاسخ داده می‌شود:
D:\ali\electron\index.js
   1:26  error  Strings must use doublequote                            quotes
  16:8   error  Strings must use doublequote                            quotes
  19:3   error  Expected indentation of 4 space characters but found 2  indent
  22:3   error  Expected indentation of 4 space characters but found 2  indent

✖ 4 problems (4 errors, 0 warnings)

برای نامگذاری فایل‌های js، به جای استفاده از _ از - استفاده کنید. این قاعده‌ای است که گیت هاب در نامگذاری فایل‌های js انجام می‌دهد. یعنی به جای عبارت dotnet_tips.js از عبارت dotnet-tips.js استفاده کنید. خاطرتان جمع باشد که هیچ فایل جاوااسکرپیتی رسمی در الکترون، خارج از این قاعده نامگذاری نشده است.
سعی کنید از جدیدترین قواعد موجود در ES6 استفاده کنید مانند:
let برای تعریف متغیرها
const برای تعریف ثابت ها
توابع جهتی به جای نوشتن عبارت function
استفاده از template string‌ها به جای چسباندن رشته‌ها از طریق عملگر +

برای نامگذاری متغیرها از همان قوانین تعریف شده برای node.js استفاده کنید که شامل موارد زیر است:
- موقعی که یک ماژول را به عنوان یک کلاس استفاده می‌کنید از متد CamelCase استفاده کنید مثل BrowserWindow
- موقعی که از یک ماژول که حاوی مجموعه‌ای از دستورات و api‌ها است استفاده می‌کنید، از قانون mixedCase استفاده کنید مثل app یا globalShortcut
- موقعی که یک api به عنوان خصوصیت یک شیء مورد استفاده قرار میگیرد، از mixedCase استفاده کنید؛ به عنوان مثال win.webContents یا fs.readFileSync
- برای مابقی اشیا مثل دستورات process و یا تگ webview که به شکل متفاوت‌تری مورد استفاده قرار می‌گیرند، از همان عنوان‌های خودشان استفاده می‌کنیم.

موقعی که api جدیدی را می‌سازید و قصد تعریف متدی را دارید بهتر است دو متد برای get و set تعریف شود، نسبت به حالتی که در کتابخانه جی کوئری مورد استفاده قرار می‌گیرد یعنی عبارت‌های زیر
setText('test');
getText();
نسبت به عبارت
.text([text]);
ترجیح داده می‌شوند.
مطالب
الگوهای طراحی API - مکانیزم جلوگیری از پردازش تکراری درخواست ها - Request Deduplication

در فضایی که همواره هیچ تضمینی وجود ندارد که درخواست ارسال شده‌ی به یک API، همواره مسیر خود را همانطور که انتظار می‌رود طی کرده و پاسخ مورد نظر را در اختیار ما قرار می‌دهد، بی‌شک تلاش مجدد برای پردازش درخواست مورد نظر، به دلیل خطاهای گذرا، یکی از راهکارهای مورد استفاده خواهد بود. تصور کنید قصد طراحی یک مجموعه API عمومی را دارید، به‌نحوی که مصرف کنندگان بدون نگرانی از ایجاد خرابی یا تغییرات ناخواسته، امکان تلاش مجدد در سناریوهای مختلف مشکل در ارتباط با سرور را داشته باشند. حتما توجه کنید که برخی از متدهای HTTP مانند GET، به اصطلاح Idempotent هستند و در طراحی آنها همواره باید این موضوع مدنظر قرار بگیرد و خروجی مشابهی برای درخواست‌های تکراری همانند، مهیا کنید.

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

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

Idempotent and Safe HTTP Methods

طبق HTTP RFC، متدهایی که پاسخ یکسانی را برای درخواست‌های همسان مهیا می‌کنند، به اصطلاح Idempotent هستند. همچنین متدهایی که باعث نشوند تغییری در وضعیت سیستم در سمت سرور ایجاد شود، به اصطلاح Safe در نظر گرفته خواهند شد. برای هر دو خصوصیت عنوان شده، سناریوهای استثناء و قابل بحثی وجود دارند؛ به‌عنوان مثال در مورد خصوصیت Safe بودن، درخواست GET ای را تصور کنید که یکسری لاگ آماری هم ثبت می‌کند یا عملیات بازنشانی کش را نیز انجام می‌دهد که در خیلی از موارد به عنوان یک قابلیت شناسایی خواهد شد. در این سناریوها و طبق RFC، باتوجه به اینکه هدف مصرف کننده، ایجاد Side-effect نبوده‌است، هیچ مسئولیتی در قبال این تغییرات نخواهد داشت. لیست زیر شامل متدهای مختلف HTTP به همراه دو خصوصیت ذکر شده می باشد:

HTTP MethodSafeIdempotent
GETYesYes
HEADYesYes
OPTIONSYesYes
TRACEYesYes
PUTNoYes
DELETENoYes
POSTNoNo
PATCHNoNo

Request Identifier as a Solution

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

در اینجا ابتدا مصرف کننده درخواستی با شناسه «۱» را برای پردازش به سرور ارسال می‌کند. سپس سرور که لیستی از شناسه‌های پردازش شده‌ی قبلی را نگهداری کرده‌است، تشخیص می‌دهد که این درخواست قبلا دریافت شده‌است یا خیر. پس از آن، عملیات درخواستی انجام شده و شناسه‌ی درخواست، به همراه پاسخ ارسالی به کلاینت، در فضایی ذخیره سازی می‌شود. در ادامه اگر همان درخواست مجددا به سمت سرور ارسال شود، بدون پردازش مجدد، پاسخ پردازش شده‌ی قبلی، به کلاینت تحویل داده می شود.

Implementation in .NET

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

public class IdempotentId(string id, DateTime time)
{
    public string Id { get; private init; } = id;
    public DateTime Time { get; private init; } = time;
}

هدف از این موجودیت ثبت و نگهداری شناسه‌های درخواست‌های دریافتی می‌باشد. در ادامه واسط IIdempotencyStorage را برای مدیریت نحوه ذخیره سازی و پاکسازی شناسه‌های دریافتی خواهیم داشت:

public interface IIdempotencyStorage
{
    Task<bool> TryPersist(string idempotentId, CancellationToken cancellationToken);
    Task CleanupOutdated(CancellationToken cancellationToken);
    bool IsKnownException(Exception ex);
}

در اینجا متد TryPersist سعی می‌کند با شناسه دریافتی یک رکورد را ثبت کند و اگر تکراری باشد، خروجی false خواهد داشت. متد CleanupOutdated برای پاکسازی شناسه‌هایی که زمان مشخصی (مثلا ۱۲ ساعت) از دریافت آنها گذشته است، استفاده خواهد شد که توسط یک وظیفه‌ی زمان‌بندی شده می تواند اجرا شود؛ به این صورت، امکان استفاده‌ی مجدد از آن شناسه‌ها برای کلاینت‌ها مهیا خواهد شد. پیاده سازی واسط تعریف شده، به شکل زیر خواهد بود:

/// <summary>
/// To prevent from race-condition, this default implementation relies on primary key constraints.
/// </summary>
file sealed class IdempotencyStorage(
    AppDbContext dbContext,
    TimeProvider dateTime,
    ILogger<IdempotencyStorage> logger) : IIdempotencyStorage
{
    private const string ConstraintName = "PK_IdempotentId";

    public Task CleanupOutdated(CancellationToken cancellationToken)
    {
        throw new NotImplementedException(); //TODO: cleanup the outdated ids based on configurable duration
    }

    public bool IsKnownException(Exception ex)
    {
        return ex is UniqueConstraintException e && e.ConstraintName.Contains(ConstraintName);
    }

    // To tackle race-condition issue, the implementation relies on storage capabilities, such as primary constraint for given IdempotentId.
    public async Task<bool> TryPersist(string idempotentId, CancellationToken cancellationToken)
    {
        try
        {
            dbContext.Add(new IdempotentId(idempotentId, dateTime.GetUtcNow().UtcDateTime));
            await dbContext.SaveChangesAsync(cancellationToken);

            return true;
        }
        catch (UniqueConstraintException e) when (e.ConstraintName.Contains(ConstraintName))
        {
            logger.LogInformation(e, "The given idempotentId [{IdempotentId}] already exists in the storage.", idempotentId);
            return false;
        }
    }
}

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

در این پیاده سازی از کتابخانه MediatR استفاده می کنیم؛ در همین راستا برای مدیریت تراکنش ها به صورت زیر می توان TransactionBehavior را پیاده سازی کرد:

internal sealed class TransactionBehavior<TRequest, TResponse>(
    AppDbContext dbContext,
    ILogger<TransactionBehavior<TRequest, TResponse>> logger) :
    IPipelineBehavior<TRequest, TResponse>
    where TRequest : IBaseCommand
    where TResponse : IErrorOr
{
    public async Task<TResponse> Handle(
        TRequest command,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken cancellationToken)
    {
        string commandName = typeof(TRequest).Name;
        await using var transaction = await dbContext.Database.BeginTransactionAsync(IsolationLevel.ReadCommitted, cancellationToken);

        TResponse? result;
        try
        {
            logger.LogInformation("Begin transaction {TransactionId} for handling {CommandName} ({@Command})", transaction.TransactionId, commandName, command);

            result = await next();
            if (result.IsError)
            {
                await transaction.RollbackAsync(cancellationToken);

                logger.LogInformation("Rollback transaction {TransactionId} for handling {CommandName} ({@Command}) due to failure result.", transaction.TransactionId, commandName, command);

                return result;
            }

            await transaction.CommitAsync(cancellationToken);

            logger.LogInformation("Commit transaction {TransactionId} for handling {CommandName} ({@Command})", transaction.TransactionId, commandName, command);
        }
        catch (Exception ex)
        {
            await transaction.RollbackAsync(cancellationToken);

            logger.LogError(ex, "An exception occured within transaction {TransactionId} for handling {CommandName} ({@Command})", transaction.TransactionId, commandName, command);

            throw;
        }

        return result;
    }
}

در اینجا مستقیما AppDbContext تزریق شده و با استفاده از خصوصیت Database آن، کار مدیریت تراکنش انجام شده‌است. همچنین باتوجه به اینکه برای مدیریت خطاها از کتابخانه‌ی ErrorOr استفاده می کنیم و خروجی همه‌ی Command های سیستم، حتما یک وهله از کلاس ErrorOr است که واسط IErrorOr را پیاده سازی کرده‌است، یک محدودیت روی تایپ جنریک اعمال کردیم که این رفتار، فقط برروی IBaseCommand ها اجرا شود. تعریف واسط IBaseCommand به شکل زیر می‌باشد:

 
/// <summary>
/// This is marker interface which is used as a constraint of behaviors.
/// </summary>
public interface IBaseCommand
{
}

public interface ICommand : IBaseCommand, IRequest<ErrorOr<Unit>>
{
}

public interface ICommand<T> : IBaseCommand, IRequest<ErrorOr<T>>
{
}

public interface ICommandHandler<in TCommand> : IRequestHandler<TCommand, ErrorOr<Unit>>
    where TCommand : ICommand
{
    Task<ErrorOr<Unit>> IRequestHandler<TCommand, ErrorOr<Unit>>.Handle(TCommand request, CancellationToken cancellationToken)
    {
        return Handle(request, cancellationToken);
    }

    new Task<ErrorOr<Unit>> Handle(TCommand command, CancellationToken cancellationToken);
}

public interface ICommandHandler<in TCommand, T> : IRequestHandler<TCommand, ErrorOr<T>>
    where TCommand : ICommand<T>
{
    Task<ErrorOr<T>> IRequestHandler<TCommand, ErrorOr<T>>.Handle(TCommand request, CancellationToken cancellationToken)
    {
        return Handle(request, cancellationToken);
    }

    new Task<ErrorOr<T>> Handle(TCommand command, CancellationToken cancellationToken);
}

در ادامه برای پیاده‌سازی IdempotencyBehavior و محدود کردن آن، واسط IIdempotentCommand را به شکل زیر خواهیم داشت:

/// <summary>
/// This is marker interface which is used as a constraint of behaviors.
/// </summary>
public interface IIdempotentCommand
{
    string IdempotentId { get; }
}

public abstract class IdempotentCommand : ICommand, IIdempotentCommand
{
    public string IdempotentId { get; init; } = string.Empty;
}

public abstract class IdempotentCommand<T> : ICommand<T>, IIdempotentCommand
{
    public string IdempotentId { get; init; } = string.Empty;
}

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

[HttpPost]
public async Task<ActionResult<long>> Register(
     [FromBody] RegisterUserCommand command,
     [FromIdempotencyToken] string idempotentId,
     CancellationToken cancellationToken)
{
     command.IdempotentId = idempotentId;
     var result = await sender.Send(command, cancellationToken);

     return result.ToActionResult();
}

در اینجا از همان Command به عنوان DTO ورودی استفاده شده‌است که وابسته به سطح Backward compatibility مورد نیاز، می توان از DTO مجزایی هم استفاده کرد. سپس از طریق FromIdempotencyToken سفارشی، شناسه‌ی درخواست، دریافت شده و بر روی command مورد نظر، تنظیم شده‌است.

رفتار سفارشی IdempotencyBehavior از ۲ بخش تشکیل شده‌است؛ در قسمت اول سعی می شود، قبل از اجرای هندلر مربوط به command مورد نظر، شناسه‌ی دریافتی را در storage تعبیه شده ثبت کند:

internal sealed class IdempotencyBehavior<TRequest, TResponse>(
    IIdempotencyStorage storage,
    ILogger<IdempotencyBehavior<TRequest, TResponse>> logger) :
    IPipelineBehavior<TRequest, TResponse>
    where TRequest : IIdempotentCommand
    where TResponse : IErrorOr
{
    public async Task<TResponse> Handle(
        TRequest command,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken cancellationToken)
    {
        string commandName = typeof(TRequest).Name;

        if (string.IsNullOrWhiteSpace(command.IdempotentId))
        {
            logger.LogWarning(
                "The given command [{CommandName}] ({@Command}) marked as idempotent but has empty IdempotentId",
                commandName, command);
            return await next();
        }

        if (await storage.TryPersist(command.IdempotentId, cancellationToken) == false)
        {
            return (dynamic)Error.Conflict(
                $"The given command [{commandName}] with idempotent-id [{command.IdempotentId}] has already been received and processed.");
        }

        return await next();
    }
}

در اینجا IIdempotencyStorage تزریق شده و در صورتی که امکان ذخیره سازی وجود نداشته باشد، خطای Confilict که به‌خطای 409 ترجمه خواهد شد، برگشت داده می‌شود. در غیر این صورت ادامه‌ی عملیات اصلی باید اجرا شود. پس از آن اگر به هر دلیلی در زمان پردازش عملیات اصلی،‌ درخواست همزمانی با همان شناسه، توسط سرور دریافت شده و پردازش شود، عملیات جاری با خطای UniqueConstaint برروی PK_IdempotentId در زمان نهایی سازی تراکنش جاری، مواجه خواهد شد. برای این منظور بخش دوم این رفتار به شکل زیر خواهد بود:

internal sealed class IdempotencyExceptionBehavior<TRequest, TResponse>(IIdempotencyStorage storage) :
    IPipelineBehavior<TRequest, TResponse>
    where TRequest : IIdempotentCommand
    where TResponse : IErrorOr
{
    public async Task<TResponse> Handle(
        TRequest command,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(command.IdempotentId)) return await next();

        string commandName = typeof(TRequest).Name;
        try
        {
            return await next();
        }
        catch (Exception ex) when (storage.IsKnownException(ex))
        {
            return (dynamic)Error.Conflict(
                $"The given command [{commandName}] with idempotent-id [{command.IdempotentId}] has already been received and processed.");
        }
    }
}

در اینجا عملیات اصلی در بدنه try اجرا شده و در صورت بروز خطایی مرتبط با Idempotency، خروجی Confilict برگشت داده خواهد شد. باید توجه داشت که نحوه ثبت رفتارهای تعریف شده تا اینجا باید به ترتیب زیر انجام شود:

services.AddMediatR(config =>
{
   config.RegisterServicesFromAssemblyContaining(typeof(DependencyInjection));

   // maintaining the order of below behaviors is crucial.
   config.AddOpenBehavior(typeof(LoggingBehavior<,>));
   config.AddOpenBehavior(typeof(IdempotencyExceptionBehavior<,>));
   config.AddOpenBehavior(typeof(TransactionBehavior<,>));
   config.AddOpenBehavior(typeof(IdempotencyBehavior<,>));
});

به این ترتیب بدنه اصلی هندلرهای موجود در سیستم هیچ تغییری نخواهند داشت و به صورت ضمنی و انتخابی، امکان تعیین command هایی که نیاز است به صورت Idempotent اجرا شوند را خواهیم داشت.

References

https://www.mscharhag.com/p/rest-api-design

https://www.manning.com/books/api-design-patterns

https://codeopinion.com/idempotent-commands/

مطالب
توابع تعمیم یافته در #C
از توابع تعمیم یافته می‌توان برای توسعه توابع هر کلاس یا اینترفیسی استفاده کرد. یعنی می‌‎توان یک تابع را به هر کلاسی اضافه کرد.

قبل از C# 3.0  فقط می‌شد یک کلاس را از طریق ارث‌بری از آن توسعه داد و به کلاس مهروموم شده یاSealed  نیز نمی‌شد تابعی افزود که البته ممکن است بگویید که این کار قوانین شیءگرایی را نقض می‌کند اما در پاسخ باید گفت که توابع تعمیم یافته به اعضاء خصوصی کلاسی که تعمیم می‌یابد، دسترسی ندارند.

تعمیم یک کلاس در خارج از بدنه کلاس انجام می‌شود و این کار می‎تواند در فضای نام همان کلاس یا خارج از آن انجام شود و شکل کلی آن به صورت زیر است

 public static class  ExtendingClassName
 {
      public static ReturnType MethodName(this   ExtendedMethod  arg)
      {
           //دستورات درون متد
          Return ReturnType;
      } 
 } 
توجه کنید که:
  1. کلاس توسعه‌دهنده و تابع توسه‌دهنده باید استاتیک باشند.
  2. در داخل آرگومان تابع، کلمه کلیدی this  استفاده می‌‎‎شود.
  3. بعد از this عنوان کلاسی که قصد توسعه آن را داریم، ذکر می‌کنیم.
  4. در هرجا که خواستیم از قابلیت تعمیم داده شده استفاده کنیم باید فضای نام مربوط به آن را ذکر کنیم.
  5. با کلمه کلیدی static  نمی‎توان کلاسی با متدهایvirtual  ، abstract  و override را توسعه داد. 

مثلا اگر قصد داریم به کلاس String  تابع AddPrefix  را اضافه کنیم به این شکل عمل می‌‎کنیم :

public static class  ExtendingString
{
  public static  string  AddPrefix(this   string  arg)
  {
     return String.Format("prefix{0}",arg);
  } 
} 

که نحوه استفاده از آن به شکل زیر است: 

string  s = "Student";
Console.WriteLine(s.AddPrefix());

و خروجی آن نمایش prefixStudent است. 

اگر بخواهیم عبارت پیشوند را از طریق آرگومان ارسال کنیم به این شکل عمل می‎کنیم: 

public static class ExtendingString   
{
    public static string AddPrefix(this   string arg, string prefix)
    {
         return String.Format("{0}{1}", prefix, arg);
     }
} 

که نحوه استفاده از آن به شکل زیر است: 

var  s = "Student";
Console.WriteLine(s.AddPrefix("tbl"));

و خروجی آن نمایش tblStudent است.

به عنوان مثال دوم کلاس زیر را در نظر بگیرید: 

public class Test 
{
     public int AddOne(int val)
     {
         return val + 1;
     }
  }

اگر بخواهیم کلاس فوق را توسعه داده و متد دیگری مثلا با عنوان AddTwo اضافه کنیم، کلاس توسعه دهنده را به این شکل می‌نویسیم:

public static class ExtendingTest   
{
     public static int AddTwo(this   Test  arg, int val)
     {
         return val + 2;
     }
}

و روش استفاده از آن بصورت زیر است: 

static void Main(string[] args)
{
       var x = new Test();
       Console.WriteLine(x.AddOne(10));
       Console.WriteLine(x.AddTwo(10));
       Console.Read();
}
نظرات مطالب
سفارشی سازی Header و Footer در PdfReport
در مورد جزئیات نحوه مقدار دهی فیلدهای این نوع قالب‌ها مراجعه کنید به مطلب «ساخت یک گزارش ساز به کمک iTextSharp و Open Office».
بعد از آشنایی، متد GetITextSharpImageFromAcroForm تعریف شده در PdfReport هم راه ساده‌تر پر کردن این نوع فیلدها است.
 public static iTextSharp.text.Image GetITextSharpImageFromAcroForm(
                            this PdfWriter pdfWriter,
                            string pdfTemplateFilePath,
                            IList<CellData> data,
                            Action<IList<CellData>, AcroFields, PdfStamper> onFillAcroForm,
                            IList<iTextSharp.text.Font> fonts,
                            int pageNumber = 1)
یک چنین امضایی داره تعریف شده در فضای نام PdfRpt.Core.Helper. 
مطالب
ایجاد چارت سازمانی تحت وب #4 - آخر
نما : Layout
در یک نمودار یا چارت سازمانی در حد امکان شاخه‌ها همواره در کنار هم و جمع و جور رسم میشوند. در مثال زیر نود u-Node 1 و u-Node 3 دارای زیر شاخه نبوده ، بنابراین نیازی به فضای زیرین جهت نمایش ندارند. جهت مشاهده این فضا میتوانید خط مشخص شده در کد را فعال نمائید و تفاوت فضای مورد نیاز و ایجاد شده را ببینید.
دو درخت نمودار متفاوت در کنار هم رسم شده اند. هیچ همپوشانی بین درختان رسم شده وجود ندارد ( بنابراین نود Root 2 روی نود u-Node 3  رسم نشده است.


var o = new orgChart();

o.addNode( 0, '', '', 'Root 1');
o.addNode( 1,  0, 'u', 'u-Node 1');
o.addNode( 2,  0, 'u', 'u-Node 2');
o.addNode( 3,  0, 'u', 'u-Node 3');
o.addNode( 4,  0, 'u', 'u-Node 4');
//میتوانید خط زیر را فعال نمائید تا تفاوت فضای ایجاد شده و مورد نیاز را مشاهده نمائید.
 //o.addNode( 9, 3, 'u', 'EXTRA', 0, '', '#CC0000', '#FF0000'); o.addNode(11, 2, 'l', 'l-Node'); o.addNode(12, 2, 'u', 'u-Node'); o.addNode(13, 2, 'u', 'u-Node'); o.addNode(14, 2, 'r', 'r-Node'); o.addNode(20, 4, 'l', 'l-Node'); o.addNode(21, '', '', 'Root 2'); o.drawChart('c_layout');
یک مثال کامل :
نمونه زیر یک مثال کامل میباشد. انواع اتصالهای تو در تو چندگانه در این نمونه استفاده شده است .


این هم کد نمونه فوق :

var o = new orgChart();

o.setFont('Arial', 18);
o.addNode( 0, '', '', 'President', 1);

o.setFont('Arial', 12);
o.addNode('',  0, 'l', 'Control');
o.addNode('',  0, 'l', 'Secretary');
o.addNode('',  0, 'l', 'Marketing');
o.addNode('',  0, 'l', 'Human resources');

o.setColor('#99CC99', '#CCFFCC');
o.addNode(12,  0, 'r', 'Facility');
o.addNode('', 12, 'r', 'IT');
o.addNode('', 12, 'r', 'Resource planning');

o.setFont('Arial', 18);
o.setColor('#CCCC66', '#FFFF99');
o.addNode(20,  0, 'u', 'Projects', 1);

o.setFont('Arial', 12);
o.addNode('', 20, 'r', 'Management');
o.addNode('', 20, 'r', 'Finance');
o.addNode('', 20, 'l', 'Development');
o.addNode('', 20, 'l', 'Maintenance');
o.addNode('', 20, 'l', 'Specials');

o.setColor('#CC4950', '#FF7C80');
o.setFont('Arial', 18);
o.addNode(30,  0, 'u', 'Specials', 1);

o.setFont('Arial', 12);
o.addNode(31, 30, 'l', 'Management');
o.addNode('', 30, 'l', 'Communication');
o.addNode(33, 30, 'r', 'Development');
o.addNode(34, 33, 'r', 'Maintenance');
o.addNode('', 33, 'r', 'Special A');
o.addNode('', 33, 'r', 'Special B');
o.addNode('', 33, 'r', 'Advice');
o.addNode('', 30, 'l', 'Taskforce');

o.setColor('#CC9966', '#FFCC99');
o.setFont('Arial', 18);
o.addNode(40,  0, 'u', 'Programming', 1);

o.setFont('Arial', 12);
o.addNode(41, 40, 'l', 'Management');
o.addNode(42, 40, 'l', 'Finance');
o.addNode('', 40, 'r', 'Assessment');
o.addNode('', 40, 'r', 'Coding team');
o.addNode('', 40, 'r', 'Quality control');

o.drawChart('c_ex1', '', true);
اضافه کردن تصویر به نودها :
شما میتوانید به نودها تصویر دلخواه خود را نیز اضافه نمائید. تصاویر بصورت عمودی قرار خواهند گرفت و در صورتی که بزرگ باشند تغییر اندازه خواهند داد. ( فراخوانی تابع setSize قبل از اضافه کردن عکس در این مثال )

کدهای مثال فوق :

var o = new orgChart();

o.setSize(120, 60);

o.setFont('Arial', 18);
o.addNode( 1, '', '', 'Icon smiley', 0, '', '', '', '', 'pic/smiley.gif');
o.addNode( 2, '', '', 'This is a tree', 0, '', '', '', '', 'pic/tree.jpg');
o.addNode( 3, 2, 'u', 'This is a tree');
o.addNode( 4, '', '', 'Right Top smiley', 0, '', '', '', '', 'pic/smiley.gif', 'rt');
o.addNode( 5, '', '', 'Center bottom smiley', 0, '', '', '', '', 'pic/smiley.gif', 'cb');

o.drawChart('c_img');

یک مثال دیگر از استفاده تصاویر در چارت :


var o = new orgChart();

o.setSize(60, 110);

o.setFont('Arial', 12);
o.addNode( 1, '', '', 'Hominidae');
o.addNode( 10, 1, 'l', 'Hominidae');
o.addNode( 11, 10, 'l', 'Hominini');
o.addNode( 12, 10, 'r', 'Gorillini');
o.addNode( 20, 1, 'r', 'Ponginae');
o.addNode( '', 11, '', 'Homo Sapiens', '', '', '', '', '', 'pic/homo.jpg', 'ct');
o.addNode( '', 11, '', 'Pan', '', '', '', '', '', 'pic/pan.jpg', 'ct');
o.addNode( '', 12, '', 'Gorilla', '', '', '', '', '', 'pic/gorilla.jpg', 'ct');
o.addNode( '', 20, '', 'Pongo', '', '', '', '', '', 'pic/pongo.jpg', 'ct');

o.drawChart('c_img2', 'c');
تبدیل تصویر از فرمت jpg به تصویر با فرمت png :
عدم امکان استفاده از مرورگر IE تا نسخه 8 ، چرا که IE  هیچ پشتیبانی از toDataURL در excanvas.js  را انجام نمیدهد.
شما میتوانید از توابع استاندارد canvas در جهت تبدیل محتویات canvas به تصویر استاتیک استفاده نمائید. برای اینکه بتوانید این کار را تست نمائید باید کد ذیل را در همان صفحه ای که کد ( یک مثال کامل ) را استفاده کردید درج نمائید و در این صورت با کلیک بر روی لینک اول میتوانید یک تصویر با فرمت png در یک صفحه جدید از نمودار خود بدست بیاورید و یا با کلیک بر روی لینک دوم یک تصویر را دانلود نمائید.
<script type="text/javascript">
function openAsPng(id){
        window.open(document.getElementById(id).toDataURL("image/png"));
}
function saveAsPng(id){
var img = document.getElementById(id).toDataURL("image/png");
        document.location.href = img.replace("image/png", "image/octet-stream");
}
</script>

<a href = "javascript:openAsPng('c_ex1');">Click here to open the image as png in a new window</a><BR>
<a href = "javascript:saveAsPng('c_ex1');">Click here to save the image as png</a><BR>

نمایش چارت فقط بصورت یک تصویر :

برای اینکه بتوانید یک چارت ایجاد شده از این روش را فقط بصورت یک تصویر نمایش دهید باید عمل تبدیل به عکس را بلافاصله پس از رسم نمودار در canvas انجام دهید بدین صورت که در کد ذیل مشاهده مینمائید:
<canvas id="c_pngchart" width="1" height="1">Your browser does not support canvas!</canvas>

<img id="pngchart">
<script type="text/javascript">
var o = new orgChart();
o.addNode(0, '', '', 'Root');
o.addNode(1, 0, 'u', 'u-Node 1');
o.addNode(2, 0, 'u', 'u-Node 2');
o.drawChart('c_pngchart', '', true);
var canvas = document.getElementById('c_pngchart');
document.getElementById("pngchart").src = canvas.toDataURL("image/png");
// The html keyword "hidden" doesn't work in IE, so resize the canvas to NUL
canvas.height = 0;
canvas.width = 0;
</script>

همه لینکهایی که در نودها ایجاد شده است غیرفعال شده و از کار می‌افتند. بنابراین برای انجام این کار ،یک المنت تصویر در صفحه خود ایجاد ، نمودار را در canvas رسم نموده ، نمودار را به تصویر تبدیل نموده و آن را به المنت تصویر مقید میکنیم و در آخر canvas مخفی میکنیم. برای این منظور از کلید واژه hidden  استفاده میکنیم که در IE این کلمه باز قابل شناسایی نبوده و باید از روش تخصیص اندازه طول و عرض صفر 0 استفاده شود یعنی width=0 , height=0

تصویر فوق ایجاد شده کد مورد نظر میباشد.

تغییر اندازه پویا :
اگر بخواهید بصورت پویا اندازه canvas را تغییر دهید ، نمودار شما ناپدید میشود و پس از تغییر اندازه ، نمودار پاک خواهد شد.
برای رسم نمودار باید دوباره از توابع drawChart() یا redrawChart()   استفاده نمائید.
برای رسم نودها نیازی به تعریف دوباره آنها نمیباشد ( مخصوصا در مثالی که در این صفحه برای شما ارائه شده است )
تابع ()drowChart تمامی نودها را در زمان رسم دوباره جاگذاری میکند ، در صورتی که اگر شما میدانید چارت شما به غیر از اندازه هیچ تغییر دیگری نداشته میتوانید با فراخوانی تابع redrawChart  یک کپی از همان چارت را که در حافظه canvas وجود دارد را رسم نمائید.
از تمامی دوستان خوبم تشکر میکنم که این مطلب را دنبال نمودند . ما را از نظرات خوب و سازنده خود بی نصیب نفرمائید.

اشتراک‌ها
ابزار CsharpRepl

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

ابزار CsharpRepl
نظرات مطالب
به اشتراک گذاری داده ها بین کنترلرها در AngularJs
بله. اگر متدی را به کنترلر 2 اضافه کنید و سپس یک تگ لینک در view برای فراخوانی این متد، به صورت زیر :
sampleApp.controller('controller2', function ($scope, BookData) {
    $scope.books = BookData;
    $scope.change = function(){
         BookData[0].name = 'change this book';
    }
});

 و view :
<div ng-controller="controller2">
        <p>Data from controller2</p>
        <table>
            <tr ng-repeat="book in books">
                <td>
                    {{book.code}}
                </td>
                <td>
                    {{book.name}}
                </td>
            </tr>
        </table>
<a href="javascript:void(0)" ng-click="change();" >Click here to change book1</a>
    </div>
میبینید که در کنترلر شماره 1 هم تغییر میکند.