Although most of the attention is on .NET Core, development continues on the original .NET Framework. Microsoft has recently released a preview of .NET Framework 4.7.2 with updates to ASP.NET, ADO.NET, WCF, WPF, and even Windows Forms. You can see the complete release notes on GitHub.
برای مثال پروژه "unhaddins" را در نظر بگیرید. این پروژه یک سری افزونه را جهت کار سادهتر با NHibernate ارائه داده است. برای مثال چگونه با WPF یا WCF و امثال آن بتوان به سادگی با NHibernate ارتباط برقرار کرد. این پروژه خروجی قابل دریافتی ندارد؛ به عبارتی یک سری سورس کد است. دریافت یک مخزن کد هم که از گوگل کد در این سمت مشکل است ... اما راه بهتری هم وجود دارد. یکی از خواص کار با سورس کنترلها، امکان تهیه یک clone از یک مخزن کد است. تمام پروژههای موجود در گوگل کد هم به این شکل با SVN در دسترس هستند:
http://someproject.googlecode.com/svn/trunk/
که به جای someproject ، نام پروژه مورد نظر قرار خواهد گرفت.برای نمونه، در سایت https://bitbucket.org ثبت نام کنید. سپس گزینه ایجاد یک مخزن جدید را انتخاب کرده:
و در صفحهی باز شده، گزینهی Import from Subversion را انتخاب کنید:
در اینجا Url خواسته شده باید شبیه به همان آدرس trunk فوق باشد و اگر تیک private فعال باشد (که هست)، دیگران امکان دسترسی به مخزن کد شما را نخواهند داشت. البته این تنظیم پس از دریافت، در برگهی Admin مخزن ایجاد شده نیز قابل تغییر است.
به علاوه سایت github.com هم هر چند بر اساس Git کار میکند، اما امکان تهیه یک کپی مطابق اصل از یک مخزن کد SVN را هم دارد؛ به شرح زیر:
یک اکانت رایگان در GitHub درست کنید. بعد یک مخزن خالی جدید را ایجاد کرده و در همان صفحه روی لینک Import a Subversion Repository کلیک کنید و آدرس svn مورد نظر را بدهید.
البته GitHub در دریافت پروژه unhaddins موفق عمل نکرد، اما bitbucket خیلی سریع کل آنرا دریافت نمود.
اشتراکها
نگاهی به NET 5.
For now, Microsoft's .NET Core milestones page shows .NET 5.0 about 53 percent complete, with 1,066 issues open and 1,216 issues closed.
Highlights of .NET 5 include:
- Developers will have more choice on runtime experiences.
- Java interoperability will be available on all platforms.
- Objective-C and Swift interoperability will be supported on multiple operating systems.
- CoreFX will be extended to support static compilation of .NET (ahead-of-time – AOT), smaller footprints and support for more operating systems.
در قسمت آخر این سری، نگاهی خواهیم داشت به نحوهی توزیع برنامههای React و نکات مرتبط با آن.
افزودن متغیرهای محیطی
در برنامهی نمایش لیست فیلمهایی که تا قسمت 29 آنرا بررسی کردیم، از فایل src\config.json برای ذخیره سازی اطلاعات تنظیمات برنامه استفاده شد. هرچند این روش کار میکند اما بر اساس محیطهای مختلف توسعه، متغیر نیست. اغلب برنامهها باید بتوانند حداقل در سه محیط توسعه، آزمایش و تولید، بر اساس متغیرها و تنظیمات خاص هر کدام، کار کنند. برای مثال بر روی سیستمی که کار توسعه در آن انجام میشود، میخواهیم apiUrl متفاوتی را نسبت به حالتیکه برنامه توزیع میشود، داشته باشیم.
برای رفع این مشکل، برنامههایی که توسط create-react-app تولید میشوند، دارای پشتیبانی توکاری از متغیرهای محیطی هستند. برای این منظور نیاز است در ریشهی پروژه (جائیکه فایل package.json قرار دارد) فایل جدید env. را ایجاد کرد. در ویندوز برای ایجاد یک چنین فایلهایی که فقط از یک پسوند تشکیل میشوند، باید نام فایل را به صورت .env. وارد کرد؛ سپس خود ویندوز نقطهی نهایی را حذف میکند. البته اگر از ادیتور VSCode برای ایجاد این فایل استفاده میکنید، نیازی به درج نقطهی انتهایی نیست. در این فایل environment ایجاد شده میتوان تمام متغیرهای محیطی مورد نیاز را با مقادیر پیشفرض آنها درج کرد. همچنین میتوان این مقادیر پیشفرض را بر اساس محیطهای مختلف کاری، بازنویسی کرد. برای مثال میتوان فایل env.development. را اضافه کرد؛ به همراه فایلهای env.test. و env.production.
متغیرهای محیطی به صورت key=value درج میشوند. این کلیدها نیر باید با REACT_APP_ شروع شوند؛ در غیر اینصورت، کار نخواهند کرد. برای مثال در فایل env.، دو متغیر پیشفرض زیر را تعریف میکنیم:
اکنون برای خواندن این متغیرها برای مثال در فایل index.js (و یا هر فایل جاوا اسکریپتی دیگری در برنامه)، سطر زیر را درج میکنیم:
process به معنای پروسهی جاری برنامهاست (و مرتبط است به پروسهی node.js ای که برنامهی React را اجرا میکند) و خاصیت env، به همراه تمام متغیرهای محیطی برنامه میباشد. در این حالت اگر برنامه را اجرا کنیم، در کنسول توسعه دهندگان مرورگر، به یک چنین خروجی خواهیم رسید:
در این خروجی، متغیر "NODE_ENV: "development به صورت خودکار با تولید بستههای مخصوص ارائهی نهایی، به production تنظیم میشود. سایر متغیرهای محیطی تعریف شده را نیز در اینجا ملاحظه میکنید. با توجه به خواص شیء env، برای مثال جهت دسترسی به نام برنامه میتوان از مقدار process.env.REACT_APP_NAME استفاده کرد.
یک نکته: با هر تغییری در مقادیر متغیرهای محیطی، نیاز است یکبار دیگر برنامه را از ابتدا توسط دستور npm start، راه اندازی مجدد کرد؛ چون این فایلها به صورت خودکار ردیابی نمیشوند.
نحوهی پردازش متغیرهای محیطی درج شدهی در برنامه
اگر همان سطر لاگ کردن خروجی process.env را به صورت زیر تغییر دهیم:
و برنامه را مجددا اجرا کنیم، با مراجعهی به برگهی Sources و انتخاب مسیر localhost:3000/static/js/main.chunk.js و سپس جستجوی "My App Name" ای که در اینجا اضافه کردیم (با فشردن دکمههای Ctrl+F)، به خروجی زیر خواهیم رسید:
همانطور که مشاهده میکنید، فراخوانی console.log ما، دیگر به همراه متغیر process.env.REACT_APP_NAME نیست؛ بلکه مقدار اصلی این متغیر در اینجا درج شدهاست. بنابراین اگر در در حین توسعهی برنامه، از متغیرهای محیطی استفاده شود، این متغیرها با مقادیر اصلی آنها در حین پروسهی Build نهایی، جایگزین میشوند.
Build برنامههای React برای محیط تولید
اجرای دستور npm start، سبب ایجاد یک Build مخصوص محیط توسعه میشود که بهینه سازی نشدهاست و به همراه اطلاعات اضافی قابل توجهی جهت دیباگ سادهتر برنامهاست. برای رسیدن به یک خروجی بهینه سازی شدهی مخصوص محیط تولید و ارائهی نهایی باید دستور npm run build را در خط فرمان اجرا کرد. خروجی نهایی این دستور، در پوشهی جدید build واقع در ریشهی پروژه، قرار میگیرد. اکنون میتوان کل محتویات این پوشه را جهت ارائهی نهایی در وب سرور خود، مورد استفاده قرار داد.
پس از پایان اجرای دستور npm run build، پیام «امکان ارائهی آن توسط static server زیر نیز وجود دارد» ظاهر میشود:
اگر علاقمند باشید تا خروجی حالت production تولید شده را نیز به صورت محلی آزمایش کنید، ابتدا باید static server یاد شده را توسط دستور npm install فوق نصب کنید. سپس ریشهی پروژه را در خط فرمان باز کرده و دستور serve -s build را صادر کنید (البته اگر با خط فرمان به پوشهی build وارد شدید، دیگر نیازی به ذکر پوشهی build نخواهد بود). اکنون میتوانید برنامه را در آدرس http://localhost:5000 در مرورگر خود بررسی نمائید.
البته با توجه به اینکه backend سرور برنامههای ما نیز در همین آدرس قرار دارد و در صورت ورود این آدرس، به صورت خودکار به https://localhost:5001/index.html هدایت خواهید شد، میتوان این پورت پیشفرض را با اجرای دستور serve -s build -l 1234 تغییر داد. اکنون میتوان آدرس جدید http://localhost:1234 را در مرورگر آزمایش کرد که ... با خطای زیر کار نمیکند:
روش رفع این مشکل را در قسمت 23 بررسی کردیم و در اینجا جهت بهبود آن میتوان متد WithOrigins فایل Startup.cs را به صورت زیر تکمیل کرد:
یک نکته: زمانیکه از دستور npm start استفاده میشود، متغیرهای محیطی از فایل env.development. خوانده خواهند شد و زمانیکه از دستور npm run build استفاده میشود، این متغیرها از فایل env.production. تامین میشوند. در این حالتها اگر متغیری در این دو فایل درج نشده بود، از مقدار پیشفرض موجود در فایل env. استفاده میگردد. از فایل env.test. با اجرای دستور npm test، به صورت خودکار استفاده میشود.
آماده سازی برنامهی React، برای توزیع نهایی
تا اینجا برنامهی React تهیه شده، اطلاعات apiUrl خودش را از فایل config.json دریافت میکند. اکنون میخواهیم بر اساس حالات مختلف توسعه و تولید، از apiUrlهای متفاوتی استفاده شود. به همین جهت به فایل env.production. مراجعه کرده و تنظیمات ذیل را به آن اضافه میکنیم:
البته فعلا همین متغیرها را به فایل env.development. نیز میتوان اضافه کرد؛ چون backend سرور ما در هر دو حالت، در این مثال، در آدرس فوق قرار دارد.
اکنون به برنامه مراجعه کرده و در هرجائی که ارجاعی به فایل config.json وجود دارد، سطر import آنرا حذف میکنیم. با این تغییر، تمام آدرسهایی مانند:
را به صورت زیر ویرایش میکنیم:
در ادامه برای تامین مقدار apiUrl به صورت خودکار، به فایل src\services\httpService.js مراجعه کرده و در ابتدای آن، یک سطر زیر را اضافه میکنیم:
به این ترتیب تمام درخواستهای ارسالی توسط Axios، دارای baseURL ای خواهند شد که از فایل متغیر محیطی جاری تامین میشود. همانطور که پیشتر نیز عنوان شد، این مقدار در زمان Build، با مقدار ثابتی که از فایل env جاری خوانده میشود، جایگزین خواهد شد.
همچنین adminRoleName مورد نیاز در فایل src\services\authService.js را نیز از همان فایل env جاری تامین میکنیم:
پس از این تغییرات، نیاز است برای حالت توسعه، یکبار دیگر دستور npm start و یا برای حالت تولید، دستور npm run build را اجرا کرد تا اطلاعات درج شدهی در فایلهای env.، پردازش و جایگزین شوند.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-34-frontend.zip و sample-34-backend.zip
افزودن متغیرهای محیطی
در برنامهی نمایش لیست فیلمهایی که تا قسمت 29 آنرا بررسی کردیم، از فایل src\config.json برای ذخیره سازی اطلاعات تنظیمات برنامه استفاده شد. هرچند این روش کار میکند اما بر اساس محیطهای مختلف توسعه، متغیر نیست. اغلب برنامهها باید بتوانند حداقل در سه محیط توسعه، آزمایش و تولید، بر اساس متغیرها و تنظیمات خاص هر کدام، کار کنند. برای مثال بر روی سیستمی که کار توسعه در آن انجام میشود، میخواهیم apiUrl متفاوتی را نسبت به حالتیکه برنامه توزیع میشود، داشته باشیم.
برای رفع این مشکل، برنامههایی که توسط create-react-app تولید میشوند، دارای پشتیبانی توکاری از متغیرهای محیطی هستند. برای این منظور نیاز است در ریشهی پروژه (جائیکه فایل package.json قرار دارد) فایل جدید env. را ایجاد کرد. در ویندوز برای ایجاد یک چنین فایلهایی که فقط از یک پسوند تشکیل میشوند، باید نام فایل را به صورت .env. وارد کرد؛ سپس خود ویندوز نقطهی نهایی را حذف میکند. البته اگر از ادیتور VSCode برای ایجاد این فایل استفاده میکنید، نیازی به درج نقطهی انتهایی نیست. در این فایل environment ایجاد شده میتوان تمام متغیرهای محیطی مورد نیاز را با مقادیر پیشفرض آنها درج کرد. همچنین میتوان این مقادیر پیشفرض را بر اساس محیطهای مختلف کاری، بازنویسی کرد. برای مثال میتوان فایل env.development. را اضافه کرد؛ به همراه فایلهای env.test. و env.production.
متغیرهای محیطی به صورت key=value درج میشوند. این کلیدها نیر باید با REACT_APP_ شروع شوند؛ در غیر اینصورت، کار نخواهند کرد. برای مثال در فایل env.، دو متغیر پیشفرض زیر را تعریف میکنیم:
REACT_APP_NAME=My App REACT_APP_VERSION=1
console.log(process.env);
در این خروجی، متغیر "NODE_ENV: "development به صورت خودکار با تولید بستههای مخصوص ارائهی نهایی، به production تنظیم میشود. سایر متغیرهای محیطی تعریف شده را نیز در اینجا ملاحظه میکنید. با توجه به خواص شیء env، برای مثال جهت دسترسی به نام برنامه میتوان از مقدار process.env.REACT_APP_NAME استفاده کرد.
یک نکته: با هر تغییری در مقادیر متغیرهای محیطی، نیاز است یکبار دیگر برنامه را از ابتدا توسط دستور npm start، راه اندازی مجدد کرد؛ چون این فایلها به صورت خودکار ردیابی نمیشوند.
نحوهی پردازش متغیرهای محیطی درج شدهی در برنامه
اگر همان سطر لاگ کردن خروجی process.env را به صورت زیر تغییر دهیم:
console.log("My App Name", process.env.REACT_APP_NAME);
همانطور که مشاهده میکنید، فراخوانی console.log ما، دیگر به همراه متغیر process.env.REACT_APP_NAME نیست؛ بلکه مقدار اصلی این متغیر در اینجا درج شدهاست. بنابراین اگر در در حین توسعهی برنامه، از متغیرهای محیطی استفاده شود، این متغیرها با مقادیر اصلی آنها در حین پروسهی Build نهایی، جایگزین میشوند.
Build برنامههای React برای محیط تولید
اجرای دستور npm start، سبب ایجاد یک Build مخصوص محیط توسعه میشود که بهینه سازی نشدهاست و به همراه اطلاعات اضافی قابل توجهی جهت دیباگ سادهتر برنامهاست. برای رسیدن به یک خروجی بهینه سازی شدهی مخصوص محیط تولید و ارائهی نهایی باید دستور npm run build را در خط فرمان اجرا کرد. خروجی نهایی این دستور، در پوشهی جدید build واقع در ریشهی پروژه، قرار میگیرد. اکنون میتوان کل محتویات این پوشه را جهت ارائهی نهایی در وب سرور خود، مورد استفاده قرار داد.
پس از پایان اجرای دستور npm run build، پیام «امکان ارائهی آن توسط static server زیر نیز وجود دارد» ظاهر میشود:
> npm install -g serve > serve -s build
البته با توجه به اینکه backend سرور برنامههای ما نیز در همین آدرس قرار دارد و در صورت ورود این آدرس، به صورت خودکار به https://localhost:5001/index.html هدایت خواهید شد، میتوان این پورت پیشفرض را با اجرای دستور serve -s build -l 1234 تغییر داد. اکنون میتوان آدرس جدید http://localhost:1234 را در مرورگر آزمایش کرد که ... با خطای زیر کار نمیکند:
Access to XMLHttpRequest at 'https://localhost:5001/api/genres' from origin 'http://localhost:1234' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
WithOrigins("http://localhost:3000", "http://localhost:1234")
یک نکته: زمانیکه از دستور npm start استفاده میشود، متغیرهای محیطی از فایل env.development. خوانده خواهند شد و زمانیکه از دستور npm run build استفاده میشود، این متغیرها از فایل env.production. تامین میشوند. در این حالتها اگر متغیری در این دو فایل درج نشده بود، از مقدار پیشفرض موجود در فایل env. استفاده میگردد. از فایل env.test. با اجرای دستور npm test، به صورت خودکار استفاده میشود.
آماده سازی برنامهی React، برای توزیع نهایی
تا اینجا برنامهی React تهیه شده، اطلاعات apiUrl خودش را از فایل config.json دریافت میکند. اکنون میخواهیم بر اساس حالات مختلف توسعه و تولید، از apiUrlهای متفاوتی استفاده شود. به همین جهت به فایل env.production. مراجعه کرده و تنظیمات ذیل را به آن اضافه میکنیم:
REACT_APP_API_URL=https://localhost:5001/api REACT_APP_ADMIN_ROLE_NAME=Admin
اکنون به برنامه مراجعه کرده و در هرجائی که ارجاعی به فایل config.json وجود دارد، سطر import آنرا حذف میکنیم. با این تغییر، تمام آدرسهایی مانند:
const apiEndpoint = apiUrl + "/users";
const apiEndpoint = "/users";
axios.defaults.baseURL = process.env.REACT_APP_API_URL;
همچنین adminRoleName مورد نیاز در فایل src\services\authService.js را نیز از همان فایل env جاری تامین میکنیم:
const adminRoleName = process.env.REACT_APP_ADMIN_ROLE_NAME;
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-34-frontend.zip و sample-34-backend.zip
پیشنیازها (الزامی)
«بررسی مفاهیم معکوس سازی وابستگیها و ابزارهای مرتبط با آن»
«اصول طراحی SOLID»
«مطالعهی بیشتر»
تزریق وابستگیها (یا Dependency injection = DI) به معنای ارسال نمونهای/وهلهای از وابستگی (یک سرویس) به شیء وابستهی به آن (یک کلاینت) است. در فرآیند تزریق وابستگیها، یک کلاس، وهلههای کلاسهای دیگر مورد نیاز خودش را بجای وهله سازی مستقیم، از یک تزریق کننده دریافت میکند. بنابراین بجای نوشتن newها در کلاس جاری، آنها را به صورت وابستگیهایی در سازندهی کلاس تعریف میکنیم تا توسط یک IoC Container تامین شوند. در اینجا به فریم ورکهایی که کار وهله سازی این وابستگیها را انجام میدهند، IoC Container و یا DI container میگوییم (IoC = inversion of control ).
چندین نوع تزریق وابستگیها وجود دارند که دو حالت زیر، عمومیترین آنها است:
الف) تزریق در سازندهی کلاس: لیست وابستگیهای یک کلاس، به عنوان پارامترهای سازندهی آن ذکر میشوند.
ب) تزریق در خواص یا Setter injection: کلاینت خواصی get و set را به صورت public معرفی میکند و سپس IoC Container با وهله سازی آنها، وابستگیهای مورد نیاز را تامین خواهد کرد.
تزریق وابستگیها در ASP.NET Core
برخلاف نگارشهای قبلی ASP.NET، این نگارش جدید از ابتدا با دید پشتیبانی کامل از DI طراحی شدهاست و این مفهوم، در سراسر اجزای آن به صورت یکپارچهای پشتیبانی میشود. همچنین به همراه یک minimalistic DI container توکار نیز هست .
این IoC Container توکار از 4 حالت طول عمر ذیل پشتیبانی میکند:
- instance: در هربار نیاز به یک وابستگی خاص، تنها یک وهله از آن در اختیار مصرف کننده قرار میگیرد و در اینجا شما هستید که مسئول تعریف نحوهی وهله سازی این شیء خواهید بود (برای بار اول).
- transient: هربار که نیاز به وابستگی خاصی بود، یک وهلهی جدید از آن توسط IoC Container تولید و ارائه میشود.
- singleton: در این حالت تنها یک وهله از وابستگی درخواست شده در طول عمر برنامه تامین میشود.
- scoped: در طول عمر یک scope خاص، تنها یک وهله از وابستگی درخواست شده، در اختیار مصرف کنندهها قرار میگیرد. برای مثال مرسوم است که به ازای یک درخواست وب، تنها یک وهله از شیءایی خاص در اختیار تمام مصرف کنندههای آن قرار گیرد (single instance per web request).
طول عمر singleton، برای سرویسها و کلاسهای config مناسب هستند. به این ترتیب به کارآیی بالاتری خواهیم رسید و دیگر نیازی نخواهد بود تا هر بار این اطلاعات خوانده شوند. حالت scoped برای وهله سازی الگوی واحد کار و پیاده سازی تراکنشها مناسب است. برای مثال در طی یک درخواست وب، یک تراکنش باید صورت گیرد.
حالت scoped در حقیقت نوع خاصی از حالت transient است. در حالت transient صرفنظر از هر حالتی، هربار که وابستگی ویژهای درخواست شود، یک وهلهی جدید از آن تولید خواهد شد. اما در حالت scoped فقط یک وهلهی از وابستگی مورد نظر، در بین تمام اشیاء وابستهی به آن، در طول عمر آن scope تولید میشود.
بنابراین در برنامههای وب دو نوع singleton برای معرفی کلاسهای config و نوع scoped برای پیاده سازی تراکنشها و همچنین بالابردن کارآیی برنامه در طی یک درخواست وب (با عدم وهله سازی بیش از اندازهی از کلاسهای مختلف مورد نیاز)، بیشتر از همه به کار برده میشوند.
یک مثال کاربردی: بررسی نحوهی تزریق یک سرویس سفارشی به کمک IoC Container توکار ASP.NET Core
مثال جاری که بر اساس ASP.NET Core Web Application و با قالب خالی آن ایجاد شدهاست، دارای نام فرضی Core1RtmEmptyTest است. در همین پروژه بر روی پوشهی src، کلیک راست کرده و گزینهی Add new project را انتخاب کنید و سپس یک پروژهی جدید از نوع NET Core -> Class library. را به آن، با نام Core1RtmEmptyTest.Services اضافه کنید (تصویر فوق).
در ادامه کلاس نمونهی سرویس پیامها را به همراه اینترفیس آن، با محتوای زیر به آن اضافه کنید:
در ادامه به پروژهی Core1RtmEmptyTest مراجعه کرده و بر روی گره references آن کلیک راست کنید. در اینجا گزینهی add reference را انتخاب کرده و سپس Core1RtmEmptyTest.Services را انتخاب کنید، تا اسمبلی آنرا بتوان در پروژهی جاری استفاده کرد.
انجام اینکار معادل است با افزودن یک سطر ذیل به فایل project.json پروژه:
در ادامه قصد داریم این سرویس را به متد Configure کلاس Startup تزریق کرده و سپس خروجی رشتهای آنرا توسط میان افزار Run آن نمایش دهیم. برای این منظور فایل Startup.cs را گشوده و امضای متد Configure را به نحو ذیل تغییر دهید:
همانطور که در قسمت قبل نیز عنوان شد، متد Configure دارای امضای ثابتی نیست و هر تعداد سرویسی را که نیاز است، میتوان در اینجا اضافه کرد. یک سری از سرویسها مانند IApplicationBuilder و IHostingEnvironment پیشتر توسط IoC Container توکار ASP.NET Core معرفی و ثبت شدهاند. به همین جهت، همینقدر که در اینجا ذکر شوند، کار میکنند و نیازی به تنظیمات اضافهتری ندارند. اما سرویس IMessagesService ما هنوز به این IoC Container معرفی نشدهاست. بنابراین نمیداند که چگونه باید این اینترفیس را وهله سازی کند.
در این حالت اگر برنامه را اجرا کنیم، به این خطا برخواهیم خورد:
برای رفع این مشکل، به متد ConfigureServices کلاس Startup مراجعه کرده و سیم کشیهای مرتبط را انجام میدهیم. در اینجا باید اعلام کنیم که «هر زمانیکه به IMessagesService رسیدی، یک وهلهی جدید (transient) از کلاس MessagesService را به صورت خودکار تولید کن و سپس در اختیار مصرف کننده قرار بده»:
در اینجا نحوهی ثبت یک سرویس را در IoC Containser توکار ASP.NET Core ملاحظه میکنید. تمام حالتهای طول عمری که در ابتدای بحث عنوان شدند، یک متد ویژهی خاص خود را در اینجا دارند. برای مثال حالت transient دارای متد ویژهی AddTransient است و همینطور برای سایر حالتها. این متدها به صورت جنریک تعریف شدهاند و آرگومان اول آنها، اینترفیس سرویس و آرگومان دوم، پیاده سازی آنها است (سیم کشی اینترفیس، به کلاس پیاده سازی کنندهی آن).
پس از اینکار، مجددا برنامه را اجرا کنید. اکنون این خروجی باید مشاهده شود:
و به این معنا است که اکنون IoC Cotanier توکار ASP.NET Core، میداند زمانیکه به IMessagesService رسید، چگونه باید آنرا وهله سازی کند.
چه سرویسهایی به صورت پیش فرض در IoC Container توکار ASP.NET Core ثبت شدهاند؟
در ابتدای متد ConfigureServices یک break point را قرار داده و برنامه را در حالت دیباگ اجرا کنید:
همانطور که ملاحظه میکنید، به صورت پیش فرض 16 سرویس در اینجا ثبت شدهاند که تاکنون با دو مورد از آنها کار کردهایم.
امکان تزریق وابستگیها در همه جا!
در مثال فوق، سرویس سفارشی خود را در متد Configure کلاس آغازین برنامه تزریق کردیم. نکتهی مهم اینجا است که برخلاف نگارشهای قبلی ASP.NET MVC (یعنی بدون نیاز به تنظیمات خاصی برای قسمتهای مختلف برنامه)، میتوان این تزریقها را در کنترلرها، در میان افزارها، در فیلترها در ... همه جا و تمام اجزای ASP.NET Core 1.0 انجام داد و دیگر اینبار نیازی نیست تا نکتهی ویژهی نحوهی تزریق وابستگیها در فیلترها یا کنترلرهای ASP.NET MVC را یافته و سپس اعمال کنید. تمام اینها از روز اول کار میکنند. همینقدر که کار ثبت سرویس خود را در متد ConfigureServices انجام دادید، این سرویس در سراسر اکوسیستم ASP.NET Core، قابل دسترسی است.
نیاز به تعویض IoC Container توکار ASP.NET Core
قابلیت تزریق وابستگیهای توکار ASP.NET Core صرفا جهت برآورده کردن نیازمندیهای اصلی آن طراحی شدهاست و نه بیشتر. بنابراین توسط آن قابلیتهای پیشرفتهای را که سایر IoC Containers ارائه میدهند، نخواهید یافت. برای مثال تعویض امکانات تزریق وابستگیهای توکار ASP.NET Core با StructureMap این مزایا را به همراه خواهد داشت:
• امکان ایجاد child/nested containers (پشتیبانی از سناریوهای چند مستاجری)
• پشتیبانی از Setter Injection
• امکان انتخاب سازندهای خاص (اگر چندین سازنده تعریف شده باشند)
• سیم کشی خودکار یا Conventional "Auto" Registration (برای مثال اتصال اینترفیس IName به کلاس Name به صورت خودکار و کاهش تعداد تعاریف ابتدای برنامه)
• پشتیبانی توکار از Lazy و Func
• امکان وهله سازی از نوعهای concrete (یا همان کلاسهای معمولی)
• پشتیبانی از مفاهیمی مانند Interception و AOP
• امکان اسکن اسمبلیهای مختلف جهت یافتن اینترفیسها و اتصال خودکار آنها (طراحیهای افزونه پذیر)
روش تعویض IoC Container توکار ASP.NET Core با StructureMap
جزئیات این جایگزین کردن را در مطلب «جایگزین کردن StructureMap با سیستم توکار تزریق وابستگیها در ASP.NET Core 1.0» میتوانید مطالعه کنید.
یا میتوانید از روش فوق استفاده کنید و یا اکنون قسمتی از پروژهی رسمی استراکچرمپ در آدرس https://github.com/structuremap/structuremap.dnx جهت کار با NET Core. طراحی شدهاست. برای کار با آن نیاز است این مراحل طی شوند:
الف) دریافت بستهی نیوگت StructureMap.Dnx
برای این منظور بر روی گره references کلیک راست کرده و گزینهی manage nuget packages را انتخاب کنید. سپس در برگهی browse آن، StructureMap.Dnx را جستجو کرده و نصب نمائید (تیک مربوط به انتخاب pre releases هم باید انتخاب شده باشد):
انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
ب) جایگزین کردن Container استراکچرمپ با Container توکار ASP.NET Core
پس از نصب بستهی StructureMap.Dnx، به کلاس آغازین برنامه مراجعه کرده و این تغییرات را اعمال کنید:
در اینجا ابتدا خروجی متد ConfigureServices، به IServiceProvider تغییر کردهاست تا استراکچرمپ این تامین کنندهی سرویسها را ارائه دهد. سپس Container مربوط به استراکچرمپ، وهله سازی شده و همانند روال متداول آن، یک سرویس و کلاس پیاده سازی کنندهی آن معرفی شدهاند (و یا هر تنظیم دیگری را که لازم بود باید در اینجا اضافه کنید). در پایان کار متد Configure آن و پس از این متد، نیاز است متدهای Populate فراخوانی شوند (اولی تعاریف را اضافه میکند و دومی کار تنظیمات را نهایی خواهد کرد).
سپس وهلهای از IServiceProvider، توسط استراکچرمپ تامین شده و بازگشت داده میشود تا بجای IoC Container توکار ASP.NET Core استفاده شود.
در این مثال چون در متد Scan، کار بررسی اسمبلی لایه سرویس برنامه با قراردادهای پیش فرض استراکچرمپ انجام شدهاست، دیگر نیازی به سطر تعریف config.For نیست. در اینجا هرگاه IName ایی یافت شد، به کلاس Name متصل میشود (name هر نامی میتواند باشد).
«بررسی مفاهیم معکوس سازی وابستگیها و ابزارهای مرتبط با آن»
«اصول طراحی SOLID»
«مطالعهی بیشتر»
تزریق وابستگیها (یا Dependency injection = DI) به معنای ارسال نمونهای/وهلهای از وابستگی (یک سرویس) به شیء وابستهی به آن (یک کلاینت) است. در فرآیند تزریق وابستگیها، یک کلاس، وهلههای کلاسهای دیگر مورد نیاز خودش را بجای وهله سازی مستقیم، از یک تزریق کننده دریافت میکند. بنابراین بجای نوشتن newها در کلاس جاری، آنها را به صورت وابستگیهایی در سازندهی کلاس تعریف میکنیم تا توسط یک IoC Container تامین شوند. در اینجا به فریم ورکهایی که کار وهله سازی این وابستگیها را انجام میدهند، IoC Container و یا DI container میگوییم (IoC = inversion of control ).
چندین نوع تزریق وابستگیها وجود دارند که دو حالت زیر، عمومیترین آنها است:
الف) تزریق در سازندهی کلاس: لیست وابستگیهای یک کلاس، به عنوان پارامترهای سازندهی آن ذکر میشوند.
ب) تزریق در خواص یا Setter injection: کلاینت خواصی get و set را به صورت public معرفی میکند و سپس IoC Container با وهله سازی آنها، وابستگیهای مورد نیاز را تامین خواهد کرد.
تزریق وابستگیها در ASP.NET Core
برخلاف نگارشهای قبلی ASP.NET، این نگارش جدید از ابتدا با دید پشتیبانی کامل از DI طراحی شدهاست و این مفهوم، در سراسر اجزای آن به صورت یکپارچهای پشتیبانی میشود. همچنین به همراه یک minimalistic DI container توکار نیز هست .
این IoC Container توکار از 4 حالت طول عمر ذیل پشتیبانی میکند:
- instance: در هربار نیاز به یک وابستگی خاص، تنها یک وهله از آن در اختیار مصرف کننده قرار میگیرد و در اینجا شما هستید که مسئول تعریف نحوهی وهله سازی این شیء خواهید بود (برای بار اول).
- transient: هربار که نیاز به وابستگی خاصی بود، یک وهلهی جدید از آن توسط IoC Container تولید و ارائه میشود.
- singleton: در این حالت تنها یک وهله از وابستگی درخواست شده در طول عمر برنامه تامین میشود.
- scoped: در طول عمر یک scope خاص، تنها یک وهله از وابستگی درخواست شده، در اختیار مصرف کنندهها قرار میگیرد. برای مثال مرسوم است که به ازای یک درخواست وب، تنها یک وهله از شیءایی خاص در اختیار تمام مصرف کنندههای آن قرار گیرد (single instance per web request).
طول عمر singleton، برای سرویسها و کلاسهای config مناسب هستند. به این ترتیب به کارآیی بالاتری خواهیم رسید و دیگر نیازی نخواهد بود تا هر بار این اطلاعات خوانده شوند. حالت scoped برای وهله سازی الگوی واحد کار و پیاده سازی تراکنشها مناسب است. برای مثال در طی یک درخواست وب، یک تراکنش باید صورت گیرد.
حالت scoped در حقیقت نوع خاصی از حالت transient است. در حالت transient صرفنظر از هر حالتی، هربار که وابستگی ویژهای درخواست شود، یک وهلهی جدید از آن تولید خواهد شد. اما در حالت scoped فقط یک وهلهی از وابستگی مورد نظر، در بین تمام اشیاء وابستهی به آن، در طول عمر آن scope تولید میشود.
بنابراین در برنامههای وب دو نوع singleton برای معرفی کلاسهای config و نوع scoped برای پیاده سازی تراکنشها و همچنین بالابردن کارآیی برنامه در طی یک درخواست وب (با عدم وهله سازی بیش از اندازهی از کلاسهای مختلف مورد نیاز)، بیشتر از همه به کار برده میشوند.
یک مثال کاربردی: بررسی نحوهی تزریق یک سرویس سفارشی به کمک IoC Container توکار ASP.NET Core
مثال جاری که بر اساس ASP.NET Core Web Application و با قالب خالی آن ایجاد شدهاست، دارای نام فرضی Core1RtmEmptyTest است. در همین پروژه بر روی پوشهی src، کلیک راست کرده و گزینهی Add new project را انتخاب کنید و سپس یک پروژهی جدید از نوع NET Core -> Class library. را به آن، با نام Core1RtmEmptyTest.Services اضافه کنید (تصویر فوق).
در ادامه کلاس نمونهی سرویس پیامها را به همراه اینترفیس آن، با محتوای زیر به آن اضافه کنید:
namespace Core1RtmEmptyTest.Services { public interface IMessagesService { string GetSiteName(); } public class MessagesService : IMessagesService { public string GetSiteName() { return "DNT"; } } }
انجام اینکار معادل است با افزودن یک سطر ذیل به فایل project.json پروژه:
{ "dependencies": { // same as before "Core1RtmEmptyTest.Services": "1.0.0-*" },
public void Configure( IApplicationBuilder app, IHostingEnvironment env, IMessagesService messagesService)
public void Configure( IApplicationBuilder app, IHostingEnvironment env, IMessagesService messagesService) { app.Run(async context => { var siteName = messagesService.GetSiteName(); await context.Response.WriteAsync($"Hello {siteName}"); }); }
System.InvalidOperationException No service for type 'Core1RtmEmptyTest.Services.IMessagesService' has been registered. at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) at Microsoft.AspNetCore.Hosting.Internal.ConfigureBuilder.Invoke(object instance, IApplicationBuilder builder) System.Exception Could not resolve a service of type 'Core1RtmEmptyTest.Services.IMessagesService' for the parameter 'messagesService' of method 'Configure' on type 'Core1RtmEmptyTest.Startup'. at Microsoft.AspNetCore.Hosting.Internal.ConfigureBuilder.Invoke(object instance, IApplicationBuilder builder) at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<IMessagesService, MessagesService>(); }
پس از اینکار، مجددا برنامه را اجرا کنید. اکنون این خروجی باید مشاهده شود:
و به این معنا است که اکنون IoC Cotanier توکار ASP.NET Core، میداند زمانیکه به IMessagesService رسید، چگونه باید آنرا وهله سازی کند.
چه سرویسهایی به صورت پیش فرض در IoC Container توکار ASP.NET Core ثبت شدهاند؟
در ابتدای متد ConfigureServices یک break point را قرار داده و برنامه را در حالت دیباگ اجرا کنید:
همانطور که ملاحظه میکنید، به صورت پیش فرض 16 سرویس در اینجا ثبت شدهاند که تاکنون با دو مورد از آنها کار کردهایم.
امکان تزریق وابستگیها در همه جا!
در مثال فوق، سرویس سفارشی خود را در متد Configure کلاس آغازین برنامه تزریق کردیم. نکتهی مهم اینجا است که برخلاف نگارشهای قبلی ASP.NET MVC (یعنی بدون نیاز به تنظیمات خاصی برای قسمتهای مختلف برنامه)، میتوان این تزریقها را در کنترلرها، در میان افزارها، در فیلترها در ... همه جا و تمام اجزای ASP.NET Core 1.0 انجام داد و دیگر اینبار نیازی نیست تا نکتهی ویژهی نحوهی تزریق وابستگیها در فیلترها یا کنترلرهای ASP.NET MVC را یافته و سپس اعمال کنید. تمام اینها از روز اول کار میکنند. همینقدر که کار ثبت سرویس خود را در متد ConfigureServices انجام دادید، این سرویس در سراسر اکوسیستم ASP.NET Core، قابل دسترسی است.
نیاز به تعویض IoC Container توکار ASP.NET Core
قابلیت تزریق وابستگیهای توکار ASP.NET Core صرفا جهت برآورده کردن نیازمندیهای اصلی آن طراحی شدهاست و نه بیشتر. بنابراین توسط آن قابلیتهای پیشرفتهای را که سایر IoC Containers ارائه میدهند، نخواهید یافت. برای مثال تعویض امکانات تزریق وابستگیهای توکار ASP.NET Core با StructureMap این مزایا را به همراه خواهد داشت:
• امکان ایجاد child/nested containers (پشتیبانی از سناریوهای چند مستاجری)
• پشتیبانی از Setter Injection
• امکان انتخاب سازندهای خاص (اگر چندین سازنده تعریف شده باشند)
• سیم کشی خودکار یا Conventional "Auto" Registration (برای مثال اتصال اینترفیس IName به کلاس Name به صورت خودکار و کاهش تعداد تعاریف ابتدای برنامه)
• پشتیبانی توکار از Lazy و Func
• امکان وهله سازی از نوعهای concrete (یا همان کلاسهای معمولی)
• پشتیبانی از مفاهیمی مانند Interception و AOP
• امکان اسکن اسمبلیهای مختلف جهت یافتن اینترفیسها و اتصال خودکار آنها (طراحیهای افزونه پذیر)
روش تعویض IoC Container توکار ASP.NET Core با StructureMap
جزئیات این جایگزین کردن را در مطلب «جایگزین کردن StructureMap با سیستم توکار تزریق وابستگیها در ASP.NET Core 1.0» میتوانید مطالعه کنید.
یا میتوانید از روش فوق استفاده کنید و یا اکنون قسمتی از پروژهی رسمی استراکچرمپ در آدرس https://github.com/structuremap/structuremap.dnx جهت کار با NET Core. طراحی شدهاست. برای کار با آن نیاز است این مراحل طی شوند:
الف) دریافت بستهی نیوگت StructureMap.Dnx
برای این منظور بر روی گره references کلیک راست کرده و گزینهی manage nuget packages را انتخاب کنید. سپس در برگهی browse آن، StructureMap.Dnx را جستجو کرده و نصب نمائید (تیک مربوط به انتخاب pre releases هم باید انتخاب شده باشد):
انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
{ "dependencies": { // same as before "StructureMap.Dnx": "0.5.1-rc2-final" },
پس از نصب بستهی StructureMap.Dnx، به کلاس آغازین برنامه مراجعه کرده و این تغییرات را اعمال کنید:
public class Startup { public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddDirectoryBrowser(); var container = new Container(); container.Configure(config => { config.Scan(_ => { _.AssemblyContainingType<IMessagesService>(); _.WithDefaultConventions(); }); //config.For<IMessagesService>().Use<MessagesService>(); config.Populate(services); }); container.Populate(services); return container.GetInstance<IServiceProvider>(); }
سپس وهلهای از IServiceProvider، توسط استراکچرمپ تامین شده و بازگشت داده میشود تا بجای IoC Container توکار ASP.NET Core استفاده شود.
در این مثال چون در متد Scan، کار بررسی اسمبلی لایه سرویس برنامه با قراردادهای پیش فرض استراکچرمپ انجام شدهاست، دیگر نیازی به سطر تعریف config.For نیست. در اینجا هرگاه IName ایی یافت شد، به کلاس Name متصل میشود (name هر نامی میتواند باشد).
- DateTime.Now = Cannot provide the value: host value not found
- MSVC codegen error vector reverse_iterator x64 C++17 debug build
- C2440 error building atlenc.h with /permissive- in VS 16.4 Preview 1
- Last Visual Studio update broke NTLM authentication on Android (seems like new version of Momo would be a reason)
- Visual Studio 16.3.1 fails at compiling template code (e.g. from Basler Pylon SDK)
- Using TypeScript 3.7 for IntelliSense
- XCode 11.3 is too new all of a sudden after update to 8.4
- You uploaded an APK or Android App Bundle with invalid or missing signing information for some of its files. You need to create a valid signed APK or Android App Bundle.
- Unable to Run UWP project
- JavaScript IntelliSense not working after update 16.4.1
- Xib : Xcode is to new(rendering problem when using custom components)
- Storyboard error: 'Xcode is too new'
- Stability improvements for debugging watchOS applications.
- Adds Xcode 11.3 SDK support.
- Fixed crash when fstack-protector-strong flag is enabled.
- Fixed some inconsistent behavior with Debugger.
Security Advisory Notice
CVE-2020-0602 ASP.NET Core Denial of Service Vulnerability
CVE-2020-0603 ASP.NET Core Remote Code Execution Vulnerability
CVE-2020-0605 .NET Core Remote Code Execution Vulnerability
CVE-2020-0606 .NET Core Remote Code Execution Vulnerability
در قسمت قبل، در حین بررسی رفتار جزیرههای تعاملی Blazor Server، نکتهی زیر را هم دربارهی راهبری صفحات SSR مرور کردیم:
« اگر دقت کنید، جابجایی بین صفحات، با استفاده از fetch انجام شده؛ یعنی با اینکه این صفحات در اصل static HTML خالص هستند، اما ... کار full reload صفحه مانند ASP.NET Web forms قدیمی انجام نمیشود (و یا حتی برنامههای MVC و Razor pages) و نمایش صفحات، Ajax ای است و با fetch استاندارد آن صورت میگیرد تا هنوز هم حس و حال SPA بودن برنامه حفظ شود. همچنین اطلاعات DOM کل صفحه را هم بهروز رسانی نمیکند؛ فقط موارد تغییر یافته در اینجا به روز رسانی خواهند شد.»
در این قسمت، نکات تکمیلی این قابلیت را که به آن enhanced navigation هم گفته میشود، بررسی میکنیم.
روش غیرفعال کردن راهبری بهبودیافته برای بعضی از لینکها
ویژگی راهبری بهبودیافته فقط در حین هدایت بین صفحات مختلف یک برنامهی Blazor 8x SSR، فعال است. اگر در این بین، کاربری به یک صفحهی غیر بلیزری هدایت شود، راهبری بهبود یافته شکست خورده و سعی میکند حالت full document load را پیاده سازی و اجرا کند. مشکل اینجاست که در این حالت دو درخواست ارسال میشود: ابتدا حالت راهبری بهبودیافته فعال میشود و در ادامه پس از شکست این راهبری، هدایت مستقیم صورت میگیرد. برای رفع این مشکل میتوان ویژگی جدید data-enhance-nav را با مقدار false، به لینکهای خارجی مدنظر اضافه کرد تا برای این حالتها دیگر ویژگی راهبری بهبودیافته فعال نشود:
<a href="/not-blazor" data-enhance-nav="false">A non-Blazor page</a>
فعالسازی مدیریت بهبودیافتهی فرمهای SSR
در قسمت چهارم این سری با فرمهای جدید SSR مخصوص Blazor 8x آشنا شدیم. این فرمها هم میتوانند از امکانات راهبری بهبود یافته استفاده کنند (یعنی مدیریت ارسال آن، توسط fetch API انجام شده و به روز رسانی قسمتهای تغییریافتهی صفحه را Ajax ای انجام دهند)؛ برای نمونه اینبار همانند تصویر زیر، از fetch استاندارد برای ارسال اطلاعات به سمت سرور کمک گرفته میشود (یعنی عملیات Ajax ای شده؛ بجای یک post-back معمولی):
اما ... این قابلیت به صورت پیشفرض در فرمهای تعاملی SSR غیرفعال است. چون همانطور که عنوان شد، اگر مقصد این فرم، یک آدرس غیربلیزری باشد، دوبار ارسال فرم صورت خواهد گرفت؛ یکبار با استفاده از fetch API و بار دیگر پس از شکست، به صورت معمولی. اما اگر مطمئن هستید که endpoint این فرم، قطعا یک کامپوننت بلیزری است، بهتر است این قابلیت را در یک چنین فرمهایی نیز به صورت زیر فعال کنید:
<form method="post" @onsubmit="() => submitted = true" @formname="name" data-enhance> <AntiforgeryToken /> <InputText @bind-Value="Name" /> <button>Submit</button> </form> @if (submitted) { <p>Hello @Name!</p> } @code { bool submitted; [SupplyParameterFromForm] public string Name { get; set; } = ""; }
<EditForm method="post" Model="NewCustomer" OnValidSubmit="() => submitted = true" FormName="customer" Enhance> <DataAnnotationsValidator /> <ValidationSummary/> <p> <label> Name: <InputText @bind-Value="NewCustomer.Name" /> </label> </p> <button>Submit</button> </EditForm> @if (submitted) { <p id="pass">Hello @NewCustomer.Name!</p> } @code { bool submitted = false; [SupplyParameterFromForm] public Customer? NewCustomer { get; set; } protected override void OnInitialized() { NewCustomer ??= new(); } public class Customer { [StringLength(3, ErrorMessage = "Name is too long")] public string? Name { get; set; } } }
نکتهی مهم: در این حالت فرض بر این است که هیچگونه هدایتی به یک Non-Blazor endpoint صورت نمیگیرید؛ وگرنه با یک خطا مواجه خواهید شد.
غیرفعال کردن راهبری بهبودیافته برای قسمتی از صفحه
اگر با استفاده از جاواسکریپت و خارج از کدهای بیلزر، اطلاعات DOM را بهروز رسانی میکنید، ویژگی راهبری بهبودیافته، از آن آگاهی نداشته و به صورت خودکار تمام تغییرات شما را بازنویسی میکند. به همین جهت اگر نیاز است قسمتی از صفحه را که مستقیما توسط کدهای جاواسکریپتی تغییر میدهید، از بهروز رسانیهای این قابلیت مصون نگهدارید، میتوانید ویژگی جدید data-permanent را به آن قسمت اضافه کنید:
<div data-permanent> Leave me alone! I've been modified dynamically. </div>
امکان آگاه شدن از بروز راهبری بهبودیافته در کدهای جاواسکریپتی
اگر به هردلیلی در کدهای جاواسکریپتی خودنیاز به آگاه شدن از وقوع یک هدایت بهبودیافته را دارید (برای مثال جهت بازنویسی تغییرات ایجاد شدهی توسط آن)، میتوانید به نحو زیر، مشترک رخدادهای آن شوید:
<script> Blazor.addEventListener('enhancedload', () => { console.log('enhanced load event occurred'); }); </script>
ویژگی جدید Named Element Routing در Blazor 8x
Blazor 8x از ویژگی مسیریابی سمت کلاینت به کمک تعریف URL fragments پشتیبانی میکند. به این صورت رسیدن (اسکرول) به یک قسمت از صفحهای طولانی، بسیار ساده میشود.
برای مثال المان h2 با id مساوی targetElement را درنظر بگیرید:
<div class="border border-info rounded bg-info" style="height:500px"></div> <h2 id="targetElement">Target H2 heading</h2> <p>Content!</p>
<a href="/counter#targetElement"> <NavLink href="/counter#targetElement"> Navigation.NavigateTo("/counter#targetElement");
معرفی متد جدید Refresh در Blazor 8x
در Blazor 8x، امکان بارگذاری مجدد صفحه با فراخوانی متد جدید NavigationManager.Refresh(bool forceLoad = false) میسر شدهاست. این متد در حالت پیشفرض از قابلیت راهبری بهبودیافته برای به روز رسانی صفحه استفاده میکند؛ مگر اینکه اینکار میسر نباشد. اگر آنرا با پارامتر true فراخوانی کنید، full-page reload رخ خواهد داد.
همین اتفاق در مورد متد Navigation.NavigateTo نیز رخدادهاست. این متد نیز در Blazor 8x به صورت پیشفرض بر اساس قابلیت راهبری بهبود یافته کار میکند؛ مگر اینکه اینکار میسر نباشد و یا پارامتر forceLoad آنرا به true مقدار دهی کنید.
نظرات مطالب
استفاده از Awesomium.NET در برنامههای وب
از cefSharp استفاده کنید که به روزتر است و مدام هم پشتیبانی میشود.
- بستهی نیوگت تهیه تصاویر از صفحات وب آن
- یک مثال نحوهی کاربرد این بستهی نیوگت
- بستهی نیوگت تهیه تصاویر از صفحات وب آن
- یک مثال نحوهی کاربرد این بستهی نیوگت