مطالب
بررسی خطاهای ممکن در حین راه اندازی اولیه برنامه‌های ASP.NET Core در IIS
نحوه‌ی نصب و راه اندازی برنامه‌های ASP.NET Core را در IIS، پیشتر در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 22 - توزیع برنامه توسط IIS» بررسی کردیم. در این مطلب می‌خواهیم به تعدادی از خطاهای ممکن در حین راه اندازی اولیه‌ی این نوع برنامه‌ها بپردازیم.


خطای 500.19


این خطا زمانی رخ می‌دهد که ماژول هاستینگ ASP.NET Core، توسط IIS شناسایی نشده باشد. نصب مجدد آن این مشکل را برطرف می‌کند.
لیست تمام ماژول‌های هاستینگ را همواره در اینجا می‌توانید پیدا کنید.


خطای 502.5 و یا گاهی از اوقات 502


باید دقت داشته باشید که اگر تنظیم disableStartUpErrorPage در IIS فعال باشد (قابل افزودن به تگ aspNetCore تنظیمات وب کانفیگ ذیل)، صرفا خطای 502 را دریافت می‌کنید.

این خطا به معنای شکست در اجرای ماژول هاستینگ ASP.NET Core است و ممکن است به یکی از دلایل ذیل ایجاد شده باشد:
الف) در حین اجرای برنامه‌ی شما، استثنایی در کدهای فایل آغازین startup.cs برنامه، رخ داده‌است.
ب) پورت مورد استفاده‌ی برنامه، توسط پروسه‌ی دیگری در حال استفاده است.
ج) برنامه‌ی شما برای SDK با نگارش 1.1.2 تنظیم و کامپایل شده‌است؛ اما بر روی سرور حداکثر، SDK نگارش 1.1.1 نصب شده‌است.
د) ممکن است پروسه‌ی IIS قادر به یافتن و حتی اجرای dotnet.exe نباشد.

برای لاگ کردن مورد «الف»، باید لاگ کردن خطاهای برنامه را در web.config آن فعالسازی کنید:
<system.webServer>
   <handlers>
     <add name="aspNetCore" path="*" verb="*"
modules="AspNetCoreModule" resourceType="Unspecified" />
    </handlers>
    <aspNetCore processPath="dotnet" arguments=".\MyApp.dll" stdoutLogEnabled="true"
stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="true" />
</system.webServer>
چند نکته:
- اگر این مورد به مسیر logs\stdout\. تنظیم شده‌است، باید پوشه‌ی logs را در ریشه‌ی پروژه به صورت دستی ایجاد کنید؛ و گرنه IIS آن‌را به صورت خودکار ایجاد نخواهد کرد.
- کاربر App Pool برنامه (با نام پیش‌فرض « IIS AppPool\DefaultAppPool») باید دسترسی نوشتن در این پوشه را داشته باشد؛ وگرنه فایل لاگی در آن ایجاد نخواهد شد.
- همچنین اگر با رعایت تمام این موارد، محتوای این فایل تولید شده باز هم خالی بود، یکبار IIS را ری‌استارت کنید. ممکن است IIS کار نوشتن در فایل لاگ را تمام نکرده باشد و با این کار مجبور به تکمیل و بستن فایل می‌شود.  

- برای حالت «ب» قبل از هر تغییری، یکبار کل سرور را ری‌استارت کنید.

- برای مورد «ج» نیز باید آخرین SDK هاستینگ را بر روی سرور نصب کنید.
لیست تمام SDKهای نصب شده‌ی بر روی سیستم را در مسیر «C:\Program Files\dotnet\sdk» می‌توانید مشاهده کنید. همچنین دستور «dotnet --list-sdks» نیز لیست SDKهای نصب شده را نمایش می‌دهد.

- برای رفع حالت «د»، نیاز است این موارد را بررسی کنید:
1- «Load User Profile» را به true تنظیم کنید.


برای اینکار به قسمت Application pools مراجعه کرده و تنظیمات پیشرفته‌ی App pool مورد استفاده را ویرایش کنید (تصویر فوق).
این تنظیم برای دائمی کردن کلیدهای رمزنگاری برنامه‌های ASP.NET Core نیز ضروری است و باید جزو چک لیست نصب برنامه‌های ASP.NET Core قرار گیرد.
2- مورد «د» حتی می‌تواند به علت عدم تعریف مسیر «C:\Program Files\dotnet\» در path ویندوز باشد. برای این منظور دستور env:path$ را در power shell اجرا کنید و بررسی کنید که آیا این مسیر در خروجی آن موجود است یا خیر؟ اگر نبود، پس از اضافه کردن آن به path ویندوز، باید یکبار IIS را هم ریست کنید تا این تنظیمات جدید را بخواند.
3- مورد «د» ممکن است به علت اشتباه تنظیم پوشه‌ی اصلی برنامه در IIS نیز باشد. یعنی dotnet.exe قادر به یافتن اسمبلی‌های برنامه نیست.
4- برای رفع مورد «د» دو دسترسی دیگر را نیز باید بررسی کنید:
الف) آیا کاربر Application pool برنامه به پوشه‌ی برنامه دسترسی read & execute را دارد یا خیر؟
ب) آیا کاربر Application pool برنامه به پوشه‌ی C:\Program Files\dotnet دسترسی read & execute را دارد یا خیر؟
اگر خیر، نحوه‌ی دسترسی دادن به آن‌ها به صورت زیر است:
Right click on the folder -> Properties -> Security tab -> Click at Edit button ->
Enter `IIS AppPool\DefaultAppPool` user (IIS AppPool\<app_pool_name>) -> Click at Check names -> OK ->
Then give it `read & execute` or other permissions.


خطای 502.3 و یا گاهی از اوقات 500


این خطا به صورت خلاصه به معنای «Bad Gateway: Forwarder Connection Error» است و زمانی رخ می‌دهد که پروسه‌ی dotnet.exe به درخواست رسیده شده یا پاسخی نداده‌است (مشاهده خطای 0x80072EE2  یا  ERROR_WINHTTP_TIMEOUT) و یا بیش از اندازه این پاسخ دهی طول کشیده‌است (این تنظیمات را در configuration editor می‌توانید مشاهده کنید که در حقیقت همان تگ aspNetCore در تنظیمات وب کانفیگ فوق است).


برای دیباگ بهتر این مورد نیاز است علاوه بر تنظیم web.config فوق، به فایل appsettings.json مراجعه کرده و سطح پیش فرض لاگ کردن اطلاعات را که warning است به information تغییر دهید:
"Console": {
     "LogLevel": {
        "Default": "Information"
     }
}
در این حالت درخواستی که پردازش نشده‌است نیز در لاگ‌ها حضور خواهد داشت و ممکن است این درخواست به علت عدم تنظیم CORS بدون پاسخ باقی مانده باشد.
و یا اگر پردازشی دارید که بیش از 2 دقیقه طول می‌کشد (مطابق تنظیمات تصویر فوق)، می‌توانید مقدار request time out را بیشتر کنید.


خطای 0x80004005 : 80008083

Application ‘<IIS path>’ with physical root ‘<Application path>’ failed to start 
process with commandline ‘”dotnet” .\MyApp.dll’, ErrorCode = ‘0x80004005 : 80008083.
خطای 0x80008083 به معنای تداخل نگارش‌ها است و خطای 0x80004005 به معنای مفقود بودن یک فایل یا عدم دسترسی به آن است.
این خطا زمانی رخ می‌دهد که برنامه‌ی خود را ارتقاء داده باشید، اما ماژول هاستینگ ASP.NET Core را بر روی سرور به روز رسانی نکرده باشید.


خطای 500.19

HTTP Error 500.19 - Internal Server Error
The requested page cannot be accessed because the related configuration data for the page is invalid.
اگر برنامه‌ی شما از امکانات URL Rewrite خود IIS استفاده می‌کند، عدم نصب بودن آن بر روی سرور، این خطا را سبب خواهد شد.
برای اینکار ابتدا IIS را متوقف کنید. سپس SDK جدید را نصب و پس از آن IIS را مجددا راه اندازی نمائید.


خطای 503

برنامه اجرا نشده و سطر ذیل در Event Viewer ویندوز قابل مشاهده است:
 The Module DLL C:\WINDOWS\system32\inetsrv\aspnetcore.dll failed to load.  The data is the error.
