Tools -> Code Snippets Manager (Ctrl+K,Ctrl+B)
در ویژوال استودیو 2010 دو نوع snippet وجود دارد :
1- Expansion snippets : که در محل کرسر (Cursor) اضافه میشوند . مثل cw و enum که به ترتیب دستور writeLine و ساختار یک enum را ایجاد میکنند .
2- SurroundsWith snippets : که میتوانند یک تکه کد انتخاب شده را در بر بگیرند مثل for و یا do که کد انتخاب شده را در یک حلقه for و do-while محصور میکنند .
نکته ای که باید توجه داشت این است که یک snippet میتواند از هر دو نوع باشد . برای مثال for و do و یا if ، در صورتی که کدی انتخاب شده باشد آن را محصور میکنند و گرنه ساختار خالی مرتبط را در محل cursor اضافه میکنند .
همانطور که در ابتدا هم ذکر شد ، علاوه بر snippetهای آمادهی موجود ، توسعه دهنده میتواند قطعه کدهایی را خود ایجاد کرده و مورد استفاده قرار دهد .
در اینجا یک expansion snippet خواهیم ساخت تا کار اضافه کردن بلاک try-catch-finally را برای ما انجام دهد .
- ابتدا یک فایل xml به پروژه اضافه میکنیم و آنرا TryCatchFinally.snippet مینامیم . توجه کنید که نام فایل باید به .snippet ختم شود .
- فایل را باز و درون آن راست کلیک کرده و گزینه Insert snippet > Snippet را انتخاب میکنیم . با اینکار یک قالب پایه snippet ( که یک ساختار xml ) است به فایل اضافه میشود . هر فایل snippet از دو بخش اصلی header و snippet تشکیل شده که بخش header اطلاعاتی کلی درباره قطعه کد را نگهداری میکند و بخش snippet مربوط به تعریف محتوای قطعه کد است .
<codesnippet format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <header> <title>title</title> <author>author</author> <shortcut>shortcut</shortcut> <description>description</description> <snippettypes> <snippettype>SurroundsWith</snippettype> <snippettype>Expansion</snippettype> </snippettypes> </header> <snippet> <declarations> <literal> <id>name</id> <default>value</default> </literal> </declarations> <code language="XML"> <!--[CDATA[<test--> <name>$name$</name> $selected$ $end$]]> </code> </snippet> </codesnippet>
- قالب پیش فرض شامل هر دو نوع snippet است .
<codesnippet format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <header> ... <snippettypes> <snippettype>SurroundsWith</snippettype> <snippettype>Expansion</snippettype> </snippettypes> </header> ... </codesnippet>
از آنجا که قصد داریم یک Expansion snippet بسازیم پس تگ SurroundsWith را حذف میکنیم .
<snippettypes> <snippettype>Expansion</snippettype> </snippettypes>
- در بخش header مقدار تگ Title را به “Try Catch Finally”و مقدار تگ Shortcut را به “trycf” و Description را به “Adds a try-catch-finally block ” تغییر میدهیم . Title عنوان snippet است و وجود آن ضروری است . اضافه کردن shortcut اختیاری است و به عنوان یک متن میانبر برای اضافه کردن snippet استفاده میشود .
<Header> <Title>Try Catch Finally</Title> <Author>mohsen.d</Author> <Shortcut>trycf</Shortcut> <Description>Adds a try-catch-finally block</Description>
- تگ Literal برای تعریف جایگزین برای بخشی از کد درون snippet که احتمال دارد پس از اضافه شدن ، توسط برنامه نویس و یا در صورت استفاده از function توسط خود ویژوال استودیو تغییر کند استفاده میشود . در قطعه کد try-catch-finally ، ما میخواهیم به کاربر اجازه بدهیم که نوع استثنائی را که catch میشود تغییر دهد .
تگ id نامی برای بخش قابل ویرایش تعریف میکند ( که از آن در ادامه در تعریف خود قطعه کد استفاده میکنیم ) . آنرا به “ExceptionName” تغییر میدهیم . تگ default هم مقدار پیش فرضی را برای آن بخش مشخص میکند . ما میخواهیم تمام استثناها را Catch کنیم پس مقدار پیش فرض را برابر "Exception" قرار میدهیم .
..... <declarations> <literal> <id>ExceptionName</id> <default>Exception</default> </literal> </declarations> ...
- و در تگ Code ، خود قطعه کدی که ویژوال استودیو باید آن را اضافه کند ، تعریف میشود . مقدار مشخصه Language آن را به CSharp تغییر میدهیم و محتویات داخل آنرا به شکل زیر اضافه میکنیم .
<code language="CSharp"> <!--[CDATA[ try { $end$ } catch($ExceptionName$) { } finally { } ]]--> </code>
به نحوه استفاده از ExceptionName که در قسمت Literal تعریف کردیم توجه کنید . عبارت $end$ هم یک کلمه رزرو شده است که محل قرار گرفتن cursor را بعد از اضافه شدن قطعه کد مشخص میکند .
- در نهایت snippet ما به شکل زیر خواهد بود :
<codesnippet format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <header> <title>Try Catch Finally</title> <author>mohsen.d</author> <shortcut>trycf</shortcut> <description>Adds a try-catch-finally block</description> <snippettypes> <snippettype>Expansion</snippettype> </snippettypes> </header> <snippet> <declarations> <literal> <id>ExceptionName</id> <default>Exception</default> </literal> </declarations> <code language="CSharp"> <!--[CDATA[ try { $end$ } catch($ExceptionName$) { } finally { } ]]--> </code> </snippet> </codesnippet>
اضافه کردن snippet ساخته شده به visual studio
دو راه برای اضافه کردن snippet تعریف شده به ویژوال استودیو وجود دارد :
روش اول قرار دادن فایل .snippet در پوشه code snippets ویژوال استودیو است که مسیر پیش فرض آن
C:\Users\<UserName>\Documents\Visual Studio 2010\Code Snippets\
گزینه دوم import کردن فایل .snippet به داخل ویژوال استودیو است . در ویژوال استودیو به مسیر
Tools -> Code Snippets Manager (Ctrl+K,Ctrl+B)
استفاده از snippet ساخته شده
برای استفاده از snippet میتوانیم متن میانبر تعریف شده را تایپ کنیم و با دو بار فشردن کلید tab قطعه کد تعریف شده به محل کرسر اضافه میشودهمینطور با فشردن کلیدهای Ctrl+K و Ctrl+X به صورت پشت سر هم منوی “Insert Snippet” ظاهر میشود که از طریق آن میتوانیم Snippet موردنظر را یافته ( بدنبال Title تعریف شده برای snippet در پوشه ای که آنرا ذخیره کرده اید بگردید ) و با انتخاب آن کد تعریف شده اضافه خواهد شد .
برای آشنایی با روشهای مختلف دسترسی به snippetها اینجا را بررسی کنید .
ابزارها
در این پروژه هم مجموعه snippetهای موجود ویژوال استودیو 2010 برای زبان سی شارپ ، جهت سازگاری با stylecop ویرایش و refactor شده اند ( در کنار تعریف snippetهای دیگر ).
روش نگاشت محتوای یک سایت استاتیک در یک Container که وب سرور است
فرض کنید یک سایت استاتیک بوت استرپی را تهیه کردهاید و قصد دارید آنرا توسط وب سرور nginx، هاست کنید. برای اینکار، چندین گزینه پیش روی ما هستند:
گزینهی اول: دریافت image مربوط به nginx، سپس ایجاد یک container از آن و در آخر با استفاده از «روش به اشتراک گذاری فایل سیستم میزبان با کانتینرها» که در قسمت قبل بررسی کردیم، این وب سایت را آمادهی اجرا و دسترسی میکنیم.
گزینهی دوم: کپی کردن فایلهای وب سایت از سیستم میزبان، به درون فایل سیستم خود container.
گزینهی سوم: ایجاد یک image سفارشی که از ابتدا به همراه فایلهای وب سایت استاتیک ما است و در این حالت تنها کافی است این image را تبدیل به container اجرایی کنیم.
روش اول: به اشتراک گذاری فایل سیستم میزبان با کانتینر وب سرور جهت هاست آن
در قسمت قبل، یک فایل tar ایجاد شدهی در سیستم میزبان ویندوزی را با یک کانتینر لینوکسی به اشتراک گذاشتیم تا بتوانیم محتویات آنرا استخراج کنیم. در اینجا قصد داریم پوشهی وب سایت استاتیک خود را که در سیستم میزبان ویندوزی قرار دارد، با وب سرور nginx که توسط یک container در حال اجرا است، به اشتراک بگذاریم تا آنرا هاست کند.
فرض کنید وب سایت استاتیک ما در مسیر c:\users\vahid\mysite سیستم میزبان قرار دارد که داخل آن یک فایل index.html و تعدادی فایل css و js آمادهی برای هاست شدن، وجود دارند. برای هاست آن توسط nginx، از دستور زیر استفاده خواهیم کرد:
docker run --rm -it -p 8080:80 -v c:\users\vahid\mysite:/usr/share/nginx/html nginx
- سوئیچ rm سبب میشود تا پس از خاتمهی کار nginx، این container نیز حذف شود.
- از سوئیچ it استفاده شدهاست تا با فشردن ctrl+c، بتوانیم پروسهی container را خاتمه دهیم و پس از آن، برنامهی nginx دیگر در background در حال اجرا نباشد (اجرای آن در foreground).
- سپس پورت 8080 سیستم میزبان، به پورت 80 وب سرور nginx نگاشت شدهاست. چون containerها دارای network stack خاص خودشان هستند (که آنرا در قسمت سوم بررسی کردیم)، پورت 80 آنها با پورت 80 سیستم میزبان تداخل نمیکند و اگر برای مثال بر روی پورت 80 سیستم جاری، IIS در حال اجرا باشد، سبب عدم اجرا شدن وب سرور nginx به دلیل تداخل پورتها نمیشود.
- در ادامه روش volume mount را مشاهده میکنید که در قسمت قبل بررسی کردیم. مسیر c:\users\vahid\mysite سیستم میزبان، به مسیر ویژهی /usr/share/nginx/html داخل container نگاشت شدهاست. این مسیر، یک مسیر استاندارد بوده و در مستندات docker hub این وب سرور، ذکر شدهاست.
- در آخر هم نام image این وب سرور را ذکر کردهایم.
پس از اجرای این دستور، اگر nginx پیشتر دریافت نشده باشد، image آن دریافت شده، یک container بر اساس آن ساخته میشود و سپس با پارامترهایی که توضیح دادیم، اجرا خواهد شد. اکنون اگر در سیستم میزبان، مسیر http://localhost:8080 را در مرورگر باز کنید، وب سایت استاتیک خود را مشاهده خواهید کرد.
روش دوم: کپی کردن فایلهای وب سایت از سیستم میزبان، به درون فایل سیستم خود container
همانطور که در قسمت سوم نیز بررسی کردیم، فایل سیستم مربوط به هاست، به طور کامل از فایل سیستم container، جدا و ایزوله است و بدون volume mount، یک container نمیتواند به فایلهای میزبان خود دسترسی پیدا کند. بنابراین گزینهی دیگری که در اینجا وجود خواهد داشت، کپی کردن فایلهای میزبان و انتقال آنها به container میباشد؛ شبیه به کپی کردن فایلها از یک کامپیوتر موجود در شبکه به کامپیوتر دیگری در آن.
برای این منظور ابتدا nginx را در پسزمینه اجرا میکنیم:
docker run -d -p 8080:80 --name nginx nginx
پس از اجرای این دستور و بازگشت به command prompt، جهت اطمینان حاصل کردن از اجرای آن در پس زمینه، دستور docker ps را صادر میکنیم که لیست آن، حاوی گزارشی از containerهای در حال اجرا است.
اکنون توسط دستور ویژهی docker exec، میخواهیم درون یک container در حال اجرا، پروسهای را اجرا کنیم. یعنی با اینکه پروسهی nginx داخل این container در حال اجرا است، برای مثال میخواهیم یک shell را نیز داخل آن اجرا کنیم:
docker exec -it nginx bash
cd /usr/share/nginx/html
ls mv index.html index2.html exit
docker cp c:\users\vahid\mysite nginx:/usr/share/nginx/html
docker exec nginx ls /usr/share/nginx/html
روش سوم: ایجاد یک image سفارشی که از ابتدا به همراه فایلهای وب سایت استاتیک ما است
در روش دوم، موفق شدیم که فایلهای مدنظر خود را به درون container در حال اجرا کپی کنیم. اکنون میخواهیم یک snapshot را از آن تهیه کنیم؛ شبیه به کاری که با ماشینهای مجازی نیز انجام میشود و این روشی است که از آن برای ساخت یک image سفارشی استفاده میشود. برای این منظور از دستور docker commit استفاده میشود تا تصویری را از وضعیت یک container در حال اجرا، در آن لحظه تهیه کنیم:
docker commit nginx mysite:nginx
اکنون پس از اجرای این دستور، با استفاده از فرمان docker images میتوان مشاهده کرد که image جدید mysite، با tag ای معادل nginx، ایجاد شدهاست.
در ادامه برای اجرای این image جدید، میتوان از دستور زیر استفاده کرد:
docker run -d -p 8090:80 --name mysite mysite:nginx
اکنون اگر در سیستم میزبان، مسیر http://localhost:8090 را در مرورگر باز کنید، وب سایت استاتیک خود را مشاهده خواهید کرد و یا توسط دستور زیر میتوانید فایلهای موجود در پوشهی html وب سرور nginx این container جدید در حال اجرا را ملاحظه نمائید:
docker exec mysite ls /usr/share/nginx/html
نگاهی به لایههای یک Image در مقایسه با یک Container
زمانیکه خواستیم image جدید و سفارشی خاص خود را ایجاد کنیم، با image اصلی nginx شروع کردیم. اولین لایهی موجود در این image، سیستم عاملی است که میتواند آنرا اجرا کند. برفراز این لایه، لایهی خود nginx قرار گرفتهاست. اگر خواستید تاریخچهی ایجاد یک image را مشاهده کنید، از دستور docker history nginx استفاده نمائید. خروجی آن لیست دستوراتی را نمایش میدهد که برای ساخت این image مورد استفاده قرار گرفتهاند. البته دستور docker history nginx --no-trunc، اطلاعات بیشتری را با نمایش لیست کامل و خلاصه نشدهی دستورات، ارائه میدهد. این دستورات را در صفحهی docker hub هر image نیز میتوان مشاهده کرد. در قسمت full description هر image، در ابتدای توضیحات، قسمتی است به نام supported tags and respective dockerfile links. در اینجا هر tag نامبرده شده، در حقیقت لینکی است به یک فایل که دقیقا همین دستورات را لیست کردهاست. به این فایل، docker file گفته میشود که روش ساخت یک image را توضیح میدهد. هدف آن، خودکار سازی اجرای دستوراتی است که سبب ساخت یک image میشوند.
در ادامه اگر از این image، یک container را ایجاد کنیم، این container هر دو لایهی OS و Framework را به همراه خواهد داشت؛ به علاوهی لایهی دیگری به نام Container/Run که میتوان فایلهای آنرا خواند و یا در آن نوشت. بنابراین لایهای که فایلهای وب سایت استاتیک ما در آن کپی شدند، دقیقا همین لایهاست.
و زمانیکه از یک container تصویری تهیه میشود، تغییراتی را که به فایل سیستم آن ایجاد کردهایم، به صورت یک لایهی جدید بر روی لایههای قبلی آن image، ظاهر و ثبت میشود. برای اثبات این موضوع، میتوان از دستور docker diff nginx استفاده کرد. در اینجا nginx نام container ای است که میخواهیم تغییرات آنرا با image قبلی که بر پایهی آن ایجاد شدهاست، مشاهده کنیم.
تبدیل دستورات docker به یک docker file
تا اینجا یک چنین دستوراتی را برای اجرای کانتینر nginx، کپی فایلها به آن و سپس تهیهی یک تصویر از آن، اجرا کردیم:
docker run -d -p 8080:80 --name nginx nginx docker cp c:\users\vahid\mysite nginx:/usr/share/nginx/html docker commit nginx mysite:nginx
بجای سطر اول، تنها نام image ای را که میخواهیم کار را بر مبنای آن انجام دهیم، ذکر میکنیم:
FROM nginx
COPY mysite /usr/share/nginx/html
سپس برای اجرای این فایل، بجای دستور docker commit آخر، از دستور زیر استفاده میکنیم:
docker build -f Dockerfile -t mysite:nginx-df .
docker build -t mysite:nginx-df .
تگ این image را نیز متفاوت با قبلیها انتخاب کردهایم؛ nginx-df بجای مقدار قبلی.
در این حالت اگر دستور آخر را اجرا کنیم، دستور docker images گزارش اضافه شدن این image جدید را ارائه خواهد داد.
مرجع کامل ساخت Dockerfileها را در اینجا میتوانید مطالعه کنید.
ساخت یک image سفارشی برای هاست یک وب سایت استاتیک در IIS
تا اینجا از وب سرور لینوکسی nginx برای هاست وب سایت استاتیک خود استفاده کردیم. در ادامه میخواهیم از وب سرور IIS برای اینکار استفاده نمائیم. بنابراین ابتدا نیاز است یا از ویندوز سرور استفاده کنیم و یا میتوان با کلیک راست بر روی آیکن Docker در قسمت Tray Icons ویندوز، به Windows Containers سوئیچ کرد و سپس به صورت زیر عمل نمود.
اینبار محتوای Dockerfile ای که کنار پوشهی mysite قرار میگیرد، به صورت زیر خواهد بود:
FROM microsoft/iis:nanoserver COPY mysite c:/inetpub/wwwroot
در ادامه با استفاده از دستور زیر و اجرای فایل Dockerfile، این image جدید را با tag ای به نام iis ایجاد میکنیم:
docker build -t mysite:iis .
به اشتراک گذاری imageهای سفارشی در Docker Hub
برای به اشتراک گذاری imageهای سفارشی خود در Docker Hub، نیاز است tag آنها را توسط دستور docker tag مطابق فرمت ویژهی docker hub ویرایش کرد:
docker tag mysite:nginx-df my_user_name/some_name:new_tag_name
و در آخر برای انتشار آن میتوان از دستور docker push استفاده کرد:
docker push my_user_name/some_name:new_tag_name
پس از پایان کار اگر به سایت docker hub و مخازن خود مراجعه کنید، این image جدید قابل مشاهده خواهد بود.
روش توسعه waterfall در برابر agile
معرفی کتابخانه PdfReport
BloggerToCHM
میدونید ممکنه بلاگ هایی رو بخواهید chm کنید ولی همه پست ها رو نخواهید که دانلود بشه پس اینجا یک گزینه برای دانلود پستهایی که دارای تگ خاصی که معمولا در بلاگها تکراری است رو بشه به هنگام دانلود پست ها اعمال کرد.
- استفاده از WebHost.CreateDefaultBuilder: این روش جهت تنظیم شروع به کار یک برنامهی ASP.NET Core 2x مورد استفاده قرار میگرفت.
- استفاده از Host.CreateDefaultBuilder: روش پیشفرض آغاز برنامههای وب NET Core 3x. و NET 5x. که با معرفی generic host، امکان تهیهی Worker services را میسر کردند.
- استفاده از WebApplication.CreateBuilder: روش جدید شروع به کار با برنامههای وب مبتنی بر NET 6.
استفاده از WebHost.CreateDefaultBuilder در ASP.NET Core 2.x
در نگارش اول ASP.NET Core، مفهومی به نام هاست پیشفرض (default host) وجود نداشت. یکی از مهمترین نکات درنظر گرفته شده در طراحی ASP.NET Core، مفهوم «هزینه کردن به ازای احتیاج» است. یعنی اگر نیاز به قابلیتی نیست، نباید وجود داشته باشد. به این ترتیب یک قالب آغازین نباید به همراه تعداد زیادی بستههای NuGet و مقدار زیادی کد برای تنظیم باشد؛ زمانیکه واقعا قرار نیست از تمام آنها استفاده شود. به همین جهت از نگارش 2، شروع به سادهکردن این قالب اولیه کردند و در این زمان، WebHost.CreateDefaultBuilder ارائه شد. این تنظیم به ظاهر ساده، کدهای پیشفرض مورد نیاز قابل توجهی را جهت ساخت سادهی IWebHostBuilder و IWebHost به همراه دارد. در قالب به همراه آن، بین مفاهیم تنظیمات application و host تفاوت قائل شدهاند؛ یعنی دو فایل Program.cs را برای تنظیمات application و فایل Startup.cs را برای ارائهی تنظیمات هاست، تدارک دیدند.
در این طراحی، بین میدان دید Program و Startup تفاوت وجود دارد. هدف از Program، ارائهی تنظیمات زیرساختی برنامه، مانند تنظیمات HTTP Server، یکپارچگی با IIS و امثال آن شد و عموما در طول عمر برنامه ثابت است. اما برای تغییر رفتار برنامه، بیشتر تغییرات و تنظیمات، به Startup محول شدند؛ مانند تنظیمات تزریق وابستگیها، تنظیمات میانافزارها، مسیریابیها و غیره.
public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .Build(); }
معرفی Generic Host در ASP.NET Core 3.x/5
ASP.NET Core 3.x به همراه تغییرات بزرگی در کدهای آغازین برنامههای ASP.NET Core بود. تا پیش از آن، امکانات پایهی ASP.NET Core تنها برای مقاصد وب قابل استفاده بود. اما در نگارشهای 3x، با ارائهی یک هاست عمومی، امکان پشتیبانی از سایر برنامهها، مانند worker services را نیز میسر کرد؛ برای مثال پیشتیبانی از کارهای طولانی پس زمینه، هاست سرویسهای gRPC، هاست سرویسهای ویندوز و غیره. هدف اصلی از این تغییرات، به اشتراک گذاری فریمورک پایهی ASP.NET Core که تنها برای برنامههای وب ساخته شده بود و به همراه امکاناتی مانند تنظیمات، ثبت وقایع، تزریق وابستگیها و غیره بود، با سایر انواع برنامههای یاد شده است. برای رسیدن به این هدف، Web Host نگارش 2x، به Generic Host نگارش 3x تغییر کرد و سپس ASP.NET Core برفراز آن بنا شد. اینبار بجای IWebHostBuilder، نمونهی جدید IHostBuilder را داریم:
public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }; } }
ASP.NET Core 5 به همراه تغییرات مهمی در این زمینه نبود و تنها با تغییر target framework در فایل csproj و به روز رسانی بستههای نیوگت مرتبط، کار ارتقاء به نگارش جدید در آن صورت میگرفت.
معرفی WebApplicationBuilder در ASP.NET Core 6x
در دات نت 6، روش آغاز برنامههای وب بطور کامل تغییر کردهاست. در تمام نگارشهای پیشین ASP.NET Core، همواره شاهد دو فایل Program.cs و Startup.cs بودیم؛ اما در اینجا فقط یک فایل Program.cs بیشتر وجود ندارد. هر چند همانطور که در مطلب «ارتقاء فایلهای آغازین برنامههای ASP.NET Core 5x به 6x» نیز عنوان شد، میتوان همان سبک و سیاق پیشین را نیز برگرداند و از این لحاظ محدودیتی وجود ندارد.
var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.MapGet("/", () => "Hello World!"); app.MapRazorPages(); app.Run();
- استفاده از top level statements
- استفاده از usingهای سراسری و سایر قابلیتهای C# 10.0
- حذف کامل کلاس Startup؛ اینبار همه چیز فقط یک فایل است.
دیدگاهی را که در اینجا بکار گرفتهاند شامل این موارد است و بیشتر تازهواردان را مدنظر دارند:
- برای شروع به کار، using statements اضافی هستند؛ پس حذف شدهاند!
- برای شروع به کار، namespaces اضافی هستند؛ پس حذف شدهاند!
- برای شروع به کار، Program.Main ... هم اضافی است!
- تنظیمات بین دو فایل Program.cs و Startup.cs تقیسم نشدهاند و همهچیز یکجا است و این هم نیازی به توضیح اضافی به تازهواردان، ندارد.
- همچنین همانطور که عنوان شد، « ... متد UseStartup به صورت خودکار به دنبال متدهای ویژهی ConfigureServices و Configure در کلاس آغازین برنامه گشته و آنها را فراخوانی میکند تا تنظیمات تزریق وابستگیها، مسیریابیها و میانافزارها صورت گیرند ...»
نکتهی مهم این توضیح، این است که کلاس Startup، هیچ اینترفیسی را پیاده سازی نمیکند. یعنی نام این متدها، دقیقا باید به همین صورت باشند (بدون اینکه قرار دادی توسط یک اینترفیس برای آنها وضع شده باشد) و ... چرا واقعا باید به این صورت باشد؟! به همین جهت اینها هم حذف شدهاند!
- در اینجا WebApplication هم مشاهده میشود؛ اما آیا واقعا نیازی به آن است؟
پاسخ: خیر! WebApplication.CreateBuilder ای که در اینجا ملاحظه میکنید در حقیقت ساده شدهی قطعه کد زیر از کدهای ASP.NET Core 3x/5x است:
var hostBuilder = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddRazorPages(); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.Configure((ctx, app) => { if (ctx.HostingEnvironment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", () => "Hello World!"); endpoints.MapRazorPages(); }); }); }); hostBuilder.Build().Run();
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(); builder.Services.AddSingleton<MyThingy>();
builder.Logging.AddFile();
var app = builder.Build();
app.UseStaticFiles(); app.MapRazorPages();
Cookie - قسمت سوم
Cookie - قسمت اول: مقدمه، تاریخچه، معرفی، و شرح کامل
Cookie - قسمت دوم: کوکی در جاوا اسکریپت
نکته مهم: خواندن قسمتهای قبلی این سری (مخصوصا قسمت اول) برای درک بهتر مطالب پیشنهاد میشود.
.
کوکی در ASP.NET - بخش اول
در قسمتهای قبلی مقدمات و مباحث کلی راجع به کوکیها و انواع آن، شرح کامل خواص، نحوه رفتار مرورگرها با انواع کوکیها و درنهایت نحوه کار کردن با کوکیها در سمت کلاینت با استفاده از زبان محبوب جاوا اسکریپت پرداخته شد.
در ادامه این سری مطالب به نحوه برخورد ASP.NET با کوکیها و چگونگی کار کردن با کوکی در سمت سرور آشنا خواهیم شد. در بخش اول این قسمت مباحث ابتدایی و اولیه برای کار با کوکیها در ASP.NET ارائه میشود. در بخش دوم مباحث پیشرفتهتر همچون SubCookieها در ASP.NET و نیز سایر نکات ریز کار با کوکیها در ASP.NET بحث خواهد شد.
.
.
Response و Request در ASP.NET
در قسمت اول این سری به مفاهیم Http Response و Http Request اشاره کوتاهی شده بود. بهصورت خلاصه، درخواستی که از سمت یک کلاینت به یک وب سرور ارسال میشود Request و پاسخی که وب سرور به آن درخواست میدهد Response نامیده میشود.
در ASP.NET، کلیه اطلاعات مرتبط با درخواست رسیده از سمت یک کلاینت در نمونهای منحصر به فرد از کلاس HttpRequest نگهداری میشود. محل اصلی نگهداری این نمونه در پراپرتی Request از نمونه جاری کلاس System.Web.HttpContext (قابل دسترسی ازطریق HttpContext.Current) است. البته کلاس Page هم یک پراپرتی با نام Request دارد که دقیقا از همین پراپرتی کلاس HttpContext استفاده میکند.
همچنین کلیه اطلاعات مرتبط با پاسخ ارسالی وب سرور به سمت کلاینت مربوطه در نمونهای از کلاس HttpResponse ذخیره میشود. محل اصلی نگهداری این نمونه نیز در پراپرتی Response از نمونه جاری کلاس HttpContext است. همانند Request، کلاس Page یک پراپرتی با نام Response برای نگهداری این نمونه دارد که این هم دقیقا از پراپرتی متناظر در کلاس HttpContext استفاده میکند.
.
.
کوکیها در Response و Request
هر دو کلاس HttpResponse و HttpRequest یک پراپرتی با عنوان Cookies (^ و ^) دارند که مخصوص نگهداری کوکیهای مربوطه هستند. این پراپرتی از نوع System.Web.HttpCookieCollection است که یک کالکشن مخصوص برای ذخیره کوکیهاست.
- این پراپرتی (Cookies) در کلاس HttpRequest محل نگهداری کوکیهای ارسالی توسط مرورگر در درخواست متناظر آن است. کوکیهایی که مرورگر با توجه به شرایط جاری و تنظیمات کوکیها اجازه ارسال به سمت سرور را به آنها داده و در درخواست ارسالی ضمیمه کرده است (با استفاده از هدر :Cookie که در قسمت اول شرح داده شد) و ASP.NET پس از پردازش و Parse دادهها، درون این پراپرتی اضافه کرده است.
- این پراپرتی (Cookies) در کلاس HttpResponse محل ذخیره کوکیهای ارسالی از وب سرور به سمت مرورگر کلاینت در پاسخ به درخواست متناظر است. کوکیهای درون این پراپرتی پس از بررسی و استخراج دادههای موردنیاز توسط ASP.NET در هدر پاسخ ارسالی ضمیمه خواهند شد (با استفاده از هدر :Set-Cookie که در قسمت اول توضیح داده شد).
.
.
ایجاد و بهروزرسانی کوکی در ASP.NET
برای ایجاد یک کوکی و ارسال آن به سمت کلاینت همانطور که در بالا نیز اشاره شد، باید از پراپرتی Response.Cookies از کلاس HttpContext استفاده کرد. برای ایجاد یک کوکی روشهای مختلفی وجود دارد.
- در روش اول با استفاده از ویژگی مخصوص ایندکسر کلاس HttpCookieCollection عملیات تولید کوکی انجام میشود. در این روش، ابتدا بررسی میشود که کوکی موردنظر در لیست کوکیهای جاری وجود دارد یا خیر. درصورتیکه با این نام قبلا یک کوکی ثبت شده باشد، مقدار کوکی موجود بروزرسانی خواهد شد. اما اگر این نام وجود نداشته باشد یک کوکی جدید با این نام به لیست افزوده شده و مقدار آن ثبت میشود. مثال:
HttpContext.Current.Response.Cookies["myCookie"].Value = "myCookieValue";
- روش بعدی استفاده از متد Add در کلاس HttpCookieCollection است. در این روش ابتدا یک نمونه از کلاس HttpCookie ایجاد شده و سپس این نمونه به لیست کوکیها اضافه میشود. کد زیر چگونگی استفاده از این روش را نشان میدهد:
var myCookie = new HttpCookie("myCookie", "myCookieValue"); HttpContext.Current.Response.Cookies.Add(myCookie);
- روش دیگر استفاده از متد Set کلاس HttpCookieCollection است. تفاوت این متد با متد Add در این است که متد Set ابتدا سعی میکند عملیات update انجام دهد. یعنی عملیات افزودن تنها وقتیکه نام کوکی موردنظر در لیست کوکیها یافته نشود انجام خواهد شد. برای مثال:
HttpContext.Current.Response.Cookies.Set(new HttpCookie("myCookie", "myCookieValue"));
نکته: باتوجه به توضیحات بالا، متد Set اجازه افزودن دو کوکی با یک نام را نمیدهد. برای اینکار باید از متد Add استفاده کرد. درباره این موضوع در قسمت بعدی بیشتر توضیح داده خواهد شد.
- روش دیگری که برای ایجاد یکی کوکی میتوان از آن استفاده کرد، بکارگیری متد AppnedCookie از کلاس HttpResponse است. در این روش نیز ابتدا باید یک نمونه از کلاس HttpCookie تولید شود. این روش همانند استفاده از متد Add از کلاس HttpCookieCollection است. کد زیر مثالی از این روش را نشان میدهد:
HttpContext.Current.Response.AppendCookie(new HttpCookie("myCookie", "myCookieValue"));
- روش بعدی استفاده از متد SetCookie از کلاس HttpResponse است. فرق این متد با متد AppendCookie در این است که در متد SetCookie ابتدا وجود یک کوکی با نام ارائه شده بررسی میشود و درصورت وجود، مقدار این کوکی بروزرسانی میشود. درصورتیکه قبلا یک کوکی با این نام وجود نداشته باشد، یک کوکی جدید به لیست کوکیها اضافه میشود. این روش همانند استفاده از متد Set از کلاس HttpCookieCollection است. نمونهای از نحوه استفاده از این متد در زیر آورده شده است:
HttpContext.Current.Response.SetCookie(new HttpCookie("myCookie", "myCookieValue"));
نکته: تمامی فرایندهای نشان داده شده در بالا تنها موجب تغییر محتویات کالکشن کوکیها درون HttpContext میشود و تا زمانیکه توسط وب سرور با استفاده از دستور Set-Cookie به سمت مرورگر ارسال نشوند تغییری در کلاینت بوجود نخواهند آورد.
برای آشنایی بیشتر با این روند کد زیر را برای تعریف یک کوکی جدید درنظر بگیرید:
HttpContext.Current.Response.Cookies["myCookie"].Value = "myValue";
همانطور که مشاهده میکنید دستور ایجاد یک کوکی با نام و مقدار وارده در هدر پاسخ تولیدی توسط وب سرور گنجانیده شده است.
نکته: در ASP.NET به صورت پیش فرض از مقدار "/" برای پراپرتی Path استفاده میشود.
.
خواص کوکی در ASP.NET
برای تعیین یا تغییر خواص یک کوکی در ASP.NET باید به نمونه HttpCookie مربوطه دست یافت. سپس با استفاده از پراپرتیهای این کلاس میتوان خواص موردنظر را تعیین کرد. برای مثال:
var myCookie = new HttpCookie(string.Empty); myCookie.Name = "myCookie"; myCookie.Value = "myCookieValue"; myCookie.Domain = "dotnettip.info"; myCookie.Path = "/post"; myCookie.Expires = new DateTime(2015, 1, 1); myCookie.Secure = true; myCookie.HttpOnly = true;
نکته مهم: امکان تغییر خواص یک کوکی به صورت مستقیم در سمت سرور وجود ندارد. درواقع برای اعمال این تغییرات در سمت کلاینت باید به ازای هر کوکی موردنظر یک کوکی جدید با مقادیر جدید ایجاد و به کالکشن کوکیها در Http Response مربوطه اضافه شود تا پس از قرار دادن دستور Set-Cookie متناظر در هدر پاسخ ارسالی به سمت کلاینت و اجرای آن توسط مرورگر، مقادیر خواص مورنظر در سمت کلاینت بروزرسانی شوند. دقت کنید که تمامی نکات مرتبط با هویت یک کوکی که در قسمت اول شرح داده شد در اینجا نیز کاملا صادق است.
روش دیگری نیز برای تعیین برخی خواص کوکیها به صورت کلی در فایل وب کانفیگ وجود دارد. برای اینکار از تگ httpCookies در قسمت system.web استفاده میشود. برای مثال:
<httpCookies domain="www.example.com" httpOnlyCookies="true" requireSSL="true" />
این امکان از ASP.NET 2.0 به بعد اضافه شده است. با استفاده از این تگ، تنظیمات اعمال شده برای تمامی کوکیها درنظر گرفته میشود. البته درصورتیکه تنظیم موردنظر برای کوکی به صورت صریح آورده نشده باشد. برای نمونه به کد زیر دقت کنید:
var myCookie = new HttpCookie("myCookie", "myCookieValue"); myCookie.Domain = "test.com"; HttpContext.Current.Response.Cookies.Add(myCookie); var myCookie2 = new HttpCookie("myCookie2", "myCookieValue2"); myCookie2.HttpOnly = false; myCookie2.Secure = false; HttpContext.Current.Response.Cookies.Add(myCookie2);
با استفاده از تنظیمات تگ httpCookies که در بالا نشان داده شده است، هدر پاسخ تولیدی توسط وب سرور به صورت زیر خواهد بود:
همانطور که میبینید تنها مقادیر پراپرتیهایی که صراحتا برای کوکی آورده نشده است از تنظیمات وب کانفیگ خوانده میشود.
.
.
حذف کوکی در ASP.NET
برای حذف یک کوکی در ASP.NET یک روش کلی وجود دارد که در قسمتهای قبلی نیز شرح داده شده است، یعنی تغییر خاصیت Expires کوکی به تاریخی در گذشته. برای نمونه داریم:
var myCookie = new HttpCookie("myCookie", "myCookieValue"); myCookie.Expires = DateTime.Now.AddYears(-1);
نکته مهم: در کلاس HttpCookieCollection یک متد با نام Remove وجود دارد. از این متد برای حذف یک کوکی از لیست موجود در این کلاس استفاده میشود. دقت کنید که حذف یک کوکی از لیست کوکیها با استفاده از این متد تاثیری بر موجودیت آن کوکی در سمت کلاینت نخواهد گذاشت و تنها روش موجود برای حذف یک کوکی در سمت کلاینت همان تنظیم مقدار خاصیت Expires است.
.
خواندن کوکی در ASP.NET
برای خواندن مقدار یک کوکی ارسالی از مرورگر کلاینت در ASP.NET، باتوجه به توضیحات ابتدای این مطلب، طبیعی است که باید از پراپرتی Request.Cookies در نمونه جاری از کلاس HttpContext استفاده کرد. برای این کار نیز چند روش وجود دارد.
- روش اول استفاده از ایندکسر کلاس HttpCookieCollection است. برای اینکار نیاز به نام یا ایندکس کوکی موردنظر در لیست مربوطه داریم. برای مثال:
var myCookie = HttpContext.Current.Request.Cookies["myCookie"];
- یا این نمونه با استفاده از ایندکسر عددی:
var myCookie = HttpContext.Current.Request.Cookies[0];
- روش دیگری که برای خواند مقدار یک کوکی میتوان بکار برد، استفاده از متد Get از کلاس HttpCookieCollection است. این متد همانند ایندکسر این کلاس نیاز به نام یا ایندکس کوکی موردنظر دارد. برای نمونه:
var myCookie = HttpContext.Current.Request.Cookies.Get("myCookie");
- یا استفاده از ایندکس کوکی:
var myCookie = HttpContext.Current.Request.Cookies.Get(0);
.
.
بحث و نتیجه گیری
تا اینجا با مفاهیم اولیه درباره نحوه برخورد ASP.NET با کوکیها آشنا شدیم. روشهای مختلف ایجاد و یا بهروزرسانی کوکیها نشان داده شد. با تعیین انواع خواص کوکیها آشنا شدیم. نحوه حذف یک کوکی در ASP.NET را دیدیم. روشهای خواندن مقادیر کوکیها را نیز مشاهده کردیم.
باز هم تاکید میکنم که تمامی تغییرات اعمالی در سمت سرور تا زمانیکه بهصورت دستورات Set-Cookie در هدر پاسخ وب سرور قرار نگیرند هیچ کاری در سمت کلاینت انجام نمیدهند.
در قسمت بعدی این سری مطالب به مباحث پیشرفتهتری چون SubCookieها در ASP.NET و هویت منحصر به فرد کوکیها در سمت سرور پرداخته میشود.
.
.
منابع
Build Events
$(<Macro_Name>)
$(OutDir) یا $(ProjectName)
Copy $(OutDir)*.* %WinDir%
Copy bin\Debug\*.* %WinDir%
Copy "$(ProjectDir)$(OutDir)*.*" c:\test
"$(ProjectDir)postBuild.bat" "$(SolutionPath)"
echo --------------------------------------------------------------------------- echo Copy "$(ProjectDir)$(OutDir)*.*" c:\test --Starting... Copy "$(ProjectDir)$(OutDir)*.*" c:\test if errorlevel 1 goto error echo Copy "$(ProjectDir)$(OutDir)*.*" c:\test --DONE! echo --------------------------------------------------------------------------- echo --------------------------------------------------------------------------- echo Copy $(OutDir)*.* c:\test --Starting... Copy $(OutDir)*.* c:\test if errorlevel 1 goto error echo Copy $(OutDir)*.* c:\test --DONE! echo --------------------------------------------------------------------------- goto ok :error echo POSTBUILDSTEP for $(ProjectName) FAILED notepad.exe exit 1 :ok echo POSTBUILDSTEP for $(ProjectName) COMPLETED OK
if $(ConfigurationName) == Release ( gacutil.exe /i "$(SolutionDir)$(OutDir)$(TargetFileName)" )
... <PropertyGroup> <PostBuildEvent>echo --------------------------------------------------------------------------- echo Copy "$(ProjectDir)$(OutDir)*.*" c:\test --Starting... Copy "$(ProjectDir)$(OutDir)*.*" c:\test if errorlevel 1 goto error echo Copy "$(ProjectDir)$(OutDir)*.*" c:\test --DONE! echo --------------------------------------------------------------------------- echo --------------------------------------------------------------------------- echo Copy $(OutDir)*.* c:\test --Starting... Copy $(OutDir)*.* c:\test if errorlevel 1 goto error echo Copy $(OutDir)*.* c:\test --DONE! echo --------------------------------------------------------------------------- goto ok :error echo POSTBUILDSTEP for $(ProjectName) FAILED notepad.exe exit 1 :ok echo POSTBUILDSTEP for $(ProjectName) COMPLETED OK</PostBuildEvent> </PropertyGroup> ...
Could not load type 'System.Windows.Forms.Form' from assembly 'System.Windows.Forms, Version=3.5.0.0, Culture=neutral, PublicKeyToken=969DB8053D3322AC'. System.Windows.Forms 3.5.7283.0 but 3.5.0.0 windows CE
برای انجام این فرآیند احتیاج به نصب مقدماتی بر روی یک ویندوز تازه است که ترتیب نصب آن نیز بسیار مهم است:
1. Microsoft Visual Studio 2005 2. Microsoft Visual Studio 2005 Service Pack 1 3. Microsoft Windows Embedded CE 6.0 Toolkit 4. Windows Embedded CE 6.0 Platform Builder Service Pack 1 5. Windows Embedded CE 6.0 R2 6. Windows Embedded CE 6.0 R3