مطالب
آموزش سیلورلایت 4 - قسمت‌های 1 تا 5

مجموعه‌ی نسبتا مفصلی را از مباحث Silverlight 4 آماده کرده‌ام (حدود 27 قسمت) که سعی می‌کنم هر یکی دو روزی یکبار چند قسمت از آن‌را اینجا قرار دهم.

فصل عنوان فایل مرتبط
1 معرفی Silverlight +
2 بررسی ساختار یک پروژه‌ی Silverlight در VS.NET +
3 معرفی XAML +
4 آشنایی با طرح بندی رابط کاربر در Silverlight +
5 آشنایی با نحوه‌ی مدیریت رویدادها در Silverlight +
دریافت تمام قسمت‌ها از GitHub  
مطالب
آشنایی با الگوی M-V-VM‌ - قسمت دوم

در این قسمت، یک مثال ساده، بدون استفاده از فریم ورک‌های متداول M-V-VM بررسی شده است. در قسمت‌های بعدی با یک سری از فریم ورک‌های موجود آشنا خواهیم شد.

فهرست مطالب:
فصل 2- معرفی مثالی مقدماتی از پیاده سازی الگوی M-V-VM در WPF
  • مقدمه
  • ساختار پوشه‌های یک برنامه‌ی MVVM
  • معرفی برنامه‌ی فصل
  • مدل برنامه
  • View برنامه
  • ViewModel برنامه

دریافت قسمت دوم
دریافت مثال قسمت دوم


مطالب
Cookie - قسمت اول

مقدمه 

مدت زیادی است که کوکی‌ها در عرصه وب نقش مهمی ایفا میکنند، اما متاسفانه مفهوم روشن و واضحی از آن و نحوه کار آن در اختیار اکثر کاربران و توسعه دهندگان وب نیست. شاید اولین مشکل ناشی از سوءتفاهم‌های بسیاری باشد که درباره کوکی وجود دارد. مثلا برخی آن را ابزاری صرفا برای جاسوسی از کاربران اینترنتی میدانند. برخی دیگر، از آنها و نحوه کارکردشان کلا صرفنظر میکنند. مشکل دیگری که در رابطه با کوکی‌ها میتوان برشمرد، عدم وجود رابط کاربری مناسب برای بررسی و مدیریت کوکی هاست. اما با وجود این مشکلات و برخی دیگر امروزه کوکی‌ها جزئی بسیار مهم در وب هستند که درصورت حذفشان، بسیاری از وب سایتها و برنامه‌های مبتنی بر وب از کار خواهند افتاد.
یک کوکی (cookie به معنی شیرینی یا کلوچه! که با عناوین دیگری چون Http Cookie و Web Coockie و Browser Cookie نیز شناخته میشود)، به داده‌های ارسالی از یک وب سرور (که معمولا بصورت داده‌های متنی کدگذاری شده هستند) اطلاق میشود که در مخزنی مخصوص در مرورگر کاربر به هنگام بازدید از یک سایت ذخیره میشود. وقتیکه کاربر سایت مذبور را در آینده دوباره مرور کند، این داده‌های ذخیره شده توسط مروگر به وب سرور ارسال میشود تا مثلا فعالیتهای قبلی کاربر مورد بررسی قرار گیرد. کوکی‌ها برای فراهم کردن مکانیزمی قابل اعتماد جهت ذخیره فعالیتهای قبلی یا آخرین وضعیت کاربر در یک وب‌سایت طراحی شده‌اند. با اینکه کوکی‌ها دسترسی بسیار محدودی در سمت کلاینت دارند (تقریبا هیچ دسترسی‌ای به هیچیک از منابع سیستم کاربر ندارند) اما با پیگیری هوشمند و هدفمند برخی از آنها میتوان به داده‌هایی از تاریخچه فعالیتهای کاربر در یک مرورگر و سایت خاص دست یافت که به نوعی نقض حریم شخصی کاربران به حساب می‌آید.

نکته: درواقع میتوان گفت که از کوکی به نوعی برای فراهم کردن "حافظه" موقت برای مرورگرها در ارتباط با وب سرورها استفاده میشود.

پروتوکل HTTP که برای تبادل داده‌ها میان مرورگر و وب سرور در بارگذاری صفحات وب استفاده میشود، پروتوکلی بدون حالت یا وضعیت (state-less) است. بدین معنی که به محض ارسال داده‌های یک صفحه وب به سمت مرورگری که آنرا درخواست کرده، وب سرور هیچ چیزی از این تبادل داده را ذخیره و نگهداری نمیکند. بنابراین در درخواست‌های دوباره و سه باره و ... بعدی، وب سرور با آنها همچون اولین درخواست برخورد کرده و رفتاری کاملا یکسان در برخورد با این درخواستها نشان خواهد و دقیقا همان داده‌ها را به سمت مرورگر ارسال خواهد کرد.
این رفتار در موارد زیادی میتواند دردسرساز باشد. مثلا وب سرور نمیتواند بفهمد که یک کاربر لاگ‌آن (LogOn یا همان SignIn) کرده و یا اینکه یکسری تنظیمات شخصی اعمال کرده است، چون جایی برای ذخیره  و نگهداری این حالات یا وضعیتها در پروتوکل HTTP وجود ندارد. خوشبختانه وجود کوکی‌ها یکی از بهترین راه حل‌ها برای رفع مشکلات اشاره شده است.

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

تاریخچه
واژه Cookie از عبارت Magic Cookie برگرفته شده است. به طور خلاصه Magic Cookie به مجموعه‌ای از داده‌های «بدون نیاز به تغییر» میگویند که بین برنامه‌هایی که در ارتباط با یکدیگرند، ردوبدل میشود. داده‌های موجود در Magic Cookieها معمولا برای سمت دریافت کننده مفهوم خاصی ندارد و به نوعی برای ذخیره وضعیت «سمت دریافت کننده» در «برنامه ارسال کننده» و استفاده از آن در ارتباطهای بعدی کاربرد دارد. به بیان دیگر حالت یا وضعیت یا تنظیمات «برنامه مقصد در برنامه مبدأ» با استفاده از کوکی در «خود برنامه مقصد» نگهداری میشود!
در سال 1994 آقای Lou Montulli هنگامیکه در شرکت Netscape Communications در توسعه یک برنامه تجاری تحت وب مشارکت داشت، ایده استفاده از این تکنولوژی را در ارتباطات وب ارائه داد که بعدها عنوان HTTP Coockie را بخود گرفت. برای اولین بار از این کوکی‌ها در نسخه 0.9 بتای نت اسکیپ که در همان سال ارائه شد، پشتیبانی و استفاده شد. مروگر IE هم در سال 1995 و در نسخه 2.0 آن، پشتیبانی از کوکی را آغاز کرد. آقای مانتولی پتنت (Patent) تکنولوژی کوکی را در سال 1995 ارائه داد اما ثبت نهایی آن به دلیل مشکلات و مباحث حریم شخصی کاربران تا سال 1998 طول کشید.

کوکی واقعا چیست؟
یک کوکی در واقع یک فایل متنی کوچک است که در قسمتی مشخص از کامپیوتر کلاینت که توسط مرورگر تنظیم شده است، ذخیره میشود. این فایل متنی کوچک حاوی اطلاعات زیر است:
- یک جفت داده نام-مقدار (name-value pair) که داده اصلی کوکی را نگهداری میکند.
- خاصیتی برای مشخص کردن زمان انقضای کوکی (پس از این زمان این فایل متنی کوچک از درون مرورگر حذف خواهد شد)
- خاصیتهایی برای مشخص کردن محدوده‌ها و مسیرهای قابل دسترسی کوکی
- خاصیتهایی برای تعیین نحوه تبادل داده‌های کوکی و نوع دسترسی به این داده‌ها (مثلا الزام به استفاده از پروتوکلهای امن)
 
انواع کوکی
بطور کلی دو نوع اصلی کوکی وجود دارد:
1. Session cookie
از این نوع کوکی برای نگهداری موقت داده‌ها نظیر داده‌های مربوط به وضعیت یک کاربر، تنها در زمان مرور وب سایت استفاده میشود. معمولا با بستن مرورگر (یا اتمام سشن) این کوکی‌ها ازبین میروند.
2. Persistent cookie 
برخلاف کوکی‌های سشنی این نوع کوکی‌ها در سیستم کلاینت به صورت دائمی ذخیره میشوند. معمولا دارای یک تاریخ انقضا هستند که پس از آن از بین میروند. در طول زمان حیات این کوکی ها، مرورگرها داده‌های ذخیره شده در آنها را با توجه به تنظیمات درونشان در هر درخواست به سمت وب سرور سایت مربوطه ارسال میکنند.

با توجه به کاربردهای فراوان کوکی، دسته بندیها و انواع دیگری از کوکی را هم میتوان نام برد. مانند انواع زیر:
Secure cookie 
معمولا به کوکی‌هایی که خاصیت امن (Secure Attribute) در آنها فعال است این عنوان اطلاق میشود. این نوع از کوکی‌ها تنها قابل استفاده در ارتباطهای امن (با استفاده از پروتوکل HTTPS) هستند. این خاصیت اطمینان میدهد که داده‌های موجود هنگام تبادل بین سرور و کلاینت همواره کدگذاری میشود.
HttpOnly cookie
در این کوکی‌ها خاصیت HttpOnly فعال است، که موجب میشود که از آنها تنها در ارتباطات از نوع HTTP و HTTPS بتوان استفاده کرد. در سایر روشهای دسترسی به کوکی (مثلا از طریق برنامه نویسی سمت کلاینت) نمیتوان به محتوای این نوع از کوکی‌ها دسترسی پیدا کرد.
Third-party cookie 
این نوع از کوکی‌ها در مقابل کوکی‌های First party (یا شخص اول) وجود دارند. کوکی‌های شخص اول توسط وب سایت جاری تولید شده اند، یعنی نشانی دامِین این کوکیها مربوط به سایت جاری است. منظور از سایت یا دامین جاری، سایتی است که آدرس آن در نوار آدرس مرورگر نشان داده میشود. کوکی‌های Third party (یا شخص سوم) به آن دسته از کوکی‌ها میگویند که توسط دامین یا وب سایت دیگری غیر از وب سایت جاری تولید و مدیریت میشوند. مثلا کوکی‌های مربوط به سایتهای تبلیغاتی. البته در مرورگرهای مدرن این نوع از کوکی‌ها به دلیل مشکلات امنیتی و نقض حریم شخصی کاربران عموما بلاک میشوند.
Supercookie 
یک سوپرکوکی به آن دسته از کوکی‌ها گفته میشود که خاصیت دامین آنها به یک پسوند خیلی کلی مثل com. تنظیم شده باشد. به دلیل مسائل امنیتی بیشتر مرورگرهای مدرن تمامی انواع این سوپرکوکی‌ها را بلاک میکنند. امروزه لیستی از این پسوندهای کلی با عنوان Public Suffix List موجود است که در مرورگرهای مدرن برای کنترل کوکی‌ها استفاده میشود.
 