اگر اخیرا سیستم عامل را ارتقاء داده‌اید، ممکن است این خطا را دریافت کنید. راه حل آن نصب مجدد ماژول هاستینگ ASP.NET Core است تا نصب قبلی تعمیر شود.
مطالب
پروسیجرها و شنود پارامترها در SQL Server
در اس کیو ال سرور 2016، قابلیت غیر فعال نمودن parameter sniffing در سطح بانک اطلاعاتی مهیا شده است. اما چرا؟


قبل از پاسخگویی به سؤال بالا، به یک سری مقدمات نیاز است:

وقتی یک کوئری به اس کیو ال ارسال می‌شود، چه اتفاقی رخ می‌دهد؟
وقتی یک کوئری ارسال می‌شود، تعدادی از پروسس‌ها بر روی کوئری شروع به فعالیت‌هایی مانند مهیا نمودن داده‌های بازگشتی، یا ذخیره سازی و ... می‌کنند.
 پروسس‌ها به دو دسته زیر تقسیم می‌شوند:
  1. پروسس‌هایی که در relational engine رخ می‌دهند
  2. پروسس‌هایی که در storage engine رخ می‌دهند

در relational engine، هر کوئری pars شده و سپس بوسیله query optimizer پردازش و پلن اجرایی (execution plan) آن که بفرمت باینری است، ایجاد می‌شود و به storage engine ارسال می‌گردد. در storage engine پروسس‌هایی مانند قفل گذاری، نگهداری ایندکس‌ها و تراکنش‌ها رخ می‌دهد. هنگامیکه اس کیو ال سرور کوئری را دریافت می‌نمایند، آن را بلافاصله به relational engine ارسال می‌کند. سپس نحو (syntax) آن بررسی می‌شود؛ این عمل  query parsing نامیده می‌شود. خروجی عملیات پارسر، یک ساختار درختی (query tree) است. این ساختار درختی مشخص کننده مراحل لازم جهت اجرای کوئری ارائه شده می‌باشد.
اگر یک کوئری شامل DML نباشد (مانند ساخت جدول)، علمیات بهبود برروی آن صورت نخواهد گرفت. ولی در صورتیکه کوئری ارسالی، DML باشد، درخت اشاره شده در بالا به algebrizer فرستاده می‌شود که وظیفه آن تفسیر و بررسی کلیه نام اشیاء، جداول و ستون‌های اشاره شده در متن کوئری است. فرآیند algebrizer بسیار مهم و حیاتی است؛ بدلیل اینکه در کوئری ممکن است اشاره کننده‌هایی به اشیایی باشند که در بانک اطلاعاتی موجود نیست. خروجی algebrizer یک query processor tree باینری است که به بهبود دهنده کوئری ارسال می‌گردد. 

معرفی Query Optimizer (بهبود دهنده پرس و جو)

بهبود دهنده، بهترین مسیر اجرای کوئری را مشخص می‌کند. این بهبود دهنده است که مشخص می‌کند که اطلاعات بوسیله ایندکس دریافت شوند، یا اینکه از چه اتصالی استفاده شود و الی آخر. این تصمیمات براساس محاسبات هزینه‌های (میزان پردازش لازم cpu و I/O) پلن اجرایی صورت خواهد پذیرفت. بهمین دلیل به پلن cost-based نیز شناخته می‌شود.
هنگامیکه کوئری ساده‌ای مانند دریافت اطلاعات از یک جدول، که بر روی آن ایندکس گذاری انجام نشده‌است، ارسال شود، بهبود دهنده بجای مشخص نمودن یک پلن مناسب بهینه، از یک پلن ساده (trivial) استفاده می‌کند. ولی برعکس در صورتیکه کوئری trivial نباشد (یعنی مثلا کوئری به گونه‌ای باشد که از ایندکس‌ها به شکل صحیحی استفاده شده باشند)، بهبود دهنده یک پلن مناسب را براساس اطلاعات آماری مهیا شده در اس کیو ال سرور، تولید و انتخاب می‌نماید.

اطلاعات آماری از ستون‌ها و ایندکس‌ها جمع آوری می‌شود. این اطلاعات شامل نحوه توزیع داده، یکتایی و انتخاب شوندگی است. این اطلاعات توسط یک histogram ارائه می‌شود. اگر اطلاعات آماری برای یک ستون و یا ایندکس وجود داشته باشد، بهبود دهنده از آن‌ها برای محاسبات خود استفاده خواهد کرد. اطلاعات آماری بصورت خودکار برای تمام ایندکس‌ها و یا هر ستونی که بشود بر روی آن‌ها where یا join نوشت، فراهم خواهد شد.
بهبود دهنده با مقایسه پلن‌ها براساس بررسی تفاوت‌های انواع joinها، چیدمان مجدد ترتیب join و بررسی ایندکس‌های مختلف و سایر فعالیت‌های دیگر، پلن مناسب را انتخاب و از آن استفاده می‌کند. در طی هر کدام از فعالیت‌های اشاره شده، زمان اجرای آن‌ها نیز تخمین زده (estimated cost) خواهد شد و در پایان، زمان کل تخمینی بدست خواهد آمد و بهبود دهنده از این زمان برای انتخاب پلن مناسب بهره خواهد برد. باید توجه داشت که این زمان تقریبی است. زمانیکه بهبود دهنده پلن اجرایی انتخاب می‌کند، یک actual plan را ایجاد و در حافظه ذخیره می‌شود؛ بنام plan cache. البته درصورتیکه پلن مشابه و بهینه‌تری وجود نداشته باشد. 

استفاده مجدد از پلن ها

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

هنگامیکه کوئری به سرور ارسال می‌شود، بوسیله بهبود دهنده، یک estimated plan ایجاد خواهد شد و قبل از اینکه به storage engine ارسال شود، بهبود دهنده estimated plan را با actual execution planهای موجود در plan cache مقایسه می‌کند. در صورتیکه یک actual plan را مطابق با estimated plan پیدا نماید، از آن مجدد استفاده خواهد کرد. این استفاده مجدد به عدم تحمیل سربار اضافه‌ای به سرور جهت کوئری‌های بزرگ و پیچیده که در زمان واحد، هزاران بار اجرا خواهند شد، منجر می‌شود.
هر پلن فقط یکبار در حافظه ذخیره خواهد شد. ولی در مواقعی با تشخیص بهبود دهنده و هزینه پلن، یک کوئری می‌تواند پلن دیگری نیز داشته باشد. بنابراین پلن دوم نیز با مجموعه عملیاتی متفاوت، جهت اجرای موازی (parallel execution) برای یک کوئری ایجاد و در حافظه ذخیره می‌شود.
پلن‌های اجرایی برای همیشه در حافظه باقی نخواهند ماند. پلن‌های اجرایی دارای طول عمری طبق فرمول حاصل ضرب هزینه، در تعداد دفعات می‌باشند. مثلاً پلنی با هزینه 10 و تعداد دفعات اجرای 5، طول عمر 50 را خواهد داشت. پروسس lazywriter که یک پروسس داخلی است وظیفه آزاد سازی تمام انواع کش‌ها، از جمله پلن کش را دارد. این پروسس در بازه‌های مشخص، تمام اشیاء درون حافظه را بررسی کرده و یک واحد از طول عمر آن‌ها می‌کاهد.
در موارد زیر، یک پلن از حافظه پاک خواهد شد:
1. به حافظه بیشتری نیاز باشد
2. طول عمر پلن صفر شده باشد 

حال فرض کنید شما یک پروسیجر یا یک کوئری پارامتری دارید (پارامتر ورودی: شناسه سفارش یا نال) که کلیه محصولات سفارش داده شده یا محصولات یک سفارش خاص را نمایش می‌دهد. هنگامی که SQL Server optimizer پلن این کوئری را ایجاد می‌کند و یا آن را کامپایل می‌کند، به پارامترهای ورودی این پروسیجر گوش می‌دهد (نال یا یک شناسه سفارش). optimizer بوسیله column statistics از تعداد رکوردهایی که بازگشت داده می‌شود، برآوردی می‌کند (مثلا 40 رکورد). سپس یک پلن مناسب را انتخاب می‌کند و آن را برای اجرا ارسال می‌کند و پلن را ذخیره می‌نماید.
جمله آخر، معمولا باعث ایجاد مشکل می‌شوند.
اگر optimizer تکست کوئری مشابهی را مشاهده نماید، ولی با پارامترهای متفاوت، به کش پلن مراجعه کرده و اگر در آن جا قرار داشت، از آن مجددا استفاده می‌نماید. این استفاده مجدد خوب است؛ اما  درصورتیکه پارامتر ارسالی نال باشد چه اتفاقی رخ می‌دهد؟ جدول سفارشات محصول بسیار حجیم است و متاسفانه از پلنی که برای بازگشت 40 رکورد قبلا ایجاد شده، برای بازگشت این حجم بالای از رکوردها استفاده می‌شود که این کشنده است.
هیچ تضمینی وجود ندارد که از وقوع این اتفاق جلوگیری نمایید؛ اما می‌توانید در هنگام توسعه، پروسیجر را شناسایی و نسبت به رفع آنها اقدام نمایید. ابتدا کش پلن را خالی نمایید و سپس پروسیجر را با مقادیر متفاوت، اجرا نمایید. در صورتیکه پلن‌های متفاوتی مشاهده نمودید، این یک علامت هشدار است و می‌بایست نسبت به رفع آن‌ها اقدام فوری نمایید. 
مطالب
آموزش فایرباگ - #10 - DOM Panel
در پنل DOM توابع و متغییرهایی که در صفحه وجود دارند بصورت درختی نمایش داده می‌شوند. object‌ها و array‌ها قابل باز شدن هستند و بصورت درختی می‌توانید محتویات آن‌ها را مشاهده کنید. توابع هم بصورت لینک هستند که با کلیک برون آن ها، کد مربوط در پنل Script نمایش داده می‌شود. توجه کنید که محتویاتی که مشاهده می‌کنید برای همان لحظه ای است که پنل را باز کردید و برای مشاهده تغییرات ثانویه باید محتویات پنل را بروزرسانی کرد.

قبل از بررسی این پنل اجازه دهید نگاهی به تعریف DOM بیندازیم.

DOM چیست؟
مدل شیءگرای سند یا دام (DOM - Document Object Model) عنوان یکی از دو ساختوارۀ (architecture) اصلی است (در کنار اس‌اِی‌اکس) که بر اساس آن سندهای اکس‌ام‌ال را به اشیایی که در بردارندهٔ آن است، تجزیه نموده، و آن‌ها را به‌صورت یک ساختار درختی داده‌ها در فضای حافظه اصلی پهن می‌کند. ساختوارۀ دام، نه به زبان برنامه‌نویسی خاصّی وابستگی دارد و نه به سکّوی برنامه‌نویسی ویژه‌ای، بلکه، به منظور اجراء و پیاده‌سازی آن باید از یک زبان برنامه‌نویسی بلندتراز همچون جاوا، سی‌شارپ، جاوااسکریپت یا مشابه آن‌ها سود بجوییم. آنسوی رابط کاربر سند با مدلی شیءگرا نمایانده می‌شود. 


Options Menu
این منو با راست کلیک کردن بروی نام پنل یا کلیک کردن بروی مثلثی که روی پنل قرار دارد، نمایش داده می‌شود.

  • Show User-defined Properties
    در صورت فعال بودن، پراپرتی هایی که توسط کاربر به صفحه اضافه شده اند را نمایش می‌دهد.
  • Show User-defined Functions
    در صورت فعال بودن، توابعی که توسط کاربر به صفحه اضافه شده اند را نمایش می‌دهد.
  • Show DOM Properties
    در صورت فعال بودن، پراپرتی هایی که بصورت پیشفرض در DOM وجود دارند را نمایش می‌دهد.
  • Show DOM Functions
     در صورت فعال بودن، توابعی که بصورت پیشفرض در DOM وجود دارند را نمایش می‌دهد.
  • Show DOM Constants
    در صورت فعال بودن، const هایی که بصورت پیشفرض در DOM وجود دارند را نمایش می‌دهد.
  •  Show Inline Event Handlers
    در صورت فعال بودن، رویدادهایی که بصورت خطی در تگ‌ها تعریف شده اند را نمایش می‌دهد.
  • Show Closures
    در صورت فعال بودن، Closure‌ها را نمایش می‌دهد.
  • Show Own Properties Only
    در صورت فعال بودن، فقط پراپرتی هایی که بروی خود شئ تعریف شده اند را نمایش می‌دهد.
  • Show Enumerable Properties Only
    در صورت فعال بودن، فقط پراپرتی‌های شمارشی را نمایش می‌دهد.
  • Refresh
    محتویات پنل را بروزرسانی می‌کند.


Property Path
این قسمت در بالاترین بخش پنل قرار دارد و وظیفه‌ی آن نمایش مسیر شئ از خود شئ تا window است.
همچنین با راست کلیک کردن بروی این قسمت دو گزینه نمایش داده می‌شود. Refresh برای بروزسازی آدرس نمایش داده شده و Use in Command Line هم برای استفاده از شئ در خط فرمان است. پس از راست کلیک کردن بروی یک شئ و انتخاب گزینه‌ی Use in Command Line فایرباگ تمرکز برنامه را به خط فرمان منتقل می‌کند و شئ را تحت متغییری به نام $p در خط فرمان کپی می‌کند.


رنگ ها
برای مشخص کردن نوع متغییرهای این پنل، فایرباگ برای هر نوع متغییر از یک رنگ استفاده می‌کند.
اشیاء
، اشیاء DOM ، توابع Getter ، توابع تعریفی کاربر ، توابع DOM ، توابع Constructor ، پراپرتی‌های Read-only 


Auto-Completion
مشابه پنل‌های Console, CSS, HTML در این پنل هم امکان اعمال تغییرات همراه با قابلیت تکمیل خودکار وجود دارد.


localStorage
در HTML5 سیستمی برای ذخیره مقادیر در سمت کاربر، به نام localStorage معرفی شد. در این پنل می‌توانید محتویات آن را بررسی/ویرایش کنید. برای کار با توابع آن هم می‌توانید از پنل Console استفاده کنید. ( همچنین می‌توانید از پنل Console بصورت Popup در این پنل و پنل‌های دیگر هم استفاده کنید. به تصویر زیر توجه فرمایید. )
توجه کنید که برای مشاهده‌ی این شئ، باید گزینه‌ی Show DOM Properties فعال باشد و همیچنین در Property Path، شئ window فعال باشد.

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


Context Menu
با راست کلیک کردن در قسمت‌های مختلف پنل، منوهای متفاوتی را خواهید دید. همچنین با راست کلیک کردن بروی مقادیر پراپرتی ها، منوی متناسب با آن مقدار را خواهید دید. مثلا اگر بروی یک تگ HTML در این پنل راست کلیک کنید، منویی که خواهید دید همان منویی است که در پنل HTML مشاهده می‌کردید.

 گزینه Context
توضیحات
Copy Name
Property List نام پراپرتی را در حافظه کپی می‌کند.
Copy Path Property List آدرس پراپرتی را در حافظه کپی می‌کند.
Copy Value
String and Number values محتوای پراپرتی را در حافظه کپی می‌کند.
Edit Property... Property List ( پراپرتی و توابع کاربر )
پراپرتی را به حالت ویرایش می‌آورد.
Delete Property Property List ( پراپرتی و توابع کاربر ) پراپرتی را حذف می‌کند.
Break On Property Change Property List ( پراپرتی و توابع کاربر ) مشابه پاراگرف قبلی.
Refresh Property List, Property Path محتویات پنل را بروزرسانی می‌کند.

برای توابع هم دو منوی اضافی وجود دارد:

    • ‪ Log Calls to "<function name>"
      فراخوانی‌های تابع مورد نظر را Log می‌کند. ( برای توضیحات بیشتر دستور monitor که از توابع خط فرمان است را ملاحظه بفرمایید. )
    • Copy Function
      نام و بنده‌ی تابع را در حافظه کپی می‌کند.
مطالب
معرفی JSON Web Token


دو روش کلی و پرکاربرد اعتبارسنجی سمت سرور، برای برنامه‌های سمت کاربر وب وجود دارند:
الف) Cookie-Based Authentication که پرکاربردترین روش بوده و در این حالت به ازای هر درخواست، یک کوکی جهت اعتبارسنجی کاربر به سمت سرور ارسال می‌شود (و برعکس).


ب) Token-Based Authentication که بر مبنای ارسال یک توکن امضاء شده به سرور، به ازای هر درخواست است.