موارد استفاده از کوکی
- مدیریت جلسات (Session Management):
از کوکی میتوان برای نگهداری داده‌های مربوط به یک کاربر در بازدید از صفحات سایت و تعامل با آنها (که ممکن است در زمانهای مختلف رخ دهد) استفاده کرد. یکی از موارد بسیار پرکاربرد در این زمینه، کوکی‌های تعیین اعتبار یک کاربر است که پس از ورود به سایت در هر درخواست توسط مرورگر به سمت سرور ارسال میشود. 
مثال دیگری در مورد این کاربرد نگهداری از داده‌های سبد خرید یک کاربر است. این داده‌ها را میتوان قبل از تسویه نهایی درون یک کوکی ذخیره کرد. معمولا در تمام این موارد از یک کلید منحصر به فرد که در سمت سرور تولید شده و درون کوکی به همراه سایر اطلاعات ذخیره میشود، برای تعیین هویت کاربر استفاده میشود. 
- شخصی سازی (Personalization):
یکی دیگر از موارد پرکاربرد کوکیها ذخیره تنظیمات یا داده‌های مرتبط با شخصی سازی تعامل کاربر با سایت است. مثلا میتوان تنظیمات مربوط به استایل یک سایت یا زبان انتخابی برای یک کاربر مشخص را درون کوکی در سمت کلاینت ذخیره کرد. سایتهای بزرگ معمولا از این روش برای ذخیره تنظیمات استفاده میکنند، مثل گوگل و ویکیپدیا. همچنین میتوان از کوکی برای ذخیره شناسه آخرینکاربری که در سایت لاگ‌آن کرده استفاده کرد تا در مراجعه بعدی به عنوان اولین انتخاب در صفحه ورود به سایت به کاربر نمایش داد (هرچند این کار معایب خودش را دارد).
- پیگیری یا ردیابی (Tracking):
از کوکی‌ها میتوان برای پیگیری بازدیدهای یک کاربر در یک کلاینت از یک سایت یا مسیری به خصوص از یک سایت بهره برد. بدین صورت که برای هر کاربر یک کد شناسایی منحصر به فرد تولید شده و درون کوکی مخصوص این کار ذخیره میشود. سپس برای هردرخواست میتوان مثلا نشانی صفحه موردنظر و زمان و تاریخ آن را درون یک منبع ذخیره کرد تا برای استفاده‌های آتی به کار روند.
البته کاربردها و استفاده‌های دیگری نیز برای کوکی میتوان برشمرد که بدلیل طولانی شدن بحث از آنها صرفنظر میشود.
 
ایجاد کوکی
همانطور که در بالا نیز اشاره شد، کوکی‌ها داده هایی هستند که توسط وب سرور برای ذخیره در کلاینت (مرورگر) تولید میشوند. مرورگرها نیز باید این داده‌ها را بدون هیچ تغییری در هر درخواست عینا به سمت سرور برگردانند. درواقع با استفاده از کوکی‌ها میتوان به ارتباط بدون حالت HTTP به نوعی خاصیتی از جنس state اضافه کرد. به غیر از خود وب سرور، برای تنظیم و یا تولید کوکی میتوان از زبان‌های برنامه نویسی سمت کلاینت (مثل جاوا اسکریپت) البته درصورت پشتیبانی توسط مرورگر نیز استفاده کرد.
در جدیدترین استانداردهای موجود (RFC 6265) درباره کوکی آورده شده که مرورگرها باید بتوانند حداقلهای زیر را پشتیبانی کنند:
- توانایی ذخیره حداقل 3000 کوکی
- توانایی ذخیره کوکیها با حجم حداقل 4 کیلوبایت
- توانایی ذخیره و نگهداری حداقل 50 کوکی به ازای هر سایت یا دامین
 
نکته: توانایی مرورگرهای مدرن در مدیریت کوکی‌ها ممکن است فراتر از استانداردهای اشاره شده در بالا باشد.
 
انتقال داده‌های صفحات وب سایتها از طریق پروتوکل HTTP انجام میشود. مرورگر برای بارگذاری صفحه موردنظر کاربر از یک وب سایت، معمولا یک متن کوتاه به وب سرور مربوطه ارسال میکند که به آن HTTP Request میگویند. مثلا برای دریافت صفحه https://www.dntips.ir/index.html درخواستی به شکل زیر به سمت وب سرور ارسال میشود:
GET /index.html HTTP/1.1
Host: www.dotnettips.info 
مثلا نمونه یک درخواست کامل خام (Raw) از صفحه اول سایت جاری در نرم افزار Fiddler بصورت زیر است:
GET https://www.dntips.ir/ HTTP/1.1
Host: www.dotnettips.info
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: BlogPost-1175=NLOpR%2fgHcUGqPL8dZYv3BDDqgd4xOtiiNxHIp1rD%2bAQ%3d; BlogComment-5002=WlS1iaIsiBnQN1UDD4p%2fHFvuoxC3b8ckbw78mAWXZOSWMpxPlLo65%2bA40%2flFVR54; ReaderEmail=DP%2bx4TEtMT2LyhNQ5QqsArka%2fWALP5LYX8Y
وب سرور با ارسال محتویات صفحه موردنظر به این درخواست پاسخ میدهد که به آن HTTP Response میگویند.
در پاسخ ارسالی، وب سرور میتواند با استفاده از یک header مخصوص با نام Set-Cookie یک کوکی را ایجاد کند. در زیر یک نمونه از این پاسخها را مشاهده میکنید:
 HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: cookieName=cookieValue
Set-Cookie: 
cookieName2=cookieValue2; Expires=Thr, 10-Jun-2021 10:18:14 GMT
...
نمونه پاسخ ارسالی خام (Raw) در نرم افزار Fiddler مربوط به درخواست صفحه اول سایت جاری بصورت زیر است:
HTTP/1.1 200 OK
Date: Wed, 30 Jan 2013 20:25:15 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-Compressed-By: HttpCompress
Set-Cookie: .ASPXROLES=NzZ9qIRpCWHofryYglbsQFv_SSGPn7ivo0zKFoS94gcODVdIKQAe_IBwuc-TQ-03jGeIkZabTuxA0A3k2-nChy7iAWw9rPMYXSkqzMkizRFkDC0k3gQTkdLqLmmeIfnL9UjfMNWO8iVkYQrSv24ecbpFDSQCH827V2kEj8k2oCm_5sKRSmFpifh4N7kinEi0vomG1vW4Rbg9JWMhCgcvndvsFsXxpj-NiEikC1RqHpiLArIyalEMEN-cIuVtRe7uoo938u9l-7OXb8yzXucVl4bdqPy2DXM3ddWzb3OH1jSFM6gxwJ8qRZDlSGmEEbhji7rA-efI4aYGTKx6heWfUsY6E2k73jJLbuZ3RB4oNwRYmz8FRB0-vm1pO7rhF1JIoi1YB17ez-Ox5chNEFkPVREanHVU9DxboJ5dKgN-2B5udUFPunnshbN8EBhixbFQOpqRiiOK4uWWaWy3rVEJYpCCDBRctKCfEyYD1URFYeajB0AXmiMUTcGeuUtwb-XFjbQZnbylmMF3EJgG16bcc1IEkTAUv1JfKjaql0XGWJI1; path=/; HttpOnly
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 106727

<!DOCTYPE html>
<html>
...
وب سرور دستور Set-Cookie را تنها برای ثبت کوکی در مرورگر در پاسخ ارسالی قرار میدهد. برای آشنایی بیشتر با این هدر و ساختار آن به RFC 6265 مراجعه کنید. این دستور برای مرورگر مشخص میکند که (درصورت پشتیبانی از کوکی و فعال بودن آن در مروگر) در درخواستهای بعدی باید این کوکی‌ها را در متن درخواست ارسالی به سمت سرور ضمیمه کند. البته اینکار با توجه به تنظیمات خاصیتهای کوکی مربوطه  انجام میشود که در ادامه بحث میشود. برای ارسال کوکی به سمت وب سرور، مرورگر از هدر Cookie در درخواست خود استفاده میکند.
مثلا با توجه به مثال قبل برای درخواست صفحه https://www.dntips.ir/index2.html مرورگر میتواند متن زیر را به سمت وب سرور ارسال میکند:
 GET /index2.html HTTP/1.1