مزیت‌های استفاده‌ی از روش مبتنی بر توکن چیست؟

 • Cross-domain / CORS: کوکی‌ها و CORS آنچنان با هم سازگاری ندارند؛ چون صدور یک کوکی وابسته‌است به دومین مرتبط به آن و استفاده‌ی از آن در سایر دومین‌ها عموما پذیرفته شده نیست. اما روش مبتنی بر توکن، وابستگی به دومین صدور آن‌را ندارد و اصالت آن بر اساس روش‌های رمزنگاری تصدیق می‌شود.
 • بدون حالت بودن و مقیاس پذیری سمت سرور: در حین کار با توکن‌ها، نیازی به ذخیره‌ی اطلاعات، داخل سشن سمت سرور نیست و توکن موجودیتی است خود شمول (self-contained). به این معنا که حاوی تمام اطلاعات مرتبط با کاربر بوده و محل ذخیره‌ی آن در local storage و یا کوکی سمت کاربر می‌باشد.
 • توزیع برنامه با CDN: حین استفاده از روش مبتنی بر توکن، امکان توزیع تمام فایل‌های برنامه (جاوا اسکریپت، تصاویر و غیره) توسط CDN وجود دارد و در این حالت کدهای سمت سرور، تنها یک API ساده خواهد بود.
 • عدم در هم تنیدگی کدهای سمت سرور و کلاینت: در حالت استفاده‌ی از توکن، این توکن می‌تواند از هرجایی و هر برنامه‌ای صادر شود و در این حالت نیازی نیست تا وابستگی ویژه‌ای بین کدهای سمت کلاینت و سرور وجود داشته باشد.
 • سازگاری بهتر با سیستم‌های موبایل: در حین توسعه‌ی برنامه‌های بومی پلتفرم‌های مختلف موبایل، کوکی‌ها روش مطلوبی جهت کار با APIهای سمت سرور نیستند. تطابق یافتن با روش‌های مبتنی بر توکن در این حالت ساده‌تر است.
 • CSRF: از آنجائیکه دیگر از کوکی استفاده نمی‌شود، نیازی به نگرانی در مورد حملات CSRF نیست. چون دیگر برای مثال امکان سوء استفاده‌ی از کوکی فعلی اعتبارسنجی شده، جهت صدور درخواست‌هایی با سطح دسترسی شخص لاگین شده وجود ندارد؛ چون این روش کوکی را به سمت سرور ارسال نمی‌کند.
 • کارآیی بهتر: حین استفاده‌ی از توکن‌ها، به علت ماهیت خود شمول آن‌ها، رفت و برگشت کمتری به بانک اطلاعاتی صورت گرفته و سرعت بالاتری را شاهد خواهیم بود.
 • امکان نوشتن آزمون‌های یکپارچگی ساده‌تر: در حالت استفاده‌ی از توکن‌ها، آزمودن یکپارچگی برنامه، نیازی به رد شدن از صفحه‌ی لاگین را ندارد و پیاده سازی این نوع آزمون‌ها ساده‌تر از قبل است.
 • استاندارد بودن: امروزه همینقدر که استاندارد JSON Web Token را پیاده سازی کرده باشید، امکان کار با انواع و اقسام پلتفرم‌ها و کتابخانه‌ها را خواهید یافت.


اما JWT یا JSON Web Token چیست؟

JSON Web Token یا JWT یک استاندارد وب است (RFC 7519) که روشی فشرده و خود شمول (self-contained) را جهت انتقال امن اطلاعات، بین مقاصد مختلف را توسط یک شیء JSON، تعریف می‌کند. این اطلاعات، قابل تصدیق و اطمینان هستند؛ از این‌رو که به صورت دیجیتال امضاء می‌شوند. JWTها توسط یک کلید مخفی (با استفاده از الگوریتم HMAC) و یا یک جفت کلید خصوصی و عمومی (توسط الگوریتم RSA) قابل امضاء شدن هستند.
در این تعریف، واژه‌هایی مانند «فشرده» و «خود شمول» بکار رفته‌اند:
 - «فشرده بودن»: اندازه‌ی شیء JSON یک توکن در این حالت کوچک بوده و به سادگی از طریق یک URL و یا پارامترهای POST و یا داخل یک HTTP Header قابل ارسال است و به دلیل کوچک بودن این اندازه، انتقال آن نیز سریع است.
 - «خود شمول»: بار مفید (payload) این توکن، شامل تمام اطلاعات مورد نیاز جهت اعتبارسنجی یک کاربر است؛ تا دیگر نیازی به کوئری گرفتن هر باره‌ی از بانک اطلاعاتی نباشد (در این روش مرسوم است که فقط یکبار از بانک اطلاعاتی کوئری گرفته شده و اطلاعات مرتبط با کاربر را امضای دیجیتال کرده و به سمت کاربر ارسال می‌کنند).


چه زمانی بهتر است از JWT استفاده کرد؟

اعتبارسنجی: اعتبارسنجی یک سناریوی متداول استفاده‌ی از JWT است. زمانیکه کاربر به سیستم لاگین کرد، هر درخواست بعدی او شامل JWT خواهد بود که سبب می‌شود کاربر بتواند امکان دسترسی به مسیرها، صفحات و منابع مختلف سیستم را بر اساس توکن دریافتی، پیدا کند. برای مثال روش‌های «Single Sign On» خود را با JWT انطباق داده‌اند؛ از این جهت که سربار کمی را داشته و همچنین به سادگی توسط دومین‌های مختلفی قابل استفاده هستند.
انتقال اطلاعات: توکن‌های با فرمت JWT، روش مناسبی جهت انتقال اطلاعات امن بین مقاصد مختلف هستند؛ زیرا قابل امضاء بوده و می‌توان اطمینان حاصل کرد که فرستنده دقیقا همانی است که ادعا می‌کند و محتوای ارسالی دست نخورده‌است.


ساختار یک JWT به چه صورتی است؟

JWTها دارای سه قسمت جدا شده‌ی با نقطه هستند؛ مانند xxxxx.yyyyy.zzzzz و شامل header، payload و signature می‌باشند.
الف) Header
Header عموما دارای دو قسمت است که نوع توکن و الگوریتم مورد استفاده‌ی توسط آن را مشخص می‌کند:
 {
   "alg": "HS256",
   "typ": "JWT"
}
نوع توکن در اینجا JWT است و الگوریتم‌های مورد استفاده، عموما  HMAC SHA256 و یا RSA هستند.

ب) payload
payload یا «بار مفید» توکن، شامل claims است. منظور از claims، اطلاعاتی است در مورد موجودیت مدنظر (عموما کاربر) و یک سری متادیتای اضافی. سه نوع claim وجود دارند:
Reserved claims: یک سری اطلاعات مفید و از پیش تعیین شده‌ی غیراجباری هستند؛ مانند:
iss یا صادر کنند (issuer)، exp یا تاریخ انقضاء، sub یا عنوان (subject) و aud یا مخاطب (audience)
 Public claims: می‌تواند شامل اطلاعاتی باشد که توسط IANA JSON Web Token Registry پیشتر ثبت شده‌است و فضاهای نام آن‌ها تداخلی نداشته باشند.
Private claims: ادعای سفارشی هستند که جهت انتقال داده‌ها بین مقاصد مختلف مورد استفاده قرار می‌گیرند.
یک نمونه‌ی payload را در اینجا ملاحظه می‌کنید:
 {
   "sub": "1234567890",
   "name": "John Doe",
   "admin": true
}
این اطلاعات (هم header و هم payload)، به صورت base64 انکد شده و به JWT اضافه می‌شوند.

ج) signature
یک نمونه فرمول محاسبه‌ی امضای دیجیتال پیام JWT به صورت ذیل است:
 HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
در اینجا بر اساس الگوریتم HMAC SHA256، هدر و بار مفید پیام به صورت base64 دریافت و به کمک یک کلید مخفی، محاسبه و به JWT اضافه می‌شود تا توسط آن بتوان اصالت پیام و فرستنده‌ی آن‌را تائید کرد. امضاء نیز در نهایت با فرمت base64 در اینجا انکد می‌شود:


یک نمونه مثال تولید این نوع توکن‌ها را در آدرس https://jwt.io می‌توانید بررسی کنید.
در این سایت اگر به قسمت دیباگر آن مراجعه کنید، برای نمونه قسمت payload آن قابل ویرایش است و تغییرات را بلافاصله در سمت چپ، به صورت انکد شده نمایش می‌دهد.


یک نکته‌ی مهم: توکن‌ها امضاء شده‌اند؛ نه رمزنگاری شده

همانطور که عنوان شد، توکن‌ها از سه قسمت هدر، بار مفید و امضاء تشکیل می‌شوند (header.payload.signature). اگر از الگوریتم HMACSHA256 و کلید مخفی shhhh برای امضای بار مفید ذیل استفاده کنیم:
 {
   "sub": "1234567890",
   "name": "Ado Kukic",
   "admin": true
}
یک چنین خروجی باید حاصل شود:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFkbyBLdWtpYyIsImFkbWluIjp0cnVlLCJpYXQiOjE0NjQyOTc4ODV9.Y47kJvnHzU9qeJIN48_bVna6O0EDFiMiQ9LpNVDFymM
در اینجا باید دقت داشت که هدر و بار مفید آن، صرفا با الگوریتم base64 انکد شده‌اند و این به معنای رمزنگاری نیست. به عبارتی می‌توان اطلاعات کامل هدر و بار مفید آن‌را به دست آورد. بنابراین هیچگاه اطلاعات حساسی را مانند کلمات عبور، در اینجا ذخیره نکنید.
البته امکان رمزنگاری توسط JSON Web Encryption نیز پیش بینی شده‌است (JWE).


از JWT در برنامه‌ها چگونه استفاده می‌شود؟

زمانیکه کاربر، لاگین موفقی را به سیستم انجام می‌دهد، یک توکن امن توسط سرور صادر شده و با فرمت JWT به سمت کلاینت ارسال می‌شود. این توکن باید به صورت محلی در سمت کاربر ذخیره شود. عموما از local storage برای ذخیره‌ی این توکن استفاده می‌شود؛ اما استفاده‌ی از کوکی‌ها نیز منعی ندارد. بنابراین دیگر در اینجا سشنی در سمت سرور به ازای هر کاربر ایجاد نمی‌شود و کوکی سمت سروری به سمت کلاینت ارسال نمی‌گردد.
سپس هر زمانیکه کاربری قصد داشت به یک صفحه یا محتوای محافظت شده دسترسی پیدا کند، باید توکن خود را به سمت سرور ارسال نماید. عموما اینکار توسط یک header سفارشی Authorization به همراه Bearer schema صورت می‌گیرد و یک چنین شکلی را دارد:
 Authorization: Bearer <token>
این روش اعتبارسنجی، بدون حالت (stateless) است؛ از این جهت که وضعیت کاربر، هیچگاه در سمت سرور ذخیره نمی‌گردد. API سمت سرور، ابتدا به دنبال هدر Authorization فوق، در درخواست دریافتی می‌گردد. اگر یافت شد و اصالت آن تائید شد، کاربر امکان دسترسی به منبع محافظت شده را پیدا می‌کند. نکته‌ی مهم اینجا است که چون این توکن‌ها «خود شمول» هستند و تمام اطلاعات لازم جهت اعطای دسترسی‌های کاربر به او، در آن وجود دارند، دیگر نیازی به رفت و برگشت به بانک اطلاعاتی، جهت تائید این اطلاعات تصدیق شده، نیست. به همین جهت کارآیی و سرعت بالاتری را نیز به همراه خواهند داشت.


نگاهی به محل ذخیره سازی JWT و نکات مرتبط با آن

محل متداول ذخیره‌ی JWT ها، در local storage مرورگرها است و در اغلب سناریوها نیز به خوبی کار می‌کند. فقط باید دقت داشت که local storage یک sandbox است و محدود به دومین جاری برنامه و از طریق برای مثال زیر دامنه‌های آن قابل دسترسی نیست. در این حالت می‌توان JWT را در کوکی‌های ایجاد شده‌ی در سمت کاربر نیز ذخیره کرد که چنین محدودیتی را ندارند. اما باید دقت داشت که حداکثر اندازه‌ی حجم کوکی‌ها 4 کیلوبایت است و با افزایش claims ذخیره شده‌ی در یک JWT و انکد شدن آن، این حجم ممکن است از 4 کیلوبایت بیشتر شود. بنابراین باید به این نکات دقت داشت.
امکان ذخیره سازی توکن‌ها در session storage مرورگرها نیز وجود دارد. session storage بسیار شبیه است به local storage اما به محض بسته شدن مرورگر، پاک می‌شود.
اگر از local storage استفاده می‌کنید، حملات Cross Site Request Forgery در اینجا دیگر مؤثر نخواهند بود. اما اگر به حالت استفاده‌ی از کوکی‌ها برای ذخیره‌ی توکن‌ها سوئیچ کنید، این مساله همانند قبل خواهد بود و مسیر است. در این حالت بهتر است طول عمر توکن‌ها را تاحد ممکن کوتاه تعریف کنید تا اگر اطلاعات آن‌ها فاش شد، به زودی بی‌مصرف شوند.


انقضاء و صدور مجدد توکن‌ها به چه صورتی است؟

توکن‌های بدون حالت، صرفا بر اساس بررسی امضای پیام رسیده کار می‌کنند. به این معنا که یک توکن می‌تواند تا ابد معتبر باقی بماند. برای رفع این مشکل باید exp یا تاریخ انقضای متناسبی را به توکن اضافه کرد. برای برنامه‌های حساس این عدد می‌تواند 15 دقیقه باشد و برای برنامه‌های کمتر حساس، چندین ماه.
اما اگر در این بین قرار به ابطال سریع توکنی بود چه باید کرد؟ (مثلا کاربری را در همین لحظه غیرفعال کرده‌اید)
یک راه حل آن، ثبت رکورد‌های تمام توکن‌های صادر شده در بانک اطلاعاتی است. برای این منظور می‌توان یک فیلد id مانند را به توکن اضافه کرد و آن‌را صادر نمود. این idها را نیز در بانک اطلاعاتی ذخیره می‌کنیم. به این ترتیب می‌توان بین توکن‌های صادر شده و کاربران و اطلاعات به روز آن‌ها ارتباط برقرار کرد. در این حالت برنامه علاوه بر بررسی امضای توکن، می‌تواند به لیست idهای صادر شده و ذخیره شده‌ی در دیتابیس نیز مراجعه کرده و اعتبارسنجی اضافه‌تری را جهت باطل کردن سریع توکن‌ها انجام دهد. هرچند این روش دیگر آنچنان stateless نیست، اما با دنیای واقعی سازگاری بیشتری دارد.


حداکثر امنیت JWTها را چگونه می‌توان تامین کرد؟

- تمام توکن‌های خود را با یک کلید قوی، امضاء کنید و این کلید تنها باید بر روی سرور ذخیره شده باشد. هر زمانیکه سرور توکنی را از کاربر دریافت می‌کند، این سرور است که باید کار بررسی اعتبار امضای پیام رسیده را بر اساس کلید قوی خود انجام دهد.
- اگر اطلاعات حساسی را در توکن‌ها قرار می‌دهید، باید از JWE یا JSON Web Encryption استفاده کنید؛ زیرا JWTها صرفا دارای امضای دیجیتال هستند و نه اینکه رمزنگاری شده باشند.
- بهتر است توکن‌ها را از طریق ارتباطات غیر HTTPS، ارسال نکرد.
- اگر از کوکی‌ها برای ذخیره سازی آن‌ها استفاده می‌کنید، از HTTPS-only cookies استفاده کنید تا از Cross-Site Scripting XSS attacks در امان باشید.
- مدت اعتبار توکن‌های صادر شده را منطقی انتخاب کنید.
مطالب
Roslyn #5
بررسی Semantic Models

همانطور که از قسمت قبل به‌خاطر دارید، برای دسترسی به اطلاعات semantics، نیاز به یک context مناسب که همان Compilation API است، می‌باشد. این context دارای اطلاعاتی مانند دسترسی به تمام نوع‌های تعریف شده‌ی توسط کاربر و متادیتاهای ارجاعی، مانند کلاس‌های پایه‌ی دات نت فریم‌ورک است. بنابراین پس از ایجاد وهله‌ای از Compilation API، کار با فراخوانی متد GetSemanticModel آن ادامه می‌یابد. در ادامه با مثال‌هایی، کاربرد این متد را بررسی خواهیم کرد.


ساختار جدید Optional

خروجی‌های تعدادی از متدهای Roslyn با ساختار جدیدی به نام Optional ارائه می‌شوند:
    public struct Optional<T>
    {
        public bool HasValue { get; }
        public T Value { get; }
    }
این ساختار که بسیار شبیه است به ساختار قدیمی <Nullable<T، منحصر به Value types نیست و Reference types را نیز شامل می‌شود و بیانگر این است که آیا یک Reference type، واقعا مقدار دهی شده‌است یا خیر؟


دریافت مقادیر ثابت Literals