Host: www.example.org
Cookie: cookieName=cookieValue; cookieName2=cookieValue2
Accept: */*
این درخواست که برای صفحه دیگری از همان سایت قبلی است، با درخواست اول متفاوت است. با استفاده از این درخواست وب سرور میفهمد که این درخواست با درخواست قبلی مرتبط است. وب سرور در پاسخ ارسالی میتواند کوکی‌های دیگری نیز ثبت کند.
وب سرور میتواند مقدار یک کوکی ثبت شده در مروگر را با استفاده از دستوری مشابه Set-Cookie: cookieName=cookieNewValue در پاسخ ارسالی به سمت کلاینت تغییر دهد. مرورگر با دیدن این خط در پاسخ دریافتی از وب سرور مقدار کوکی را به روز رسانی میکند. البته به شرطی که سایر خواص تنظیم شده برای کوکی عینا یکسان باشد.
 
نکته: identity یا هویت کوکی با استفاده از تمام خواص آن به جز expires یا max-age تعیین میشود.
 
مقدار یک کوکی میتواند شامل هر کاراکتر قابل چاپ اَسکی (از کاراکتر ! تا کاراکتر ~ یا از کد یونیکد u0021\ تا u007E\) به غیر از کاراکترهای , و ; و فضای خالی باشد (استفاده از این سه کاراکتر و سایر کاراکترهای غیر اسکی تنها با انکدینگ میسر است). نام یک کوکی نیز از این قاعده پیروی میکند، البته شامل کاراکتر = نیز نمیتواند باشد چون این کاراکتر نقش جداکننده «مقدار» از «نام» کوکی را ایفا میکند. استانداردهای کوکی که در RFC 6265 آورده شده است، محدودیتهای بیشتری نیز دارد که ممکن است توسط مروگرهای امروزی رعایت نشود!
 
نکته: انکدینگ کوکیها کمی بحث دارد. ازآنجاکه کوکی‌ها به صورت هدرهای پروتوکل HTTP انتقال داده میشوند، بنابراین کاراکترهای موجود در آن باید تنها از نوع ASCII باشند. اما چون ارسال کننده و دریافت کننده نهایی کوکی یک وب سرور یکسان است بنابراین وب سرور برای ذخیره کاراکترهای غیر اسکی میتواند از انکدینگ خاص خود استفاده کند. مثلا عموم وب سرورها  و نیز مرورگرها از URL Encoding برای انکدینگ کوکی‌ها استفاده میکنند (^). ظاهرا در تمام مرورگرهای مدرن برای ذخیره کوکی ها، حداقل نام و مقدار کوکی به صورت جداگانه (مثلا برای ذخیره کاراکترهای نامعتبر) انکد میشود به غیر از کاراکتر تساوی (=) بین نام و مقدار کوکی.
 
با استفاده از زبانهای برنامه نویسی سمت کلاینت نیز میتوان کوکی‌ها را مدیریت کرد. مثلا در جاوا اسکریپت از document.cookie برای اینکار استفاده میشود. نحوه کاربرد و استفاده از این پراپرتی کمی غیرعادی است. مثلا دستور 'document.cookie = 'dummy=a11a یک کوکی با نام dummy و با مقدار a11a ایجاد میکند! در ادامه با این دستور و نحوه کارکردن با کوکی در جاوا اسکریپت بیشتر آشنا میشویم.
 
نکته: برای انکد رشته‌ها در جاوا اسکریپت از دستور escape استقاده میشود. عملیات عکس آن با دستور unescape انجام میشود.
 
نکته: با اینکه استاندارد تعریف کوکی مشخص کرده که برای تعریف کوکی وجود عبارتی به صورت name=value اجباری است، اما ظاهرا بیشتر مرورگرها صحت تعریف کوکی و  اعتبار آنرا برای پیروی از این طرح بررسی نمیکنند و بنابراین میتوان صرفا با استفاده از یک رشته بدون علامت مساوی یک کوکی را ایجاد کرد.
 
خواص یک کوکی
به غیر از نام و مقدار، کوکی‌ها خواص دیگری همچون دامین (domain)، مسیر (path)، تاریخ انقضا (expiration date) یا حداکثر طول عمر (maximum age)، و Secure و HttpOnly دارند که میتوانند توسط وب سرور و یا با استفاده از زبانهای برنامه نویسی کلاینتی تنظیم شوند. مرورگرها این خاصیتها را به وب سرور ارسال نمیکنند. تنها مقادیری که به سمت وب سرور برگشت داده میشوند، نام و مقدار کوکی‌هاست. مرورگرها با استفاده از خواص کوکی زمان حذف کوکی و یا کنترل دسترسی به مقدار آن با توجه به آدرس جاری مرورگر و نیز اینکه آیا اصلا کوکی را به وب سرور ارسال کنند یا نه، را تعیین میکنند. خواص کوکی در ادامه شرح داده شده است:
 
1. تاریخ انقضا و حداکثر طول عمر (Expires و Max-Age)
با استفاده از یکی از این دو خاصیت، تاریخی که دیگر نیازی نیست تا کوکی به سمت سرور ارسال شود، تعیین میشود. بنابراین پس از این تاریخ، ممکن است کوکی از مخزن مرورگر پاک شود. برپایه اطلاعات موجود در RFC 6265، در خاصیت Expires، تاریخ انقضای کوکی باید به فرمت “Wdy, DD Mon YYYY HH:MM:SS GMT” باشد. مثل Mon, 17-Mar-2014 1:00:00 GMT. همچنین خاصیت Max-Age طول عمر کوکی را برحسب ثانیه از لحظه دریافت توسط مرورگر مشخص میکند. به نمونه‌های زیر توجه کنید:
Set-Cookie: cookie1=abc; Expires=Mon, 17-Mar-2014 01:00:00 GMT; ...
Set-Cookie: cookie2=123; ...
Set-Cookie: cookie3=abc; Expires=Thu, 01-Jan-1970 00:00:01 GMT; ...
Set-Cookie: cookie3=abc; max-age=31536000; ... 
 ...... 
در دستور اول، cookie1 برای حذف در تاریخ مشخص شده تنظیم شده است. در خط دوم که بدون این دو خاصیت است، یک نوع کوکی سشنی تعریف شده است. این کوکی پس از بسته شدن مرورگر (اتمام سشن) از حافظه پاک خواهد شد.

نکته: اتمام یک سشن برای کوکی‌های سشنی دقیقا به معنی بستن مرورگر (یا تب مربوطه در مروگرهای مدرن) است.

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

نکته: استفاده از روش تنظیم یک تاریخ انقضا در گذشته برای حذف یک کوکی تنها وقتی کار خواهد که سایر خواص تعیین شده در دستور Set-Cookie با مقادیر موجود در حافظه مرورگر دقیقا یکی باشد تا هویت کوکی موردنظر به صورت منحصربه فرد تعیین شود. 

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

نکته: خاصیت max-age در مرورگر IE8 و نسخه‌های قبل از آن پشتیبانی نمیشود. 

نکته: گزینه ای که معمولا در صفحات لاگ‌آن (LogOn) یا ساین‌این (SignIn) برای ذخیره داده‌های کاربر وجود دارد (مثل «مرا به خاطر بسپار»)، مرتبط با این خاصیت از کوکی هاست. در صورت عدم انتخاب این گزینه معمولا یک کوکی سشنی (بدون خاصیت expires) ایجاد میشود. اما با انتخاب این گزینه، یک کوکی ماندگار (Persistent) با خاصیت expires برابر با تاریخی در آینده ایجاد میشود تا درصورت بسته شدن مرورگر (اتمام سشن) داده‌های کاربر پاک نشود. 
 
نکته: تاریخ انقضای کوکی با استفاده از تاریخ کلاینت تعیین میشود. متاسفانه هیچ راه مستقیمی برای همزمانی این تاریخ با تاریخ سرور وجود ندارد. 
 
2. دامین و مسیر (Domain و Path)
خاصیتهای دامین و مسیر کوکی، محدوده قابل دسترس کوکی را مشخص میکنند. با استفاده از این دو خاصیت مرورگر متوجه میشود که آیا کوکی را در موقعیت و آدرس جاری باید به سمت وب سرور ارسال کند یا خیر. همچنین دسترسی به کوکی‌ها در سمت کلاینت با توجه به این دو خاصیت محدود میشود. مقدار پیش فرض این دو خاصیت برابر مسیر و دامین جاری مرورگر است که اگر مقداری برای این دو خاصیت تعیین نشود، به کوکی تعلق میگیرد.
 
نکته: منظور از وضعیت جاری، موقعیتی است که کوکی مذبور در آن ایجاد شده است. مثلا آدرس صفحه ای که هدر Set-Cookie را ارسال کرده و یا آدرس صفحه ای که در آن با استفاده از دستوری مشابه ;'document.cookie = 'a=b کوکی مربوطه ایجاد شده است. به عنوان نمونه اگر یک کوکی در صفحه جاری همین سایت ایجاد شود و خاصیتهای دامین و مسیر آن مقداردهی نشود، مقدار دامین به www.dotnettips.info و مقدار مسیر آن به post/ تنظیم خواهد شد.

نکته: مرورگر بررسی دامین کوکی را ازطریق «مقایسه از انتها» انجام میدهد. یعنی اگر مثلا دامین یک کوکی برابر dotnettips.info باشد، این کوکی در ساب دامین‌های d1.dotettips.info و یا www.dotnettips.info و از این قبیل در دسترس خواهد بود. برای کسب اطلاعات بیشتر میتوان به RFC 6265 (قسمت Domain Matching) مراجعه کرد.
 
نکته: بررسی مسیر کوکی برخلاف دامین آن، ازطریق «مقایسه از ابتدا» انجام میشود. یعنی آدرس صفحه جاری پس از مقدار دامین سایت باید با مقدار مشخص شده در خاصیت مسیر شروع شود. مثلا مسیر یک کوکی برابر post/ و دامین آن نیز برابر www.dotnettips.info باشد، این کوکی در آدرسهایی چون www.dotnettips.info/post/1286 و یا www.dotnettips.info/post/1 و یا www.dotnettips.info/post/test/test2 و ... در دسترس خواهد بود. برای کسب اطلاعات بیشتر میتوان به RFC 6265 (قسمت Paths and Path-Match) مراجعه کرد. 

برای روشنتر شدن مطلب به هدرهای Set-Cookie زیر توجه کنید:
Set-Cookie: MyCookie1=hi; Domain=d1.d2.com; Path=/employee ...
Set-Cookie: MyCookie2=bye; Domain=.d3.com; Path=/ ...
Set-Cookie: MyCookie3=nth ... 
 ...... 
اولین دستور به مرورگر میگوید تا یک کوکی با نام MyCookie1 و مقدار hi را با دامین d1.d2.com و مسیر employee/ ثبت کند. بنابراین مرورگر از این کوکی تنها درصورتیکه آدرس درخواست موردنظر شامل d1.d2.com/employee باشد، استفاده میکند.
دستور دوم به مرورگر میگوید تا از کوکی MyCookie2 در مسیرهای شامل d3.com. استفاده کند.
در دستور سوم دامین و مسیر با توجه به آدرس صفحه جاری تنظیم میشود. در واقع تنها مسیرهایی که شامل آدرس صفحه جاری باشند به این کوکی دسترسی دارند.

نکته: مقدار domain تنها میتواند مربوط به دامین اصلی جاری و یا زیرمجموعه‌های آن باشد. یعنی نمیتوان یک کوکی با دامین www.d1.com در صفحه‌ای با آدرس www.d2.com ایجاد کرد.

نکته: همچنین کوکی هایی که مثلا دارای دامین www.dotnettips.info هستند از آدرسی نظیر my.dotnettips.info در دسترس نیستند. کوکی‌ها تنها در دامین و ساب دامین‌های مربوط به خود قابل خواندن هستند.

نکته: اگر مقدار خاصیت domain کوکی به چیزی شبیه dotnettip.info تنظیم شود آنگاه این کوکی در آدرسهایی چون www.dotnettips.info و یا d1.dotnettips.info نیز در دسترس است.

نکته: اگر برای خاصیت path مقدار / تنظیم شود، بدین معنی است که کوکی در تمام محدوده دامین کوکی در دسترس است. 

3. Secure و HttpOnly
این دوخاصیت برخلاف خواص قبلی مقداری را تنظیم نمیکنند! بلکه همانند یک flag عمل کرده که هر کدام رفتار خاصی را برای مرورگر الزام میکنند.
خاصیت Secure مرورگر را مجبور به استفاده از ارتباطات امن و کدگذاری شده (Https) برای تبادل داده‌های کوکی میکند. درضمن طبیعی است که وب سرور دستور ثبت چنین کوکی‌هایی را خود از طریق یک ارتباط امن به مرورگر ارسال کند تا مبادا طی یک فرایند خرابکارانه داده‌های مهم درون کوکی در بین راه دزدیده نشود. 

نکته: یک کوکی Secure تنها درصورتی به سمت سرور ارسال میشود که درخواست مذکور با استفاده از SSL و ازطریق پروتوکل HTTPS ایجاد شده باشد.

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

نکته: این خاصیت ابتدا توسط مایکروسافت در نسخه IE 6 SP1 معرفی شد و بعدها بتدریج توسط سایر مرورگرها نیز پشتیبانی شد. این ویژگی همانطور که از آن برمی‌آید برای مقابله با حملات XSS پیاده سازی شده است. البته علاوه برای جلوگیری از دسترسی به این کوکی‌ها از طریق document.cookie، در مرورگرهای مدرن از دسترسی به هدر این کوکی‌ها ازطریق متدهای شی XMLHttpRequest نیز جلوگیری میشود.

نکته: امکان تنظیم این خاصیت از طریق document.cookie در جاوا اسکریپت وجود ندارد!
 
مدیریت کوکی‌ها در مرورگر
همانطور که قبلا اشاره شد هویت یک کوکی با استفاده تمامی خواص آن به جز expires یا max-age مشخص میشود. یعنی ترکیب name-domain-path-secure/httponly هویت یک کوکی را منحصربه‌فرد میکند. بنابراین تا زمانیکه حتی یکی از این خواص دو کوکی با هم فرق داشته باشد این دو کوکی از هم متمایز خواهند بود. دو کوکی زیر را درنظر بگیرید:
Set-Cookie: cookie1=value1
Set-Cookie: cookie1=value2; domain=dotnettips.info;
 با اینکه به نظر میرسد که این دو کوکی یکسان هستند و اجرای دستور دوم موجب بازنویسی کوکی اول میشود، اما بررسی یک درخواست ارسالی از این صفحه نشان میدهد که دو کوکی مجزا با نام مشابه به سمت سرور ارسال میشود:
Cookie: cookie1=value1; cookie1=value2 
حال اگر کوکی سومی به صورت زیر تعریف شود:
Set-Cookie: cookie1=value3; domain=dotnettips.info; path=/post
وضعیت از این نیز پیچیده‌تر میشود:
Cookie: cookie1=value1; cookie1=value2; cookie1=value3
بنابراین اجرای دستور زیر در همان صفحه:
Set-Cookie: cookie1=value4
تنها مقدار کوکی اول را تغییر خواهد داد. یعنی در درخواست ارسالی به سمت سرور خواهیم داشت:
Cookie: cookie1=value4; cookie1=value2; cookie1=value3
بنابراین دقت مضاعف به این نکته که «هویت یک کوکی با استفاده از تمامی خواص آن به جز expires یا max-age تعیین میشود» مهم است. برای قسمت «به جز expire یا max-age» هم به مثال زیر توجه کنید:
Set-Cookie: cookie2=value1; max-age=1000
بنابراین خواهیم داشت:
Cookie: cookie1=value1; cookie1=value2; cookie1=value4; cookie2=value1
یک کوکی با طول عمر 1000 ثانیه تولید میکند. بنابراین با دستور زیر میتوان مقدار همین کوکی را تغییر داد:
Set-Cookie: cookie2=value2
پس داریم:
Cookie: cookie1=value4; cookie1=value2; cookie1=value3; cookie2=value2
هرچند در دستور آخر به نظر میرسد که کوکی آخر به نوع سشنی تغییر یافته است (چون خاصیت expires یا max-age ندارد) اما درواقع این چنین نیست. تنها اتفاقی که رخ داده است این است که مقدار کوکی مذبور تغییر یافته است، درحالیکه تغییری در خاصیت expires یا max-age آن رخ نداده است.

نکته: با تغییر خواص یک کوکی، میتوان آنرا از نوع سشنی به نوع ماندگار (Persistent) تغییر داد، اما عکس این عملیات ممکن نیست.

SubCookie
بدلیل محدودیت موجود در تعداد کوکی‌ها به ازای هر دامین، روشی برای نگهداری تعداد بیشتری تنظیمات درون همین تعداد محدود کوکی‌ها توسط توسعه گران ابداع شده است. در این روش از طرح ساده ای که نمونه ای از آن در زیر نشان داده شده است برای نگهداری داده‌های چندین کوکی درون یک کوکی استفاده میشود:
Set-Cookie: cookieName=cookie1=value1&cookie2=value2&cookie3=value3&cookie4=value4; path=/
در نمونه بالا با اینکه عملا تنها یک کوکی تعریف شده است اما درواقع داده‌های 4 کوکی مختلف درون یک کوکی آورده شده است. تنها عیب این روش این است که زحمت بیشتری برای استخراج داده‌های کوکی‌ها باید کشید. البته امروزه برخی از فریمورکها امکاناتی جهت کار با این کوکی‌ها فراهم کرده اند.
 
Cookie2
در ابتدای هزاه سوم! مدتی بحثی مطرح شد برای بهبود کارایی و امنیت کوکی‌ها و پیشنهادهایی مبنی بر پیاده سازی نوع جدیدی از کوکی‌ها با عنوان Cookie2 نیز ارائه شد. حتی در نسخه جدیدی از استانداردهای HTTP State Management Mechanism که در RFC 2965 آورده شده است کلا به این نوع جدید از کوکی‌ها پرداخته شده است. هرچند برخی از مرورگرها پشتیبانی از این نوع جدید را آغاز کردند (مثل Opera) اما بعدها به دلیل عدم استقبال از آن، این نوع از کوکی‌ها منسوخ شد و حالت آن در نسخه‌های جدید استانداردها به Obsolete تغییر یافت (RFC 6265)! درهرصورت برای آشنایی با این نوع کوکی‌ها میتوان به مراجع زیر رجوع کرد:

 
منابع:
مطالب
ایجاد جداول بهینه سازی شده برای حافظه در SQL Server 2014
پس از نگاهی به مفاهیم مقدماتی OLTP درون حافظه‌ای در SQL Server 2014، در ادامه به نحوه‌ی انجام تنظیمات خاص جداول بهینه سازی شده برای حافظه خواهیم پرداخت.


ایجاد یک بانک اطلاعاتی با پشتیبانی از جداول بهینه سازی شده برای حافظه

برای ایجاد جداول بهینه سازی شده برای حافظه، ابتدا نیاز است تا تنظیمات خاصی را به بانک اطلاعاتی آن اعمال کنیم. برای اینکار می‌توان یک بانک اطلاعاتی جدید را به همراه یک filestream filegroup ایجاد کرد که جهت جداول بهینه سازی شده برای حافظه، ضروری است؛ یا اینکه با تغییر یک بانک اطلاعاتی موجود و افزودن filegroup یاد شده نیز می‌توان به این مقصود رسید.
در اینگونه جداول خاص، اطلاعات در حافظه‌ی سیستم ذخیره می‌شوند و برخلاف جداول مبتنی بر دیسک سخت، صفحات اطلاعات وجود نداشته و نیازی نیست تا به کش بافر وارد شوند. برای مقاصد ذخیره سازی نهایی اطلاعات جداول بهینه سازی شده برای حافظه، موتور OLTP درون حافظه‌ای آن، فایل‌های خاصی را به نام checkpoint در یک filestream filegroup ایجاد می‌کند که از آن‌ها جهت ردیابی اطلاعات استفاده خواهد کرد و نحوی ذخیره سازی اطلاعات در آن‌ها از شیوه‌ی با کارآیی بالایی به نام append only mode پیروی می‌کند.
با توجه به متفاوت بودن نحوه‌ی ذخیره سازی نهایی اطلاعات اینگونه جداول و دسترسی به آن‌ها از طریق استریم‌ها، توصیه شده‌است که filestream filegroup‌های تهیه شده را در یک SSD یا Solid State Drive قرار دهید.

پس از اینکه بانک اطلاعاتی خود را به روش‌های معمول ایجاد کردید، به برگه‌ی خواص آن در management studio مراجعه کنید. سپس صفحه‌ی file groups را در آن انتخاب کرده و در پایین برگه‌ی آن، در قسمت جدید memory optimized data، بر روی دکمه‌ی Add کلیک کنید. سپس نام دلخواهی را وارد نمائید.


پس از ایجاد یک گروه فایل جدید، به صفحه‌ی files خواص بانک اطلاعاتی مراجعه کرده و بر روی دکمه‌ی Add کلیک کنید. سپس File type این ردیف اضافه شده را از نوع file stream data و file group آن‌را همان گروه فایلی که پیشتر ایجاد کردیم، تنظیم کنید. در ادامه logical name دلخواهی را وارد کرده و در آخر بر روی دکمه‌ی Ok کلیک کنید تا تنظیمات مورد نیاز جهت تعاریف جدول بهینه سازی شده برای حافظه به پایان برسد.


این مراحل را توسط دو دستور T-SQL ذیل نیز می‌توان سریعتر انجام داد:
USE [master]
GO
ALTER DATABASE [testdb2] 
      ADD FILEGROUP [InMemory_InMemory] CONTAINS MEMORY_OPTIMIZED_DATA 
GO
ALTER DATABASE [testdb2] 
      ADD FILE ( NAME = N'InMemory_InMemory', FILENAME = N'D:\SQL_Data\MSSQL11.MSSQLSERVER\MSSQL\DATA\InMemory_InMemory' ) 
      TO FILEGROUP [InMemory_InMemory]
GO

ساختار گروه فایل بهینه سازی شده برای حافظه

گروه فایل بهینه سازی شده برای حافظه، دارای چندین دربرگیرنده است که هر کدام چندین فایل را در خود جای خواهند داد:
- Root File که در برگیرنده‌ی متادیتای اطلاعات است.
- Data File که شامل ردیف‌های اطلاعات ثبت شده در جداول بهینه سازی شده‌ی برای حافظه هستند. این ردیف‌ها همواره به انتهای data file اضافه می‌شوند و دسترسی به آن‌ها ترتیبی است. کارآیی IO این روش نسبت به روش دسترسی اتفاقی به مراتب بالاتر است. حداکثر اندازه این فایل 128 مگابایت است و پس از آن یک فایل جدید ساخته می‌شود.
- Delta File شامل ردیف‌هایی است که حذف شده‌اند. به ازای هر ردیف، حداقل اطلاعاتی از آن را در خود ذخیره خواهد کرد؛ شامل ID ردیف حذف شده و شماره تراکنش آن. همانطور که پیشتر نیز ذکر شد، این موتور جدید درون حافظه‌ای، برای یافتن راه چاره‌ای جهت به حداقل رسانی قفل گذاری بر روی اطلاعات، چندین نگارش از ردیف‌ها را به همراه timestamp آن‌ها در خود ذخیره می‌کند. به این ترتیب، هر به روز رسانی به همراه یک حذف و سپس ثبت جدید است. به این ترتیب دیگر بانک اطلاعاتی نیازی نخواهد داشت تا به دنبال رکورد موجود برگردد و سپس اطلاعات آن‌را به روز نماید. این موتور جدید فقط اطلاعات به روز شده را در انتهای رکوردهای موجود با فرمت خود ثبت می‌کند.


ایجاد جداول بهینه سازی شده برای حافظه

پس از آماده سازی بانک اطلاعاتی خود و افزودن گروه فایل استریم جدیدی به آن برای ذخیره سازی اطلاعات جداول بهینه سازی شده برای حافظه، اکنون می‌توانیم اینگونه جداول خاص را در کنار سایر جداول متداول موجود، تعریف و استفاده نمائیم:
-- It is not a Memory Optimized
CREATE TABLE tblNormal
(
   [CustomerID] int NOT NULL PRIMARY KEY NONCLUSTERED, 
   [Name] nvarchar(250) NOT NULL,
   CustomerSince DATETIME not NULL
      INDEX [ICustomerSince] NONCLUSTERED
)

--  DURABILITY = SCHEMA_AND_DATA
CREATE TABLE tblMemoryOptimized_Schema_And_Data
(
    [CustomerID] INT NOT NULL 
PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 1000000),
    [Name] NVARCHAR(250) NOT NULL,
    [CustomerSince] DATETIME NOT NULL
INDEX [ICustomerSince] NONCLUSTERED
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)


-- DURABILITY = SCHEMA_ONLY
CREATE TABLE tblMemoryOptimized_Schema_Only
(
    [CustomerID] INT NOT NULL 
PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 1000000),
    [Name] NVARCHAR(250) NOT NULL,
    [CustomerSince] DATETIME NOT NULL
INDEX [ICustomerSince] NONCLUSTERED
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)
در اینجا سه جدول را مشاهده می‌کنید که در بانک اطلاعاتی آماده شده در مرحله‌ی قبل، ایجاد خواهند شد. مورد اول یک جدول معمولی است که از آن برای مقایسه سرعت ثبت اطلاعات با سایر جداول ایجاد شده، استفاده خواهد شد.
همانطور که مشخص است، دو جدول بهینه سازی شده برای حافظه، همان سه ستون جدول معمولی مبتنی بر دیسک سخت را دارا هستند؛ اما با این تفاوت‌ها:
- دارای ویژگی MEMORY_OPTIMIZED = ON می‌باشند. به این ترتیب اینگونه جداول نسبت به جداول متداول مبتنی به دیسک سخت متمایز خواهند شد.
- دارای ویژگی DURABILITY بوده و توسط مقدار SCHEMA_AND_DATA آن مشخص می‌کنیم که آیا قرار است اطلاعات و ساختار جدول، ذخیره شوند یا تنها قرار است ساختار جدول ذخیره گردد (حالت SCHEMA_ONLY).
- بر روی ستون Id آن‌ها یک hash index ایجاد شده‌است که وجود آن ضروری است و در کل بیش از 8 ایندکس را نمی‌توان تعریف کرد.
برخلاف ایندکس‌های B-tree جداول مبتنی بر سخت دیسک، ایندکس‌های جداول بهینه سازی شده برای حافظه، اطلاعات را تکرار نمی‌کنند. این‌ها صرفا اشاره‌گرهایی هستند به ردیف‌های اصلی اطلاعات. به این معنا که این ایندکس‌ها لاگ نشده و همچنین بر روی سخت دیسک ذخیره نمی‌شوند. کار بازسازی مجدد آن‌ها در اولین بار بازیابی بانک اطلاعاتی و آغاز آن به صورت خودکار انجام می‌شود. به همین جهت مباحثی مانند index fragmentation و نگهداری ایندکس‌ها دیگر در اینجا معنا پیدا نمی‌کنند.
دو نوع ایندکس را در اینجا می‌توان تعریف کرد. اولین آن‌ها hash index است و دومین آن‌ها range index. هش ایندکس‌ها برای حالاتی که در کوئری‌ها از عملگر تساوی استفاده می‌شود بسیار مناسب هستند. برای عملگرهای مقایسه‌ای از ایندکس‌های بازه‌ای استفاده می‌شود.
همچنین باید دقت داشت که پس از ایجاد ایندکس‌ها، دیگر امکان تغییر آن‌ها و یا تغییر ساختار جدول ایجاد شده نیست.
همچنین ایندکس‌های تعریف شده در جداول بهینه سازی شده برای حافظه، تنها بر روی ستون‌هایی غیرنال پذیر از نوع BIN2 collation مانند int و datetime قابل تعریف هستند. برای مثال اگر سعی کنیم بر روی ستون Name ایندکسی را تعریف کنیم، به این خطا خواهیم رسید:
 Indexes on character columns that do not use a *_BIN2 collation are not supported with indexes on memory optimized tables.
- در حین تعریف هش ایندکس‌ها، مقدار BUCKET_COUNT نیز باید تنظیم شود. هر bucket توسط مقداری که حاصل هش کردن یک ستون است مشخص می‌شود. کلیدهای منحصربفرد دارای هش‌های یکسان در bucketهای یکسانی ذخیره می‌شوند. به همین جهت توصیه شده‌است که حداقل مقدار bucket تعیین شده در اینجا مساوی یا بیشتر از مقدار تعداد کلیدهای منحصربفرد یک جدول باشد؛ مقدار پیش فرض 2 برابر توسط مایکروسافت توصیه شده‌است.
- نوع‌های قابل تعریف ستون‌ها نیز در اینجا به موارد ذیل محدود هستند و جمع طول آن‌ها از 8060 نباید بیشتر شود:
 bit, tinyint, smallint, int, bigint, money, smallmoney, float, real, datetime, smalldatetime, datetime2,
date, time, numberic, decimal, char(n),  varchar(n) ,nchar(n),  nvarchar(n), sysname, binary(n),
varbinary(n), and Uniqueidentifier


همچنین در management studio، گزینه‌ی جدید new -> memory optimized table نیز اضافه شده‌است و انتخاب آن سبب می‌شود تا قالب T-SQL ایی برای تهیه این نوع جداول، به صورت خودکار تولید گردد.


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


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

در مثال زیر، 100 هزار رکورد را در سه جدولی که پیشتر ایجاد کردیم، ثبت کرده و سپس مدت زمان اجرای هر کدام از مجموعه عملیات را بر حسب میلی ثانیه بررسی می‌کنیم:
set statistics time off
SET STATISTICS IO Off
set nocount on
go
-----------------------------

Print 'insert into tblNormal'

DECLARE @start datetime = getdate()
declare @insertCount int = 100000
declare @startId int = 1
declare @customerID int = @startId

while @customerID < @startId + @insertCount
begin
    insert into tblNormal values (@customerID, 'Test', '2013-01-01T00:00:00')
    set @customerID +=1
end

Print DATEDIFF(ms,@start,getdate());
go
-----------------------------

Print 'insert into tblMemoryOptimized_Schema_And_Data'

DECLARE @start datetime = getdate()
declare @insertCount int = 100000
declare @startId int = 1
declare @customerID int = @startId

while @customerID < @startId + @insertCount
begin
    insert into tblMemoryOptimized_Schema_And_Data values (@customerID, 'Test', '2013-01-01T00:00:00')
    set @customerID +=1
end
Print DATEDIFF(ms,@start,getdate());
Go
-----------------------------

Print 'insert into tblMemoryOptimized_Schema_Only'

DECLARE @start datetime = getdate()
declare @insertCount int = 100000
declare @startId int = 1
declare @customerID int = @startId

while @customerID < @startId + @insertCount
begin
    insert into tblMemoryOptimized_Schema_Only values (@customerID, 'Test', '2013-01-01T00:00:00')
    set @customerID +=1
end
Print DATEDIFF(ms,@start,getdate());

Go
با این خروجی تقریبی که بر اساس توانمندی‌های سخت افزاری سیستم می‌تواند متفاوت باشد:
 insert into tblNormal
36423

insert into tblMemoryOptimized_Schema_And_Data
30516

insert into tblMemoryOptimized_Schema_Only
3176
و برای حالت select خواهیم داشت:
 set nocount on
print 'tblNormal'
set statistics time on
select count(CustomerID) from tblNormal
set statistics time off
go
print 'tblMemoryOptimized_Schema_And_Data'
set statistics time on
select count(CustomerID) from tblMemoryOptimized_Schema_And_Data
set statistics time off
go
print 'tblMemoryOptimized_Schema_Only'
set statistics time on
select count(CustomerID) from tblMemoryOptimized_Schema_Only
set statistics time off
go
با این خروجی
 tblNormal
 SQL Server Execution Times:
CPU time = 46 ms,  elapsed time = 52 ms.

tblMemoryOptimized_Schema_And_Data
 SQL Server Execution Times:
CPU time = 32 ms,  elapsed time = 33 ms.

tblMemoryOptimized_Schema_Only
 SQL Server Execution Times:
CPU time = 31 ms,  elapsed time = 30 ms.
تاثیر جداول بهینه سازی شده برای حافظه را در 350K inserts بهتر می‌توان با نمونه‌های متداول مبتنی بر دیسک مقایسه کرد.


برای مطالعه بیشتر

Getting started with SQL Server 2014 In-Memory OLTP
Introduction to SQL Server 2014 CTP1 Memory-Optimized Tables
Overcoming storage speed limitations with Memory-Optimized Tables for SQL Server
Memory-optimized Table – Day 1 Test
Memory-Optimized Tables – Insert Test
Memory Optimized Table – Insert Test …Again
مطالب
مهاجرت از SQL Membership به ASP.NET Identity
در این مقاله مهاجرت یک اپلیکیشن وب که توسط SQL Membership ساخته شده است را به سیستم جدید ASP.NET Identity بررسی می‌کنیم. برای این مقاله از یک قالب اپلیکیشن وب (Web Forms) که توسط Visual Studio 2010 ساخته شده است برای ساختن کاربران و نقش‌ها استفاده می‌کنیم. سپس با استفاده از یک SQL Script دیتابیس موجود را به دیتابیسی که ASP.NET Identity نیاز دارد تبدیل می‌کنیم. در قدم بعدی پکیج‌های مورد نیاز را به پروژه اضافه می‌کنیم و صفحات جدیدی برای مدیریت حساب‌های کاربری خواهیم ساخت. بعنوان یک تست، کاربران قدیمی که توسط SQL Membership ساخته شده بودند باید قادر باشند به سایت وارد شوند. همچنین کاربران جدید باید بتوانند بدون هیچ مشکلی در سیستم ثبت نام کنند. سورس کد کامل این مقاله را می‌توانید از این لینک دریافت کنید.


یک اپلیکیشن با SQL Membership بسازید

برای شروع به اپلیکیشنی نیاز داریم که از SQL Membership استفاده می‌کند و دارای داده هایی از کاربران و نقش‌ها است. برای این مقاله، بگذارید پروژه جدیدی توسط VS 2010 بسازیم.

حال با استفاده از ابزار ASP.NET Configuration دو کاربر جدید بسازید: oldAdminUser و oldUser.

نقش جدیدی با نام Admin بسازید و کاربر oldAdminUser را به آن اضافه کنید.

بخش جدیدی با نام Admin در سایت خود بسازید و فرمی بنام Default.aspx به آن اضافه کنید. همچنین فایل web.config این قسمت را طوری پیکربندی کنید تا تنها کاربرانی که در نقش Admin هستند به آن دسترسی داشته باشند. برای اطلاعات بیشتر به این لینک مراجعه کنید.

پنجره Server Explorer را باز کنید و جداول ساخته شده توسط SQL Membership را بررسی کنید. اطلاعات اصلی کاربران که برای ورود به سایت استفاده می‌شوند، در جداول aspnet_Users و aspnet_Membership ذخیره می‌شوند. داده‌های مربوط به نقش‌ها نیز در جدول aspnet_Roles ذخیره خواهند شد. رابطه بین کاربران و نقش‌ها نیز در جدول aspnet_UsersInRoles ذخیره می‌شود، یعنی اینکه هر کاربری به چه نقش هایی تعلق دارد.

برای مدیریت اساسی سیستم عضویت، مهاجرت جداول ذکر شده به سیستم جدید ASP.NET Identity کفایت می‌کند.

مهاجرت به Visual Studio 2013

  • برای شروع ابتدا Visual Studio Express 2013 for Web یا Visual Studio 2013 را نصب کنید.
  • حال پروژه ایجاد شده را در نسخه جدید ویژوال استودیو باز کنید. اگر نسخه ای از SQL Server Express را روی سیستم خود نصب نکرده باشید، هنگام باز کردن پروژه پیغامی به شما نشان داده می‌شود. دلیل آن وجود رشته اتصالی است که از SQL Server Express استفاده می‌کند. برای رفع این مساله می‌توانید SQL Express را نصب کنید، و یا رشته اتصال را طوری تغییر دهید که از LocalDB استفاده کند.
  • فایل web.config را باز کرده و رشته اتصال را مانند تصویر زیر ویرایش کنید.

  • پنجره Server Explorer را باز کنید و مطمئن شوید که الگوی جداول و داده‌ها قابل رویت هستند.
  • سیستم ASP.NET Identity با نسخه 4.5 دات نت فریم ورک و بالا‌تر سازگار است. پس نسخه فریم ورک پروژه را به آخرین نسخه (4.5.1) تغییر دهید.

پروژه را Build کنید تا مطمئن شوید هیچ خطایی وجود ندارد.

نصب پکیج‌های NuGet

در پنجره Solution Explorer روی نام پروژه خود کلیک راست کرده، و گزینه Manage NuGet Packages را انتخاب کنید. در قسمت جستجوی دیالوگ باز شده، عبارت "Microsoft.AspNet.Identity.EntityFramework" را وارد کنید. این پکیج را در لیست نتایج انتخاب کرده و آن را نصب کنید. نصب این بسته، نیازمندهای موجود را بصورت خودکار دانلود و نصب می‌کند: EntityFramework و ASP.NET Identity Core. حال پکیج‌های زیر را هم نصب کنید (اگر نمی‌خواهید OAuth را فعال کنید، 4 پکیج آخر را نادیده بگیرید).
  • Microsoft.AspNet.Identity.Owin
  • Microsoft.Owin.Host.SystemWeb
  • Microsoft.Owin.Security.Facebook
  • Microsoft.Owin.Security.Google
  • Microsoft.Owin.Security.MicrosoftAccount
  • Microsoft.Owin.Security.Twitter

مهاجرت دیتابیس فعلی به سیستم ASP.NET Identity

قدم بعدی مهاجرت دیتابیس فعلی به الگویی است، که سیستم ASP.NET Identity به آن نیاز دارد. بدین منظور ما یک اسکریپت SQL را اجرا می‌کنیم تا جداول جدیدی بسازد و اطلاعات کاربران را به آنها انتقال دهد. فایل این اسکریپت را می‌توانید از لینک https://github.com/suhasj/SQLMembership-Identity-OWIN دریافت کنید.
این اسکریپت مختص این مقاله است. اگر الگوی استفاده شده برای جداول سیستم عضویت شما ویرایش/سفارشی-سازی شده باید این اسکریپت را هم بر اساس این تغییرات بروز رسانی کنید.
پنجره Server Explorer را باز کنید. گره اتصال ApplicationServices را باز کنید تا جداول را مشاهده کنید. روی گره Tables کلیک راست کرده و گزینه New Query را انتخاب کنید.

در پنجره کوئری باز شده، تمام محتویات فایل Migrations.sql را کپی کنید. سپس اسکریپت را با کلیک کردن دکمه Execute اجرا کنید.

ممکن است با اخطاری مواجه شوید مبنی بر آنکه امکان حذف (drop) بعضی از جداول وجود نداشت. دلیلش آن است که چهار عبارت اولیه در این اسکریپت، تمام جداول مربوط به Identity را در صورت وجود حذف می‌کنند. از آنجا که با اجرای اولیه این اسکریپت چنین جداولی وجود ندارند، می‌توانیم این خطاها را نادیده بگیریم. حال پنجره Server Explorer را تازه (refresh) کنید و خواهید دید که پنج جدول جدید ساخته شده اند.

لیست زیر نحوه Map کردن اطلاعات از جداول SQL Membership به سیستم Identity را نشان می‌دهد.

  • aspnet_Roles --> AspNetRoles
  • aspnet_Users, aspnet_Membership --> AspNetUsers
  • aspnet_UsersInRoles --> AspNetUserRoles

جداول AspNetUserClaims و AspNetUserLogins خالی هستند. فیلد تفکیک کننده (Discriminator) در جدول AspNetUsers باید مطابق نام کلاس مدل باشد، که در مرحله بعدی تعریف خواهد شد. همچنین ستون PasswordHash به فرم 'encrypted password|password salt|password format' می‌باشد. این شما را قادر می‌سازد تا از رمزنگاری برای ذخیره و بازیابی کلمه‌های عبور استفاده کنید. این مورد نیز در ادامه مقاله بررسی شده است.



ساختن مدل‌ها و صفحات عضویت

بصورت پیش فرض سیستم ASP.NET Identity برای دریافت و ذخیره اطلاعات در دیتابیس عضویت از Entity Framework استفاده می‌کند. برای آنکه بتوانیم با جداول موجود کار کنیم، می‌بایست ابتدا مدل هایی که الگوی دیتابیس را نمایندگی می‌کنند ایجاد کنیم. برای این کار مدل‌های ما یا باید اینترفیس‌های موجود در Identity.Core را پیاده سازی کنند، یا می‌توانند پیاده سازی‌های پیش فرض را توسعه دهند. پیاده سازی‌های پیش فرض در Microsoft.AspNet.Identity.EntityFramework وجود دارند.
در نمونه ما، جداول AspNetRoles, AspNetUserClaims, AspNetLogins و AspNetUserRole ستون هایی دارند که شباهت زیادی به پیاده سازی‌های پیش فرض سیستم Identity دارند. در نتیجه می‌توانیم از کلاس‌های موجود، برای Map کردن الگوی جدید استفاده کنیم. جدول AspNetUsers ستون‌های جدیدی نیز دارد. می‌توانیم کلاس جدیدی بسازیم که از IdentityUser ارث بری کند و آن را گسترش دهیم تا این فیلدهای جدید را پوشش دهد.
پوشه ای با نام Models بسازید (در صورتی که وجود ندارد) و کلاسی با نام User به آن اضافه کنید.

کلاس User باید کلاس IdentityUser را که در اسمبلی Microsoft.AspNet.Identity.EntityFramework وجود دارد گسترش دهد. خاصیت هایی را تعریف کنید که نماینده الگوی جدول AspNetUser هستند. خواص ID, Username, PasswordHash و SecurityStamp در کلاس IdentityUser تعریف شده اند، بنابراین این خواص را در لیست زیر نمی‌بینید.

  public class User : IdentityUser
    {
        public User()
        {
            CreateDate = DateTime.Now;
            IsApproved = false;
            LastLoginDate = DateTime.Now;
            LastActivityDate = DateTime.Now;
            LastPasswordChangedDate = DateTime.Now;
            LastLockoutDate = DateTime.Parse("1/1/1754");
            FailedPasswordAnswerAttemptWindowStart = DateTime.Parse("1/1/1754");
            FailedPasswordAttemptWindowStart = DateTime.Parse("1/1/1754");
        }

        public System.Guid ApplicationId { get; set; }
        public string MobileAlias { get; set; }
        public bool IsAnonymous { get; set; }
        public System.DateTime LastActivityDate { get; set; }
        public string MobilePIN { get; set; }
        public string Email { get; set; }
        public string LoweredEmail { get; set; }
        public string LoweredUserName { get; set; }
        public string PasswordQuestion { get; set; }
        public string PasswordAnswer { get; set; }
        public bool IsApproved { get; set; }
        public bool IsLockedOut { get; set; }
        public System.DateTime CreateDate { get; set; }
        public System.DateTime LastLoginDate { get; set; }
        public System.DateTime LastPasswordChangedDate { get; set; }
        public System.DateTime LastLockoutDate { get; set; }
        public int FailedPasswordAttemptCount { get; set; }
        public System.DateTime FailedPasswordAttemptWindowStart { get; set; }
        public int FailedPasswordAnswerAttemptCount { get; set; }
        public System.DateTime FailedPasswordAnswerAttemptWindowStart { get; set; }
        public string Comment { get; set; }
    }

حال برای دسترسی به دیتابیس مورد نظر، نیاز به یک DbContext داریم. اسمبلی Microsoft.AspNet.Identity.EntityFramework کلاسی با نام IdentityDbContext دارد که پیاده سازی پیش فرض برای دسترسی به دیتابیس ASP.NET Identity است. نکته قابل توجه این است که IdentityDbContext آبجکتی از نوع TUser را می‌پذیرد. TUser می‌تواند هر کلاسی باشد که از IdentityUser ارث بری کرده و آن را گسترش می‌دهد.

در پوشه Models کلاس جدیدی با نام ApplicationDbContext بسازید که از IdentityDbContext ارث بری کرده و از کلاس User استفاده می‌کند.

public class ApplicationDbContext : IdentityDbContext<User>
{
        
}

مدیریت کاربران در ASP.NET Identity توسط کلاسی با نام UserManager انجام می‌شود که در اسمبلی Microsoft.AspNet.Identity.EntityFramework قرار دارد. چیزی که ما در این مرحله نیاز داریم، کلاسی است که از UserManager ارث بری می‌کند و آن را طوری توسعه می‌دهد که از کلاس User استفاده کند.

در پوشه Models کلاس جدیدی با نام UserManager بسازید.

public class UserManager : UserManager<User>
{
        
}

کلمه عبور کاربران بصورت رمز نگاری شده در دیتابیس ذخیره می‌شوند. الگوریتم رمز نگاری SQL Membership با سیستم ASP.NET Identity تفاوت دارد. هنگامی که کاربران قدیمی به سایت وارد می‌شوند، کلمه عبورشان را توسط الگوریتم‌های قدیمی SQL Membership رمزگشایی می‌کنیم، اما کاربران جدید از الگوریتم‌های ASP.NET Identity استفاده خواهند کرد.

کلاس UserManager خاصیتی با نام PasswordHasher دارد. این خاصیت نمونه ای از یک کلاس را ذخیره می‌کند، که اینترفیس IPasswordHasher را پیاده سازی کرده است. این کلاس هنگام تراکنش‌های احراز هویت کاربران استفاده می‌شود تا کلمه‌های عبور را رمزنگاری/رمزگشایی شوند. در کلاس UserManager کلاس جدیدی بنام SQLPasswordHasher بسازید. کد کامل را در لیست زیر مشاهده می‌کنید.

public class SQLPasswordHasher : PasswordHasher
{
        public override string HashPassword(string password)
        {
            return base.HashPassword(password);
        }

        public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
        {
            string[] passwordProperties = hashedPassword.Split('|');
            if (passwordProperties.Length != 3)
            {
                return base.VerifyHashedPassword(hashedPassword, providedPassword);
            }
            else
            {
                string passwordHash = passwordProperties[0];
                int passwordformat = 1;
                string salt = passwordProperties[2];
                if (String.Equals(EncryptPassword(providedPassword, passwordformat, salt), passwordHash, StringComparison.CurrentCultureIgnoreCase))
                {
                    return PasswordVerificationResult.SuccessRehashNeeded;
                }
                else
                {
                    return PasswordVerificationResult.Failed;
                }
            }
        }

        //This is copied from the existing SQL providers and is provided only for back-compat.
        private string EncryptPassword(string pass, int passwordFormat, string salt)
        {
            if (passwordFormat == 0) // MembershipPasswordFormat.Clear
                return pass;

            byte[] bIn = Encoding.Unicode.GetBytes(pass);
            byte[] bSalt = Convert.FromBase64String(salt);
            byte[] bRet = null;

            if (passwordFormat == 1)
            { // MembershipPasswordFormat.Hashed 
                HashAlgorithm hm = HashAlgorithm.Create("SHA1");
                if (hm is KeyedHashAlgorithm)
                {
                    KeyedHashAlgorithm kha = (KeyedHashAlgorithm)hm;
                    if (kha.Key.Length == bSalt.Length)
                    {
                        kha.Key = bSalt;
                    }
                    else if (kha.Key.Length < bSalt.Length)
                    {
                        byte[] bKey = new byte[kha.Key.Length];
                        Buffer.BlockCopy(bSalt, 0, bKey, 0, bKey.Length);
                        kha.Key = bKey;
                    }
                    else
                    {
                        byte[] bKey = new byte[kha.Key.Length];
                        for (int iter = 0; iter < bKey.Length; )
                        {
                            int len = Math.Min(bSalt.Length, bKey.Length - iter);
                            Buffer.BlockCopy(bSalt, 0, bKey, iter, len);
                            iter += len;
                        }
                        kha.Key = bKey;
                    }
                    bRet = kha.ComputeHash(bIn);
                }
                else
                {
                    byte[] bAll = new byte[bSalt.Length + bIn.Length];
                    Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
                    Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
                    bRet = hm.ComputeHash(bAll);
                }
            }

            return Convert.ToBase64String(bRet);
    }
}



دقت کنید تا فضاهای نام System.Text و System.Security.Cryptography را وارد کرده باشید.

متد EncodePassword کلمه عبور را بر اساس پیاده سازی پیش فرض SQL Membership رمزنگاری می‌کند. این الگوریتم از System.Web گرفته می‌شود. اگر اپلیکیشن قدیمی شما از الگوریتم خاصی استفاده می‌کرده است، همینجا باید آن را منعکس کنید. دو متد دیگر نیز بنام‌های HashPassword و VerifyHashedPassword نیاز داریم. این متدها از EncodePassword برای رمزنگاری کلمه‌های عبور و تایید آنها در دیتابیس استفاده می‌کنند.

سیستم SQL Membership برای رمزنگاری (Hash) کلمه‌های عبور هنگام ثبت نام و تغییر آنها توسط کاربران، از PasswordHash, PasswordSalt و PasswordFormat استفاده می‌کرد. در روند مهاجرت، این سه فیلد در ستون PasswordHash جدول AspNetUsers ذخیره شده و با کاراکتر '|' جدا شده اند. هنگام ورود کاربری به سایت، اگر کله عبور شامل این فیلدها باشد از الگوریتم SQL Membership برای بررسی آن استفاده می‌کنیم. در غیر اینصورت از پیاده سازی پیش فرض ASP.NET Identity استفاده خواهد شد. با این روش، کاربران قدیمی لازم نیست کلمه‌های عبور خود را صرفا بدلیل مهاجرت اپلیکیشن ما تغییر دهند.

کلاس UserManager را مانند قطعه کد زیر بروز رسانی کنید.

public UserManager()
            : base(new UserStore<User>(new ApplicationDbContext()))
        {
            this.PasswordHasher = new SQLPasswordHasher();
 }

ایجاد صفحات جدید مدیریت کاربران

قدم بعدی ایجاد صفحاتی است که به کاربران اجازه ثبت نام و ورود را می‌دهند. صفحات قدیمی SQL Membership از کنترل هایی استفاده می‌کنند که با ASP.NET Identity سازگار نیستند. برای ساختن این صفحات جدید به این مقاله مراجعه کنید. از آنجا که در این مقاله پروژه جدید را ساخته ایم و پکیج‌های لازم را هم نصب کرده ایم، می‌توانید مستقیما به قسمت Adding Web Forms for registering users to your application بروید.
چند تغییر که باید اعمال شوند:
  • فایل‌های Register.aspx.cs و Login.aspx.cs از کلاس UserManager استفاده می‌کنند. این ارجاعات را با کلاس UserManager جدیدی که در پوشه Models ساختید جایگزین کنید.
  • همچنین ارجاعات استفاده از کلاس IdentityUser را به کلاس User که در پوشه Models ساختید تغییر دهید.
  • لازم است توسعه دهنده مقدار ApplicationId را برای کاربران جدید طوری تنظیم کند که با شناسه اپلیکیشن جاری تطابق داشته باشد. برای این کار می‌توانید پیش از ساختن حساب‌های کاربری جدید در فایل Register.aspx.cs ابتدا شناسه اپلیکیشن را بدست آورید و اطلاعات کاربر را بدرستی تنظیم کنید.
مثال: در فایل Register.aspx.cs متد جدیدی تعریف کنید که جدول aspnet_Applications را بررسی میکند و شناسه اپلیکیشن را بر اساس نام اپلیکیشن بدست می‌آورد.
private Guid GetApplicationID()
{
    using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString))
            {
                string queryString = "SELECT ApplicationId from aspnet_Applications WHERE ApplicationName = '/'"; //Set application name as in database

                SqlCommand command = new SqlCommand(queryString, connection);
                command.Connection.Open();

                var reader = command.ExecuteReader();
                while (reader.Read())
                {
                    return reader.GetGuid(0);
                }

            return Guid.NewGuid();
        }
}


  حال می‌توانید این مقدار را برای آبجکت کاربر تنظیم کنید.
var currentApplicationId = GetApplicationID();

User user = new User() { UserName = Username.Text,
ApplicationId=currentApplicationId, …};
در این مرحله می‌توانید با استفاده از اطلاعات پیشین وارد سایت شوید، یا کاربران جدیدی ثبت کنید. همچنین اطمینان حاصل کنید که کاربران پیشین در نقش‌های مورد نظر وجود دارند.
مهاجرت به ASP.NET Identity مزایا و قابلیت‌های جدیدی را به شما ارائه می‌کند. مثلا کاربران می‌توانند با استفاده از تامین کنندگان ثالثی مثل Facebook, Google, Microsoft, Twitter و غیره به سایت وارد شوند. اگر به سورس کد این مقاله مراجعه کنید خواهید دید که امکانات OAuth نیز فعال شده اند.
در این مقاله انتقال داده‌های پروفایل کاربران بررسی نشد. اما با استفاده از نکات ذکر شده می‌توانید پروفایل کاربران را هم بسادگی منتقل کنید. کافی است مدل‌های لازم را در پروژه خود تعریف کرده و با استفاده از اسکریپت‌های SQL داده‌ها را انتقال دهید.
نظرات مطالب
تولید SiteMap استاندارد و ایجاد یک ActionResult اختصاصی برای Return کردن SiteMap تولید شده
- قسمت‌های مختلف را کوئری می‌گیرید (یعنی به صورت پویا تولید می‌شود و نه اینکه جایی ثبت شوند)، union می‌کنید (چندین قسمت هست؟ مهم نیست. لیست‌های تمام آن‌ها باید به فرمت Sitemap نگاشت شده و یکی شوند) و نهایتا به صورت یک خروجی واحد ارائه می‌دهید. بحث لینک‌های تکراری در اینجا مهم نیست (و مرتبط است به محتوای تکراری با آدرس‌های مختلف در یک سایت و نه لینک‌های سایت‌مپ). ممکن هست یک سایت در طول هفته اصلا رکورد جدیدی نداشته باشد. موتور جستجوگری که به آن می‌رسد یک سری لینک مشخص و تکراری را هر روز دریافت می‌کند و مدیریت ثبت آن‌ها هم کاملا مشخص است و خودشان تکراری ثبت نمی‌کنند.
برای درک بهتر نحوه‌ی ایندکس شدن sitemap نیاز هست به Google Webmaster Tools وب سایت خودتان را اضافه کنید و سپس token آن‌را در سایت معرفی کنید. پس از مدتی می‌توانید گزارش بگیرید که دقیقا چه مواردی را ثبت کرده‌است و به چه نحوی.
- ضمنا امکان تعریف Sitemap index هم وجود دارد (جهت تعریف چندین سایت‌مپ در سایت). اطلاعات بیشتر در پروتکل آن آماده‌است. ولی در کل فرمت تولید آن سایت‌مپ‌ها هم به همین نحو استاندارد معرفی شده‌است. فقط در این حالت خاص که سایت‌مپ در ریشه‌ی سایت قرار نمی‌گیرد و یا به آن اشاره نمی‌کند، محدود هستید به معرفی لینک‌های همان پوشه و یا بازه.
نظرات مطالب
وی‍‍ژگی های پیشرفته ی AutoMapper - قسمت دوم
ممنون از مطلب مفیدتون
من از کلاس کمکی ای که برای نگاشت چند کلاس به یک کلاس معرفی کردید استفاده کردم ولی به یک مشکل برخورد کرد. 
من یک کلاس کالا دارم و یک کلاس برند که میخوام این دو رو به یک کلاس دیگه نگاشت بدم. 
در اون کلاسی که معرفی کردید یک به یک کلاس‌های منبع را نگاشت میدهد. هنگام استفاده وقتی که که مثلا اول کلاس کالا و بعد کلاس برند رو برای نگاشت ارسال میکنم ، کلیه پارامتر هایی که از کلاس کالا در کلاس مقصد وجود دارند مقادیر صفر و Null میگیرند و فقط مقادیر کلاس برند در کلاس مقصد مقادیر درستشان را دارند. البته من در متد configure  هنگام برای نگاشت کلاس دوم پارامتر‌های کلاس اول را Ignore کردم. ولی باز هم همین مشکل پیش اومد!
یک سوال دیگه هم داشتم :
بهتر نیست به جای اینکه ما در Controller دو تابع از هر کلاس را فراخوانی کنیم(یک تابع برای دریافت لیست کالاها و یکی برای لیست برندها) و کار نگاشت این دو به صورتیکه معرفی شد انجام دهیم، یک تابع که ترکیبی از دو کلاس را برمیگرداند (تابعی که با استفاده از Join کلاس‌ها مقادیر مورد نظرمان را برگرداند، یعنی برند هر کالا را به آن بچسباند) را اجرا کرده و سپس مقادیر آن را به صورت معمول نگاشت کنیم؟
با سپاس و ببخشید که طولانی شد
مطالب دوره‌ها
دریافت قالب WpfFramework.vsix و نحوه نصب و راه اندازی آن
فایل قالب پروژه دوره جاری را از آدرس ذیل می‌توانید دریافت کنید:

نصب ابتدایی آن بسیار ساده است و نکته خاصی ندارد.
پس از نصب، یکبار VS.NET را بسته و سپس باز کنید. این قالب جدید، ذیل قسمت پروژه‌های ویندوز مرتبط با ویژوال سی شارپ، ظاهر خواهد شد:


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



نکته مهم! برنامه کامپایل نمی‌شود!

به عمد، جهت کاهش حجم قالب دریافتی فوق، فایل‌های باینری آن پیوست نشده‌اند. وگرنه باید بالای 30 مگابایت را دریافت می‌کردید که واقعا نیازی نیست.
اما اگر به هر پروژه داخل Solution دقت کنید، فایل متنی packages.config آن نیز پیوست شده است. به کمک این فایل‌ها به سادگی می‌توان تمام وابستگی‌ها را از طریق NuGet بازیابی کرد.
برای این منظور ابتدا به اینترنت متصل شده (مهم) و سپس بر روی Solution کلیک راست کرده و گزینه فعال سازی Restore کلیه بسته‌های نیوگت را انتخاب کنید.


پس از اینکار، آخرین نگارش NuGet.exe از اینترنت دریافت و به پروژه اضافه می‌شود:


اکنون اگر Solution را Build کنید، اولین کاری که صورت خواهد گرفت، دریافت کلیه وابستگی‌ها از سایت NuGet است. اندکی تامل کنید تا اینکار تمام شود.
پس از پایان کار دریافت، پوشه packages در کنار فایل sln پروژه ایجاد شده، تشکیل می‌شود.
یکبار وجود آن‌را بررسی کنید.


اکنون اگر برنامه را Build کنید احتمالا پیغام می‌دهد که Fody را نمی‌تواند پیدا کند با اینکه دریافت شده و در پوشه packages موجود است. هر زمان، هر پیغام خطایی در مورد Fody مشاهده کردید، فقط یکبار VS.NET را بسته و باز کنید. مشکل حل می‌شود!

تنها کاری که پس از بازسازی پوشه packages بهتر است صورت گیرد (اختیاری است البته)، صدور دستور ذیل در خط فرمان پاور شل است:
 PM> Update-Package
با این دستور، دریافت کامل مجددی از اینترنت صورت نمی‌گیرد؛ مگر اینکه به روز رسانی جدیدی را یافت کند. در سایر حالات از کش داخل سیستم اطلاعات را دریافت می‌کند.
پس از اینکار، نیاز است یکبار VS.NET را بسته و مجددا باز کنید (مهم) تا تمام وابستگی‌ها به درستی بارگذاری شوند. خصوصا بسته Fody که کار AOP را انجام می‌دهد. در غیر اینصورت موفق به Build پروژه نخواهید شد.

بنابراین به صورت خلاصه:
الف) یکبار گزینه فعال سازی Restore کلیه بسته‌های نیوگت را انتخاب کنید.
ب) پروژه را Build کنید تا وابستگی‌ها را از سایت NuGet دریافت کند.
ج) دستور Update-Package را اجرا نمائید (اختیاری).
ج) VS.NET را پس از سه مرحله فوق، یکبار بسته و باز کنید.

در کل بسته‌های مورد استفاده به این شرح هستند:
PM> Install-Package MahApps.Metro -Pre 
PM> Install-Package MahApps.Metro.Resources 
PM> Install-Package EntityFramework
PM> Install-Package structuremap
PM> Install-Package PropertyChanged.Fody
PM> Install-Package MvvmLight 
PM> Install-Package Microsoft.SqlServer.Compact

یک نکته جانبی
فید NuGet در VS.NET به Https تنظیم شده است. اگر دسترسی به Https برای شما به کندی صورت می‌گیرد فقط کافی است مسیر فید آن‌را در منوی Tools، گزینه‌ی Options، ذیل قسمت Package manager یافته و به http://nuget.org/api/v2 تغییر دهید؛ یعنی به Http خالی، بجای Https؛ تا سرعت دریافت بسته‌های NuGet مورد نظر افزایش یابند.



اجرای پروژه و برنامه

پس از این مراحل و Build موفقیت آمیز پروژه، برنامه را اجرا کنید. در اولین بار اجرای برنامه به صورت خودکار بانک اطلاعاتی به همراه ساختار جداول تشکیل می‌شوند. اما ... با خطای زیر مواجه خواهید شد:
 The path is not valid. Check the directory for the database. [ Path = D:\...\bin\Debug\Db\db.sdf ]
علت این است که در فایل app.config پروژه ریشه برنامه :
<connectionStrings>
    <clear/>
    <add name="MyWpfFrameworkContext"
         connectionString="Data Source=|DataDirectory|\Db\db.sdf;Max Buffer Size=30720;File Mode=Read Write;"
         providerName="System.Data.SqlServerCE.4.0" />
  </connectionStrings>
مسیر تشکیل بانک اطلاعاتی به پوشه db کنار فایل اجرایی برنامه تنظیم شده است. این پوشه db را در پوشه bin\debug ایجاد کرده و سپس برنامه را اجرا کنید.


برای ورود به برنامه از نام کاربری Admin و کلمه عبور 123456 استفاده نمائید.
این کاربر پیش فرض در کلاس MyWpfFrameworkMigrations لایه داده‌های برنامه، توسط متد addRolesAndAdmin اضافه شده است.


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




به روز رسانی 1.1
جهت سازگاری با EF 6 ، StructureMap 3 و همچنین VS 2013، پروژه به روز شد: WpfFrmwork_1.1.zip

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

Roslyn از زمان ارائه‌ی نگارش Visual Studio 14 CTP3 با ویژوال استودیو یکپارچه شد. بنابراین اگر از نگارش نهایی آن یعنی Visual Studio 2015 استفاده می‌کنید، اولین پیشنیاز کار با آن را در اختیار دارید. البته نگارش پیش نمایش آن نیز برای VS 2013 ارائه شد که در این تاریخ منسوخ شده درنظر گرفته می‌شود و دیگر به روز رسانی نخواهد شد. بنابراین نیاز است حتما Visual Studio 2015 را نصب کنید.
در حین نصب Visual Studio 2015 نیز باید گزینه‌ی نصب SDK آن‌را انتخاب کرده باشید، تا امکان تولید فایل‌های VSIX مرتبط را پیدا کنید. از این جهت که قالب‌های پروژه‌ی Roslyn، امکان تولید افزونه‌های آنالیز کدها را نیز میسر می‌کنند.
علاوه بر این‌ها نیاز است Syntax Visualizer و قالب‌های پروژه‌ی مخصوص Roslyn را نیز جداگانه نصب کنید. برای این منظور به آدرس ذیل مراجعه کرده و افزونه‌ی آن‌را نصب کنید.
NET Compiler Platform SDK.

پس از نصب این افزونه، دو قابلیت جدید به Visual studio اضافه می‌شوند:
الف) در منوی view، ذیل گزینه‌ی other windows، گزینه‌ی جدیدی به نام Syntax visualizer اضافه شده‌است.
ب) ذیل گزینه‌ی extensibility پنجره‌ی new project، تعدادی قالب پروژه‌ی جدید مخصوص Roslyn نصب شده‌اند.


البته باید دقت داشت که این قالب‌های جدید برای نمایش در این لیست، حداقل نیاز به انتخاب دات نت 4.5.2 را دارند.

از Syntax visualizer جهت مشاهده‌ی ریز جزئیات ساختار دستوری کدهای جاری استفاده می‌شود:


زمانیکه گزینه‌ی View->Other windows->Syntax visualizer را اجرا کردید، اندکی صبر کنید تا بارگذاری شود و سپس اشاره‌گر ویرایشگر را به قسمتی دلخواه حرکت دهید. در این حالت می‌توان ساختار کدها را بر اساس تفسیر Roslyn مشاهده کرد. همچنین اگر در Syntax visualizer یک نود را انتخاب کنید، بلافاصله معادل آن در ادیتور ویژوال استودیو انتخاب می‌شود. از این ابزار جهت تولید آنالایزرهای مبتنی بر Roslyn زیاد استفاده می‌شود.


تغییرات خط فرمان Visual Studio 2015 نسبت به نگارش‌های پیشین آن

خط فرمان Visual Studio 2015 به همراه کامپایلر قدیمی خط فرمان C# 5 و همچنین کامپایلر جدید مبتنی بر Roslyn مخصوص C# 6 است. این کامپایلرها را در مسیرهای ذیل می‌توانید پیدا کنید:
کامپایلر جدید مبتنی بر Roslyn در مسیر C:\Program Files (x86)\MSBuild\14.0\Bin قرار دارد و کامپایلر قدیمی C# 5 در مسیر نصب دات نت فریم ورک یعنی C:\Windows\Microsoft.NET\Framework\v4.0.30319 قرار گرفته‌است.


همانطور که مشاهده می‌کنید، نگارش کامپایلر مبتنی بر Roslyn، یک است و شماره build آن، بیانگر تاریخ کامپایل آن است:
50618=2015/06/18

اکنون شاید این سؤال مطرح شود که کامپایلر پیش فرض کدام است؟ برای یافتن آن، به منوی استارت ویندوز مراجعه کرده و گزینه‌ی developer command prompt for vs 2015 را اجرا کنید. در اینجا اگر دستور csc را اجرا کنید، خروجی آن همان کامپایلر مبتنی بر Roslyn است:


در همینجا اگر سوئیچ ? را برای مشاهده‌ی راهنمای کامپایلر خط فرمان Roslyn صادر کنید، یکی از گزینه‌‌های جدید آن، سوئیچ analyzer است:
 C:\Program Files (x86)\Microsoft Visual Studio 14.0>csc /?
Microsoft (R) Visual C# Compiler version 1.0.0.50618
Copyright (C) Microsoft Corporation. All rights reserved.


Visual C# Compiler Options

 /analyzer:<file list> Run the analyzers from this assembly
(Short form: /a)
آنالایزرها اسمبلی‌ها و افزونه‌هایی هستند که قابلیت وارد شدن به pipeline کامپایلر را داشته و امکان دخالت در صدور خطاها و اخطارهای صادر شده‌ی توسط آن‌را دارند. به عبارتی این کامپایلر خط فرمان جدید، افزونه پذیر است.
همچنین این کامپایلر نسبت به نگارش قبلی آن، دارای سوئیچ و گزینه‌ی parallel نیز می‌باشد که به کمک ساختارهای داده‌ی immutable جدید دات نت مسیر شده‌است.