فرض کنید می‌خواهیم مقدار ثابت ; int x = 42 را دریافت کنیم. برای اینکار ابتدا باید syntax tree آن تشکیل شود و سپس نیاز به یک سری حلقه و if و else و همچنین بررسی نال بودن بسیاری از موارد است تا به نود مقدار ثابت 42 برسیم. سپس متد GetConstantValue مربوط به GetSemanticModel را بر روی آن فراخوانی می‌کنیم تا به مقدار واقعی آن که ممکن است در اثر محاسبات جاری تغییر کرده باشد، برسیم.
اما روش بهتر و توصیه شده، استفاده از CSharpSyntaxWalker است که در انتهای قسمت سوم معرفی شد:
class ConsoleWriteLineWalker : CSharpSyntaxWalker
{
    public ConsoleWriteLineWalker()
    {
        Arguments = new List<ExpressionSyntax>();
    }
 
    public List<ExpressionSyntax> Arguments { get; }
 
    public override void VisitInvocationExpression(InvocationExpressionSyntax node)
    {
        var member = node.Expression as MemberAccessExpressionSyntax;
        var type = member?.Expression as IdentifierNameSyntax;
        if (type != null && type.Identifier.Text == "Console" && member.Name.Identifier.Text == "WriteLine")
        {
            if (node.ArgumentList.Arguments.Count == 1)
            {
                var arg = node.ArgumentList.Arguments.Single().Expression;
                Arguments.Add(arg);
                return;
            }
        }
 
        base.VisitInvocationExpression(node);
    }
}
اگر به کدهای ادامه‌ی بحث دقت کنید، قصد داریم مقادیر ثابت آرگومان‌های Console.WriteLine را استخراج کنیم. به همین جهت در این SyntaxWalker، نوع Console و متد WriteLine آن مورد بررسی قرار گرفته‌اند. اگر این نود دارای یک تک آرگومان بود، آین آرگومان استخراج شده و به لیست آرگومان‌های خروجی این کلاس اضافه می‌شود.
در ادامه نحوه‌ی استفاده‌ی از این SyntaxWalker را ملاحظه می‌کنید. در اینجا ابتدا سورس کدی حاوی یک سری Console.WriteLine که دارای تک آرگومان‌های ثابتی هستند، تبدیل به syntax tree می‌شود. سپس از روی آن CSharpCompilation تولید می‌گردد تا بتوان به اطلاعات semantics دسترسی یافت:
static void getConstantValue()
{
    // Get the syntax tree.
    var code = @"
                using System;
 
                class Foo
                {
                    void Bar(int x)
                    {
                        Console.WriteLine(3.14);
                        Console.WriteLine(""qux"");
                        Console.WriteLine('c');
                        Console.WriteLine(null);
                        Console.WriteLine(x * 2 + 1);
                    }
                }
                ";
 
    var tree = CSharpSyntaxTree.ParseText(code);
    var root = tree.GetRoot();
 
    // Get the semantic model from the compilation.
    var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
    var comp = CSharpCompilation.Create("Demo").AddSyntaxTrees(tree).AddReferences(mscorlib);
    var model = comp.GetSemanticModel(tree);
 
    // Traverse the tree.
    var walker = new ConsoleWriteLineWalker();
    walker.Visit(root);
 
 
    // Analyze the constant argument (if any).
    foreach (var arg in walker.Arguments)
    {
        var val = model.GetConstantValue(arg);
        if (val.HasValue)
        {
            Console.WriteLine(arg + " has constant value " + (val.Value ?? "null") + " of type " + (val.Value?.GetType() ?? typeof(object)));
        }
        else
        {
            Console.WriteLine(arg + " has no constant value");
        }
    }
}
در ادامه با استفاده از CSharpCompilation و متد GetSemanticModel آن به SemanticModel جاری دسترسی خواهیم یافت. اکنون SyntaxWalker را وارد به حرکت بر روی ریشه‌ی syntax tree سورس کد آنالیز شده می‌کنیم. به این ترتیب لیست آرگومان‌های متدهای Console.WriteLine بدست می‌آیند. سپس با فراخوانی متد model.GetConstantValue بر روی هر آرگومان دریافتی، مقادیر آن‌ها با فرمت <Optional<T استخراج می‌شوند.
خروجی نمایش داده شده‌ی توسط برنامه به صورت ذیل است:
 3.14 has constant value 3.14 of type System.Double
"qux" has constant value qux of type System.String
'c' has constant value c of type System.Char
null has constant value null of type System.Object
x * 2 + 1 has no constant value


درک مفهوم Symbols

اینترفیس ISymbol در Roslyn، ریشه‌ی تمام Symbolهای مختلف مدل سازی شده‌ی در آن است که تعدادی از آن‌ها را در تصویر ذیل مشاهده می‌کنید:


API کار با Symbols بسیار شبیه به API کار با Reflection است با این تفاوت که در زمان آنالیز کدها رخ می‌دهد و نه در زمان اجرای برنامه. همچنین در Symbols API امکان دسترسی به اطلاعاتی مانند locals, labels و امثال آن نیز وجود دارد که با استفاده از Reflection زمان اجرای برنامه قابل دسترسی نیستند. برای مثال فضاهای نام در Reflection صرفا به صورت رشته‌ای، با دات جدا شده از نوع‌های آنالیز شده‌ی توسط آن است؛ اما در اینجا مطابق تصویر فوق، یک اینترفیس مجزای خاص خود را دارد. جهت سهولت کار کردن با Symbols، الگوی Visitor با معرفی کلاس پایه‌ی SymbolVisitor نیز پیش بینی شده‌است.
static void workingWithSymbols()
{
    // Get the syntax tree.
    var code = @"
                using System;
 
                class Foo
                {
                    void Bar(int x)
                    {
                        // #insideBar
                    }
                }
 
                class Qux
                {
                    protected int Baz { get; set; }
                }
                ";
 
    var tree = CSharpSyntaxTree.ParseText(code);
    var root = tree.GetRoot();
 
    // Get the semantic model from the compilation.
    var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
    var comp = CSharpCompilation.Create("Demo").AddSyntaxTrees(tree).AddReferences(mscorlib);
    var model = comp.GetSemanticModel(tree);
 
    // Traverse enclosing symbol hierarchy.
    var cursor = code.IndexOf("#insideBar");
    var barSymbol = model.GetEnclosingSymbol(cursor);
    for (var symbol = barSymbol; symbol != null; symbol = symbol.ContainingSymbol)
    {
        Console.WriteLine(symbol);
    }
 
    // Analyze accessibility of Baz inside Bar.
    var bazProp = ((CompilationUnitSyntax)root)
        .Members.OfType<ClassDeclarationSyntax>()
        .Single(m => m.Identifier.Text == "Qux")
        .Members.OfType<PropertyDeclarationSyntax>()
        .Single();
    var bazSymbol = model.GetDeclaredSymbol(bazProp);
    var canAccess = model.IsAccessible(cursor, bazSymbol);
}
یکی از کاربردهای مهم Symbols API دریافت اطلاعات Symbols نقطه‌ای خاص از کدها می‌باشد. برای مثال در محل اشاره‌گر ادیتور، چه Symbols ایی تعریف شده‌اند و از آن‌ها در مباحث ساخت افزونه‌های آنالیز کدها زیاد استفاده می‌شود. نمونه‌ای از آن‌را در قطعه کد فوق ملاحظه می‌کنید. در اینجا با استفاده از متد GetEnclosingSymbol، سعی در یافتن Symbols قرار گرفته‌ی در ناحیه‌ی insideBar# کدهای فوق داریم؛ با خروجی ذیل که نام demo.exe آن از نام CSharpCompilation آن گرفته شده‌است:
 Foo.Bar(int)
Foo
<global namespace>
Demo.exe
Demo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null


همچنین در ادامه‌ی کد، توسط متد IsAccessible قصد داریم بررسی کنیم آیا Symbol قرار گرفته در محل کرسر، دسترسی به خاصیت protected کلاس Qux را دارد یا خیر؟ که پاسخ آن خیر است.


آشنایی با Binding symbols

یکی از مراحل کامپایل کد، binding نام دارد و در این مرحله است که اطلاعات Symbolic هر نود از Syntax tree دریافت می‌شود. برای مثال در اینجا مشخص می‌شود که این x، آیا یک متغیر محلی است، یا یک فیلد و یا یک خاصیت؟
مثال ذیل بسیار شبیه است به مثال getConstantValue ابتدای بحث، با این تفاوت که در حلقه‌ی آخر کار از متد GetSymbolInfo استفاده شده‌است:
static void bindingSymbols()
{
    // Get the syntax tree.
    var code = @"
                using System;
 
                class Foo
                {
                    private int y;
 
                    void Bar(int x)
                    {
                        Console.WriteLine(x);
                        Console.WriteLine(y);
 
                        int z = 42;
                        Console.WriteLine(z);
 
                        Console.WriteLine(a);
                    }
                }";
 
    var tree = CSharpSyntaxTree.ParseText(code);
    var root = tree.GetRoot();
 
    // Get the semantic model from the compilation.
    var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
    var comp = CSharpCompilation.Create("Demo").AddSyntaxTrees(tree).AddReferences(mscorlib);
    var model = comp.GetSemanticModel(tree);
 
    // Traverse the tree.
    var walker = new ConsoleWriteLineWalker();
    walker.Visit(root);
 
    // Bind the arguments.
    foreach (var arg in walker.Arguments)
    {
        var symbol = model.GetSymbolInfo(arg);
        if (symbol.Symbol != null)
        {
            Console.WriteLine(arg + " is bound to " + symbol.Symbol + " of type " + symbol.Symbol.Kind);
        }
        else
        {
            Console.WriteLine(arg + " could not be bound");
        }
    }
}
با این خروجی:
 x is bound to int of type Parameter
y is bound to Foo.y of type Field
z is bound to z of type Local
a could not be bound
در مثال فوق، با استفاده از Syntax Walker طراحی شده در ابتدای بحث که کار استخراج آرگومان‌های متدهای Console.WriteLine را انجام می‌دهد، قصد داریم بررسی کنیم، هر آرگومان به چه Symbol ایی بایند شده‌است و نوعش چیست؟ برای مثال Console.WriteLine اول که از پارامتر x استفاده می‌کند، نوع x مورد استفاده‌اش چیست؟ آیا فیلد است، متغیر محلی است یا یک پارامتر؟ این اطلاعات را با استفاده از متد model.GetSymbolInfo می‌توان استخراج کرد.
مطالب
تغییرات اعمال شده در C++11 قسمت دوم (auto )
variable
متغیر :
برنامه هایی که نوشته می‌شوند برای پردازش داده‌ها بکار می‌روند،یعنی اطلاعاتی را از یک ورودی میگیرند و آنها را پردازش میکنند و نتایج مورد نظر را به خروجی می‌فرستند . برای پردازش ، لازم است که داده‌ها و نتایج ابتدا در حافظه اصلی ذخیره شوند،برای این کار از متغیر استفاده میکنیم .
متغیر مکانی از حافظه ست که شامل : نام ، نوع ، مقدار و آدرس می‌باشد . وقتی متغیری را تعریف میکنیم ابتدا با توجه به نوع متغیر ، آدرسی از حافظه در نظر گرفته می‌شود،سپس به آن آدرس یک نام تعلق میگیرد. نوع متغیر بیان میکند که در آن آدرس چه نوع داده ای می‌تواند ذخیره شود و چه اعمالی روی آن می‌توان انجام داد،مقدار نیز مشخص میکند که در آن محل از حافظه چه مقداری ذخیره شده است . در ++C قبل از استفاده از متغیر باید آن را اعلان نماییم . نحوه اعلان متغیر به شکل زیر می‌باشد :

type  name  initializer ;

عبارت type نوع متغیر را مشخص میکند . نوع متغیر به کامپایلر اطلاع میدهد که این متغیر چه مقادیری می‌تواند داشته باشد و چه اعمالی می‌توان روی آن انجام داد .عبارت name نام متغیر را نشان میدهد. عبارت initializer نیز برای مقداردهی اولیه استفاده می‌شود. نوع هایی که در ویژوال استادیو 2012 ساپورت می‌شوند شامل جدول زیر می‌باشند .

چند تعریف از متغیر به شکل زیر :

int sum(0);   //  یا  int sum=0;

char ch(65);  //  ch is A

float  pi(3.14);  //  یا  float  pi = 3.14;
همانطور که مشهود می‌باشد طبق تعریف متغیر ، نوع و نام و مقدار اولیه (اختیاری) ، مشخص گردیده است . تا قبل از C++11 تعریف نوع متغیر الزامی بود در غیر این صورت با خطای کامپایلر مواجه می‌شدیم .

تغییرات اعمال شده در C++11 :  معرفی کلمه کلیدی auto

در C++11 کلمه کلیدی auto معرفی و اضافه گردید ، با استفاده از auto ، کامپایلر این توانایی را دارد که نوع متغیر را از روی مقدار دهی اولیه آن تشخیص دهد و نیازی به مشخص نمودن نوع متغیر نداریم .

int x = 3;
auto y = x;
در تعریف فوق ابتدا نوع متغیر x را int  در نظر گرفتیم و مقدار 3 را به آن نسبت دادیم . در تعریف دوم نوع متغیر را مشخص نکردیم و کامپایلر با توجه به مقدار اولیه ای که به متغیر y  نسبت دادیم ، نوع آن را مشخص میکند . چون مقدار اولیه آن x  می‌باشد و x  از نوع int  می‌باشد پس نوع متغیر y  نیز از نوع int در نظر گرفته می‌شود .
دلایلی برای استفاده از auto :
Robustness : (خوشفکری)  به طور فرض زمانی که مقدار برگشتی یک تابع را در یک متغیر ذخیره میکنید با تغییر نوع برگشتی تابع نیازی به تغییر کد (برای نوع متغیر ذخیره کننده مقدار برگشتی تابع) ندارید .
 
int  sample()
  {
      int  result(0);
      // To Do ...
      return  result;
  }

int main()
 {
      auto  result =  sample();
      // To Do ...
      return 0;
 }
و زمانی که نوع برگشتی تابع بنا به نیاز تغییر کرد
float  sample()
  {
      float  result(0.0);
      // To Do ...
      return  result;
  }

int main()
 {
      auto  result =  sample();
      // To Do ...
      return 0;
 }
همانطور که مشاهده میکنید با اینکه کد تابع و نوع برگشتی آن تغییر یافت ولی بدنه main تابع هیچ تغییری داده نشد .
 
Usability : (قابلیت استفاده)  نیازی نیست نگران نوشتن درست و تایپ صحیح نام نوع برای متغیر باشیم
flot   f(0.0) ;   //  خطای نام نوع گرفته می‌شود
auto  f(0.0);   //  نیازی به وارد نمودن نوع تایپ نیستیم
Efficiency : برنامه نویسی ما کارآمدتر خواهد بود
مهمترین استفاده از auto سادگی آن است .
استفاده از auto  بخصوص زمانی که از STL  و templates استفاده میکنیم ، بسیار کارآمد می‌باشد و بسیاری از کد را کم میکند و باعث خوانایی بهتر کد می‌شود .

  فرض کنید که نیاز به یک iterator  جهت نمایش تمام اطلاعات کانتینری از نوع mapداریم باید از کد زیر استفاده نماییم (کانتینر را map  در نظر گرفتیم) 
map<string, string> address_book;
address_book[ "Alex" ] = "example@yahoo.com";
برای تعریف یک iterator به شکل زیر عمل میکنیم .
map<string, string>::iterator itr = address_book.begin();
با استفاده از auto  کد فوق را میتوان به شکل زیر نوشت
auto itr = address_book.begin();
  (کانتینرها :(containers) : کانتینرها اشیایی هستند که اشیا دیگر را نگهداری میکنند و دارای انواع مختلفی می‌باشند به عنوان مثال , ... vector, map )
  (تکرار کننده‌ها : (iterators): تکرار کننده‌ها اشیایی هستند که اغلب آنها اشاره گرند و با استفاده از آنها میتوان محتویات کانتینرها را همانند آرایه پیمایش کرد)
اشتراک‌ها
جدول بوت‌استرپی

ویژگی‌ها

1- داکیومنت مناسب + ایجکسی بودن

2- قابلیت نگهداری صفحه(اگر در صفحه‌ی 6 مرورگر را ببندید. در بازدید بعدی باز هم همان صفحه‌ی 6 نمایش داده می‌شود).

3- خروجی اکسل و ورد و...(pdf برای زبان فارسی مناسب نیست.)(با جاوااسکریپت کار میکنه)

4- نمایش متفاوت در گوشی‌ها( هر ستون به یک سطر تبدیل میشه)

5- فیلتر کردن و جستجوی کلی(بدون زحمت)(نگهداری فیلتر و جستجو در بازدید بعدی)

6- افزودن دکمه در هر سلول با توجه به نیاز

7- کم زیاد کردن ستون‌ها توسط کاربر و نگهداری تغییرات برای بازدید بعدی

8- فریز کردن عنوان جدول در زمان ارتفای زیاد

9- نمایش جدول در داخل جدول

10-تغییر اندازه ستون ها- مرتب سازی هر ستون- امکان استفاده از چند فیلتر با هم

آدرس مثال‌های این  پلاگین 

جدول بوت‌استرپی
مطالب دوره‌ها
حذف یک ردیف از اطلاعات به همراه پویانمایی محو شدن اطلاعات آن توسط jQuery در ASP.NET MVC
فرض کنید تعدادی ردیف در گزارشی نمایش داده شده‌اند. قصد داریم برای هر ردیف یک دکمه حذف را قرار دهیم. این حذف باید Ajax ایی باشد؛ به علاوه در حین حذف ردیف، پویانمایی محو آن ردیف را نیز سبب شود.


مدل و منبع داده برنامه

namespace jQueryMvcSample06.Models
{
    public class BlogPost
    {
        public int Id { set; get; }
        public string Title { set; get; }
        public string Body { set; get; }
    }
}

using System.Collections.Generic;
using jQueryMvcSample06.Models;

namespace jQueryMvcSample06.DataSource
{
    /// <summary>
    /// منبع داده فرضی جهت سهولت دموی برنامه
    /// </summary>
    public static class BlogPostDataSource
    {
        private static IList<BlogPost> _cachedItems;
        static BlogPostDataSource()
        {
            _cachedItems = createBlogPostsInMemoryDataSource();
        }

        /// <summary>
        /// هدف صرفا تهیه یک منبع داده آزمایشی ساده تشکیل شده در حافظه است
        /// </summary>        
        private static IList<BlogPost> createBlogPostsInMemoryDataSource()
        {
            var results = new List<BlogPost>();
            for (int i = 1; i < 30; i++)
            {
                results.Add(new BlogPost { Id = i, Title = "عنوان " + i, Body = "متن ... متن ... متن " + i});
            }
            return results;
        }

        public static IList<BlogPost> LatestBlogPosts
        {
            get { return _cachedItems; }
        }
    }
}
در اینجا مدل برنامه که ساختار نمایش یک سری مطلب را تهیه می‌کند، ملاحظه می‌کنید؛ به علاوه یک منبع داده فرضی تشکیل شده در حافظه جهت سهولت دموی برنامه.


کنترلر برنامه

using System.Web.Mvc;
using System.Web.UI;
using jQueryMvcSample06.DataSource;
using jQueryMvcSample06.Security;

namespace jQueryMvcSample06.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            var postsList = BlogPostDataSource.LatestBlogPosts;
            return View(postsList);
        }

        [AjaxOnly]
        [HttpPost]
        [OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
        public ActionResult DeleteRow(int? postId)
        {
            if (postId == null)
                return Content(null);

            //todo: delete post from db

            return Content("ok");
        }
    }
}
کنترلر برنامه بسیار ساده بوده و نکته خاصی ندارد. در حین اولین بار نمایش صفحه، لیست مطالب را به View مرتبط ارسال می‌کند. همچنین یک اکشن متد حذف ردیف‌های نمایش داده شده را نیز در اینجا تدارک دیده‌ایم. این اکشن متد از طریق ارسال اطلاعات به صورت Ajax، شماره مطلب را در اختیار برنامه قرار می‌دهد که توسط آن در ادامه برای مثال می‌توان این رکورد را از بانک اطلاعاتی حذف کرد. امضای متد DeleteRow بر اساس پارامترهای ارسالی توسط jQuery Ajax مشخص و تنظیم شده‌اند:
 data: JSON.stringify({ postId: postId }),

View برنامه

@model IEnumerable<jQueryMvcSample06.Models.BlogPost>
@{
    ViewBag.Title = "Index";
    var postUrl = Url.Action(actionName: "DeleteRow", controllerName: "Home");
}
<h2>
    حذف یک ردیف از اطلاعات به همراه پویانمایی محو شدن اطلاعات آن</h2>
<table>
    <tr>
        <th>
            عملیات
        </th>
        <th>
            عنوان
        </th>
    </tr>
    @foreach (var item in Model)
    {
        <tr>
            <td>
                <span id="row-@item.Id">حذف</span>
            </td>
            <td>
                @item.Title
            </td>
        </tr>
    }
</table>
@section JavaScript
{
    <script type="text/javascript">
        $(function () {
            $('span[id^="row"]').click(function () {
                var span = $(this);
                var postId = span.attr('id').replace('row-', '');
                var tableRow = span.parent().parent();
                $.ajax({
                    type: "POST",
                    url: '@postUrl',
                    data: JSON.stringify({ postId: postId }),
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    complete: function (xhr, status) {
                        var data = xhr.responseText;
                        if (xhr.status == 403) {
                            window.location = "/login";
                        }
                        else if (status === 'error' || !data || data == "nok") {
                            alert('خطایی رخ داده است');
                        }
                        else {
                            $(tableRow).fadeTo(600, 0, function () {
                                $(tableRow).remove();
                            });
                        }
                    }
                });
            });
        });
    </script>
}
کدهای View برنامه را در ادامه ملاحظه می‌کنید. اطلاعات مطالب دریافتی به صورت یک جدول در صفحه نمایش داده شده‌اند. در هر ردیف توسط یک span که با css تزئین گردیده است، یک دکمه حذف را تدارک دیده‌ایم. برای اینکه در حین کار با jQuery بتوانیم id هر ردیف را بدست بیاوریم، این id را در قسمتی از id این span اضافه شده قرار داده‌ایم.
در کدهای اسکریپتی صفحه، ابتدا کلیک بر روی کلیه spanهایی که id آن‌ها با row شروع می‌شود را مونیتور خواهیم کرد:
 $('span[id^="row"]').click(function () {
سپس هر زمان که بر روی یکی از این spanها کلیک شد، می‌توان بر اساس span جاری، id و همچنین tableRow مرتبط را استخراج کرد:
 var span = $(this);
var postId = span.attr('id').replace('row-', '');
var tableRow = span.parent().parent();
اکنون که به این اطلاعات دسترسی پیدا کرده‌ایم، تنها کافی است آن‌ها را توسط متد ajax به کنترلر برنامه برای پردازش نهایی ارسال نمائیم. همچنین در پایان کار عملیات، توسط متدهای fadeTo و remove ایی که ملاحظه می‌کنید، سبب حذف نمایشی یک ردیف به همراه پویانمایی محو آن خواهیم شد.


دریافت کدها و پروژه کامل این قسمت
jQueryMvcSample06.zip 
اشتراک‌ها
Visual Studio 2022 version 17.8.2 منتشر شد
حجم تقریبا آپدیت از نسخه قبلی (17.8.1) حدود 666G میشه.

Summary of What's New in this Release of Visual Studio 2022 version 17.8.2

  • Fixed an issue where, in certain situations, a document window can get stuck showing a loading message.
  • In some cases (when a project is located under a solution folder) you may see an error when saving the project. The project would get saved but you would see an error about unable to cast a COM object. This issue is now fixed so the error is no longer displayed.

Developer Community

Visual Studio 2022 version 17.8.2 منتشر شد
اشتراک‌ها
رهانش ASP.NET Core updates in .NET 8 Preview 4

.NET 8 Preview 4 is now available and includes many great new improvements to ASP.NET Core.


Here’s a summary of what’s new in this preview release:


Blazor

Streaming rendering with Blazor components

Handling form posts with Blazor SSR

Route to named elements in Blazor

Webcil packaging for Blazor WebAssembly apps

API authoring

Expanded support for form binding in minimal APIs

API project template includes .http file

Native AOT

Logging and exception handling in compile-time generated minimal APIs

ASP.NET Core top-level APIs annotated for trim warnings

Reduced app size with configurable HTTPS support

Worker Service template updates

Additional default services configured in the slim builder

API template JSON configuration changes

Support for JSON serialization of compiler-generated IAsyncEnumerable unspeakable types

Authentication and authorization

Identity API endpoints

Improved support for custom authorization policies with IAuthorizationRequirementData

ASP.NET Core metrics

For more details on the ASP.NET Core work planned for .NET 8 see the full ASP.NET Core roadmap for .NET 8 on GitHub. 

رهانش ASP.NET Core updates in .NET 8 Preview 4