Meet the new version of the leading eCommerce solution. Today we are ending one more six month cycle with the release of nopCommerce 3.70. We made a big step to the ideal eCommerce platform with the latest version of nopCommerce. This time we pursued general scalability by close integration with world-famous Azure and implemented some changes desired by store-owners and required by the newest SEO trends.
اشتراکها
React به صورت پیشفرض از ES6 برای توسعهی برنامههای خودش استفاده میکند؛ اما استفادهی از TypeScript با پروژههای React، مزایای قابل توجهی را مانند type checking در زمان کامپایل برنامه، دسترسی به intellisense آنی، امکان refactoring بهتر را در اختیار توسعه دهنده قرار میدهد که نه فقط سرعت و سهولت توسعه را افزایش میدهند، بلکه از بروز بسیاری از خطاهای زمان اجرای برنامه نیز جلوگیری میکند.
ایجاد پروژههای React مبتنی بر TypeScript
برای ایجاد ساختار ابتدایی پروژههای React که جهت استفادهی از TypeScript تنظیم شدهاند، دستور زیر را در خط فرمان اجرا میکنیم:
مزیت کار با npx، عدم نیاز به نصب محلی برنامهی create-react-app است. به این ترتیب هربار که این دستور را به این نحو اجرا میکنیم، مطمئن خواهیم بود که از آخرین نگارش برنامهی create-react-app استفاده خواهد شد؛ و نه نگارش محلی که پیشتر نصب کردهایم که ممکن است هم اکنون تاریخ مصرف گذشته باشد.
بنابراین npx create-react-app کار اجرای آخرین نسخهی برنامهی ایجاد ساختار پروژههای React را انجام میدهد. پس از آن یک نام دلخواه ذکر شدهاست و در آخر توسط سوئیچ template typescript-- سبب خواهیم شد تا این ساختار بجای استفادهی از ES6 پیشفرض، بر اساس TypeScript ایجاد و تنظیم شود.
بررسی ساختار پروژهی TypeScript ای ایجاد شده
در تصویر فوق، نمونهای از این ساختار ابتدایی ایجاد شدهی مبتنی بر TypeScript را مشاهده میکنید. اولین تفاوت مهم این ساختار، با ساختار پیشفرض پروژههای React مبتنی بر ES6، وجود فایل جدید tsconfig.json است. کار آن تنظیم پارامترهای کامپایلر TypeScript است. همچنین اینبار بجای پسوندهای js و jsx، پسوندهای ts و tsx قابل مشاهده هستند؛ مانند فایلهای serviceWorker.ts ، index.tsx و App.tsx. البته اگر به ساختار این فایلها دقت کنید، آنچنان تفاوت مهمی را با نمونههای قبلی ES6 خود ندارند و تقریبا یکی هستند. روش اجرای آنها نیز مانند قبل است و با همان دستور npm start صورت میگیرد:
قابلیت استفادهی از کدهای جاوا اسکریپتی موجود، در پروژههای تایپ اسکریپتی جدید
داخل پوشهی src، پوشهی جدید components را ایجاد کرده و داخل آن فایل جدید Head.js را اضافه میکنیم. سپس داخل آن rfac را نوشته و دکمهی tab را فشار میدهیم تا ساختار ابتدایی یک react arrow functional component جدید ایجاد شود:
در ادامه جهت نمایش آن، آنرا به فایل src\App.tsx به شکل متداولی، ابتدا با import تابع آن و سپس درج المان متناظر با آن در تابع App، اضافه میکنیم:
اگر دقت کرده باشید، پسوند این فایل را js درنظر گرفتهایم (src\components\Head.js) و نه ts و بدون مشکل میتوان از آن در داخل یک فایل tsx استفاده کرد. علت آن به وجود تنظیم allowJs در فایل tsconfig.json برنامه بر میگردد. مزیت وجود یک چنین تنظیمی، امکان مهاجرت سادهتر کدهای ES6 موجود، به یک پروژهی تایپ اسکریپتی جدید است. به این ترتیب میتوان از تمام این کدها بدون مشکل در برنامهی جدید خود استفاده کرد و سر فرصت، یکی یکی آنها را به tsx تبدیل نمود.
به همین جهت پس از مشاهدهی این قابلیت، پسوند فایل کامپوننت جدید js ایجاد شده را به tsx تغییر میدهیم (src\components\Head.tsx). البته در یک چنین حالتی اگر هنوز دستور npm start در حال اجرا است، نیاز خواهید داشت یکبار آنرا بسته و مجددا اجرا کنید. پس از آن، باز هم برنامه بدون مشکل کامپایل میشود و نشان دهندهی این است که کدهای نوشته شدهی در کامپوننت Head، کدهای کاملا معتبر تایپ اسکریپتی نیز هستند. علت اینجا است که TypeScript، در حقیقت Superset جاوا اسکریپت بهشمار میرود و قابلیتهای جدیدی را به TypeScript استفاده میکند. بنابراین کدهای جاوااسکریپتی موجود، کدهای معتبر تایپ اسکریپتی نیز بهشمار میروند.
مشخص کردن نوع props کامپوننتها توسط TypeScript
اولین استفادهی ما از TypeScript در اینجا، مشخص کردن نوع props یک کامپوننت است:
برای این منظور، دو خاصیت جدید را از طریق شیء props به این کامپوننت ارسال کرده و از آنها جهت نمایش یک عنوان و تعیین نمایش یک برچسب استفاده میکنیم. اولین موردی را که پس از این تغییر متداول مشاهده میکنیم، خط قرمز کشیده شدن زیر متغیرهای حاصل از Object Destructuring مربوط به شیء props است:
علت اینجا است که این فایل، tsx است و نه js. به همین جهت نوع این متغیرها را، همان حالت پیشفرض جاوا اسکریپت که any است، درنظر گرفتهاست و ... این مورد بر اساس تنظیمات فایل tsconfig.json برنامه، ممنوع است. البته اگر به این فایل دقت کنید، شاید چنین گزینهای را به صورت صریح نتوانید در آن پیدا کنید. علت اینجا است که تعداد گزینههای قابل تنظیم در فایل tsconfig روز به روز بیشتر میشوند. به همین جهت برای ساده سازی فعالسازی آنها، از TypeScript 2.3 به بعد، پرچم strict نیز به این تنظیمات اضافه شدهاست. کار آن فعالسازی یکجای تمام بررسیهای strict است؛ مانند noImplicitAny، strictNullChecks و غیره.
در این حالت اگر نیاز به لغو یکی از گزینهها بود، میتوان به صورت ذیل عمل کرد:
گزینهی strict تمام بررسیهای متداول را فعال میکند؛ اما ذکر و تنظیم صریح noImplicitAny به false، تنها این یک مورد را لغو خواهد کرد.
بنابراین چون در فایل tsconfig.json برنامهی React ما گزینهی strict به true تنظیم شدهاست، گزینهی فعال noImplicitAny نیز جزئی از آن است و دیگر نمیتوان متغیر یا خاصیتی را بدون ذکر صریح نوع آن، رها کرد.
برای رفع خطای noImplicitAny موجود، به ابتدای فایل src\components\Head.tsx، نوع جدید Props را اضافه میکنیم (نام آن کاملا دلخواه است):
و سپس از آن جهت مشخص سازی نوع شیء props رسیده، به نحو زیر استفاده خواهیم کرد:
پس از این تغییر، خطای noImplicitAny پیشین، برطرف میشود و دیگر خطوط قرمز ذیل دو متغیر حاصل از Object Destructuring، مشاهده نمیشوند.
یک نکته: البته اگر از سری React 16x بخاطر داشته باشید، میتوان یک چنین قابلیتی را توسط propTypes خود React نیز پیاده سازی کرد:
اما روش کار با TypeScript، نسبت به آن بسیار پیشرفتهتر است. برای کار با TypeScript، نیازی به import یک بستهی جدید، مانند PropTypes نیست و همچنین بررسی PropTypes توسط خود React، در زمان اجرای برنامه صورت میگیرد؛ اما با TypeScript، بررسی زمان کامپایل برنامه را خواهیم داشت و همچنین نمایش آنی خطاهای مرتبط با عدم رعایت آنها، در ادیتورهایی مانند VSCode. به علاوه روش تعریف type ذکر شدهی توسط TypeScript، نسبت به نمونهی پیشنهاد شدهی توسط React با propTypes، بسیار تمیزتر و خواناتر است.
پس از این تغییر، اگر به فایل src\App.tsx مراجعه کنیم، مشاهده میکنیم که ذیل تعریف المان کامپوننت Head، مجددا خط قرمزی کشیده شدهاست:
عنوان میکند که بر اساس نوع جدید Props ای که تعریف کردهاید، نیاز است دو خاصیت اجباری title و isActive را نیز در اینجا ذکر کنید؛ وگرنه تعریف این المان، بدون آنها ناقص است.
امکان جالب دیگری که با تعریف نوع props توسط تایپاسکریپت رخ میدهد، فعال شدن intellisense متناظر با تعریف این خواص و ویژگیها است:
در ادامه با تعریف این دو ویژگی جدید، خط قرمز رنگ ذیل کامپوننت Head برطرف میشود:
و اگر در حین تعریف این ویژگیها، نوعهای مقادیر آنها را به درستی وارد نکنیم، بازهم شاهد تذکر آنی خطاهای مرتبط با آنها خواهیم بود:
همچنین در این حالت، کد برنامه نیز کامپایل نمیشود و ذکر این خطاها صرفا منحصر به ادیتور مورد استفاده نیست.
بنابراین به صورت خلاصه مزیتهای کار با TypeScript برای تعاریف نوع props به شرح زیر است:
- auto-complete و داشتن intellisense خودکار.
- اگر نام المان کامپوننتی و یا نام یکی از props را به اشتباه وارد کنیم، بلافاصله خطای یافت نشدن آنها را نمایش میدهد.
- اگر ذکر یک prop اجباری را فراموش کنیم، بلافاصله خطای متناظری را دریافت میکنیم.
- اگر نوع مقدار یکی از props را به اشتباه وارد کنیم، باز هم خطایی را جهت گوشزد کردن آن مشاهده خواهیم کرد.
- فعال بودن TypeScript، امکان refactoring بسیار قویتری را میسر میکند. برای مثال با فشردن دکمهی F2 میتوان نام یک کامپوننت را در کل برنامه به سادگی تغییر داد. همچنین یک چنین قابلیتی برای تغییر نام props نیز میسر است و به صورت خودکار تمام کاربردهای آنرا نیز به روز میکند.
- اگر نوع prop ای را در تعریف آن تغییر دادیم، اما مقدار منتسب به آنرا خیر، باز هم بلافاصله متوجه این مشکل خواهیم شد.
به این ترتیب با دسترسی به بررسیهای دقیق زمان کامپایل برنامه، میتوان مشکلات بسیار کمتری را در زمان اجرای آن شاهد بود.
ایجاد پروژههای React مبتنی بر TypeScript
برای ایجاد ساختار ابتدایی پروژههای React که جهت استفادهی از TypeScript تنظیم شدهاند، دستور زیر را در خط فرمان اجرا میکنیم:
npx create-react-app tssample --template typescript
بنابراین npx create-react-app کار اجرای آخرین نسخهی برنامهی ایجاد ساختار پروژههای React را انجام میدهد. پس از آن یک نام دلخواه ذکر شدهاست و در آخر توسط سوئیچ template typescript-- سبب خواهیم شد تا این ساختار بجای استفادهی از ES6 پیشفرض، بر اساس TypeScript ایجاد و تنظیم شود.
بررسی ساختار پروژهی TypeScript ای ایجاد شده
در تصویر فوق، نمونهای از این ساختار ابتدایی ایجاد شدهی مبتنی بر TypeScript را مشاهده میکنید. اولین تفاوت مهم این ساختار، با ساختار پیشفرض پروژههای React مبتنی بر ES6، وجود فایل جدید tsconfig.json است. کار آن تنظیم پارامترهای کامپایلر TypeScript است. همچنین اینبار بجای پسوندهای js و jsx، پسوندهای ts و tsx قابل مشاهده هستند؛ مانند فایلهای serviceWorker.ts ، index.tsx و App.tsx. البته اگر به ساختار این فایلها دقت کنید، آنچنان تفاوت مهمی را با نمونههای قبلی ES6 خود ندارند و تقریبا یکی هستند. روش اجرای آنها نیز مانند قبل است و با همان دستور npm start صورت میگیرد:
قابلیت استفادهی از کدهای جاوا اسکریپتی موجود، در پروژههای تایپ اسکریپتی جدید
داخل پوشهی src، پوشهی جدید components را ایجاد کرده و داخل آن فایل جدید Head.js را اضافه میکنیم. سپس داخل آن rfac را نوشته و دکمهی tab را فشار میدهیم تا ساختار ابتدایی یک react arrow functional component جدید ایجاد شود:
import React from "react"; export const Head = () => { return ( <div> <h1>Hello</h1> </div> ); };
import { Head } from "./components/Head"; // ... function App() { return ( <div className="App"> <Head /> // ... </div> ); } export default App;
به همین جهت پس از مشاهدهی این قابلیت، پسوند فایل کامپوننت جدید js ایجاد شده را به tsx تغییر میدهیم (src\components\Head.tsx). البته در یک چنین حالتی اگر هنوز دستور npm start در حال اجرا است، نیاز خواهید داشت یکبار آنرا بسته و مجددا اجرا کنید. پس از آن، باز هم برنامه بدون مشکل کامپایل میشود و نشان دهندهی این است که کدهای نوشته شدهی در کامپوننت Head، کدهای کاملا معتبر تایپ اسکریپتی نیز هستند. علت اینجا است که TypeScript، در حقیقت Superset جاوا اسکریپت بهشمار میرود و قابلیتهای جدیدی را به TypeScript استفاده میکند. بنابراین کدهای جاوااسکریپتی موجود، کدهای معتبر تایپ اسکریپتی نیز بهشمار میروند.
مشخص کردن نوع props کامپوننتها توسط TypeScript
اولین استفادهی ما از TypeScript در اینجا، مشخص کردن نوع props یک کامپوننت است:
import React from "react"; export const Head = ({ title, isActive }) => { return ( <div> <h1>{title}</h1> {isActive && <h3>Active</h3>} </div> ); };
علت اینجا است که این فایل، tsx است و نه js. به همین جهت نوع این متغیرها را، همان حالت پیشفرض جاوا اسکریپت که any است، درنظر گرفتهاست و ... این مورد بر اساس تنظیمات فایل tsconfig.json برنامه، ممنوع است. البته اگر به این فایل دقت کنید، شاید چنین گزینهای را به صورت صریح نتوانید در آن پیدا کنید. علت اینجا است که تعداد گزینههای قابل تنظیم در فایل tsconfig روز به روز بیشتر میشوند. به همین جهت برای ساده سازی فعالسازی آنها، از TypeScript 2.3 به بعد، پرچم strict نیز به این تنظیمات اضافه شدهاست. کار آن فعالسازی یکجای تمام بررسیهای strict است؛ مانند noImplicitAny، strictNullChecks و غیره.
{ "compilerOptions": { "strict": true /* Enable all strict type-checking options. */ } }
{ "compilerOptions": { "strict": true, "noImplicitAny": false } }
بنابراین چون در فایل tsconfig.json برنامهی React ما گزینهی strict به true تنظیم شدهاست، گزینهی فعال noImplicitAny نیز جزئی از آن است و دیگر نمیتوان متغیر یا خاصیتی را بدون ذکر صریح نوع آن، رها کرد.
برای رفع خطای noImplicitAny موجود، به ابتدای فایل src\components\Head.tsx، نوع جدید Props را اضافه میکنیم (نام آن کاملا دلخواه است):
type Props = { title: string; isActive: boolean; };
export const Head = ({ title, isActive }: Props) => {
یک نکته: البته اگر از سری React 16x بخاطر داشته باشید، میتوان یک چنین قابلیتی را توسط propTypes خود React نیز پیاده سازی کرد:
Head.propTypes = { title: PropTypes.string, isActive: PropTypes.bool }
پس از این تغییر، اگر به فایل src\App.tsx مراجعه کنیم، مشاهده میکنیم که ذیل تعریف المان کامپوننت Head، مجددا خط قرمزی کشیده شدهاست:
عنوان میکند که بر اساس نوع جدید Props ای که تعریف کردهاید، نیاز است دو خاصیت اجباری title و isActive را نیز در اینجا ذکر کنید؛ وگرنه تعریف این المان، بدون آنها ناقص است.
امکان جالب دیگری که با تعریف نوع props توسط تایپاسکریپت رخ میدهد، فعال شدن intellisense متناظر با تعریف این خواص و ویژگیها است:
در ادامه با تعریف این دو ویژگی جدید، خط قرمز رنگ ذیل کامپوننت Head برطرف میشود:
<Head title="Hello" isActive={true} />
همچنین در این حالت، کد برنامه نیز کامپایل نمیشود و ذکر این خطاها صرفا منحصر به ادیتور مورد استفاده نیست.
بنابراین به صورت خلاصه مزیتهای کار با TypeScript برای تعاریف نوع props به شرح زیر است:
- auto-complete و داشتن intellisense خودکار.
- اگر نام المان کامپوننتی و یا نام یکی از props را به اشتباه وارد کنیم، بلافاصله خطای یافت نشدن آنها را نمایش میدهد.
- اگر ذکر یک prop اجباری را فراموش کنیم، بلافاصله خطای متناظری را دریافت میکنیم.
- اگر نوع مقدار یکی از props را به اشتباه وارد کنیم، باز هم خطایی را جهت گوشزد کردن آن مشاهده خواهیم کرد.
- فعال بودن TypeScript، امکان refactoring بسیار قویتری را میسر میکند. برای مثال با فشردن دکمهی F2 میتوان نام یک کامپوننت را در کل برنامه به سادگی تغییر داد. همچنین یک چنین قابلیتی برای تغییر نام props نیز میسر است و به صورت خودکار تمام کاربردهای آنرا نیز به روز میکند.
- اگر نوع prop ای را در تعریف آن تغییر دادیم، اما مقدار منتسب به آنرا خیر، باز هم بلافاصله متوجه این مشکل خواهیم شد.
به این ترتیب با دسترسی به بررسیهای دقیق زمان کامپایل برنامه، میتوان مشکلات بسیار کمتری را در زمان اجرای آن شاهد بود.
اشتراکها
WebAssembly کردن NET. با Blazor
اشتراکها
WPF و IOC در NET Core 3.0.
At work, we are planning to migrate our WPF application from .NET Framework 4.7 to .NET Core 3.0. The main reason for doing so is that it was always a big pain to organize the updates of the .NET Framework on our customer machines. So being able to bundle .NET Core with our application is a big plus for us. Then, for sure, we are looking for the performance improvements brought by .NET Core and finally the new capabilities brought by the fast pace of innovation of .NET Core.
public class HandleConcurrencyExceptionAttribute : FilterAttribute, IExceptionFilter { private PropertyMatchingMode _propertyMatchingMode; /// <summary> /// This defines when the concurrencyexception happens, /// </summary> public enum PropertyMatchingMode { /// <summary> /// Uses only the field names in the model to check against the entity. This option is best when you are using /// View Models with limited fields as opposed to an entity that has many fields. The ViewModel (or model) field names will /// be used to check current posted values vs. db values on the entity itself. /// </summary> UseViewModelNamesToCheckEntity = 0, /// <summary> /// Use any non-matching value fields on the entity (except timestamp fields) to add errors to the ModelState. /// </summary> UseEntityFieldsOnly = 1, /// <summary> /// Tells the filter to not attempt to add field differences to the model state. /// This means the end user will not see the specifics of which fields caused issues /// </summary> DontDisplayFieldClashes = 2 } public HandleConcurrencyExceptionAttribute() { _propertyMatchingMode = PropertyMatchingMode.UseViewModelNamesToCheckEntity; } public HandleConcurrencyExceptionAttribute(PropertyMatchingMode propertyMatchingMode) { _propertyMatchingMode = propertyMatchingMode; } /// <summary> /// The main method, called by the mvc runtime when an exception has occured. /// This must be added as a global filter, or as an attribute on a class or action method. /// </summary> /// <param name="filterContext"></param> public void OnException(ExceptionContext filterContext) { if (!filterContext.ExceptionHandled && filterContext.Exception is DbUpdateConcurrencyException) { //Get original and current entity values DbUpdateConcurrencyException ex = (DbUpdateConcurrencyException)filterContext.Exception; var entry = ex.Entries.Single(); //problems with ef4.1/4.2 here because of context/model in different projects. //var databaseValues = entry.CurrentValues.Clone().ToObject(); //var clientValues = entry.Entity; //So - if using EF 4.1/4.2 you may use this workaround var clientValues = entry.CurrentValues.Clone().ToObject(); entry.Reload(); var databaseValues = entry.CurrentValues.ToObject(); List<string> propertyNames; filterContext.Controller.ViewData.ModelState.AddModelError(string.Empty, "The record you attempted to edit " + "was modified by another user after you got the original value. The " + "edit operation was canceled and the current values in the database " + "have been displayed. If you still want to edit this record, click " + "the Save button again to cause your changes to be the current saved values."); PropertyInfo[] entityFromDbProperties = databaseValues.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance); if (_propertyMatchingMode == PropertyMatchingMode.UseViewModelNamesToCheckEntity) { //We dont have access to the model here on an exception. Get the field names from modelstate: propertyNames = filterContext.Controller.ViewData.ModelState.Keys.ToList(); } else if (_propertyMatchingMode == PropertyMatchingMode.UseEntityFieldsOnly) { propertyNames = databaseValues.GetType().GetProperties(BindingFlags.Public).Select(o => o.Name).ToList(); } else { filterContext.ExceptionHandled = true; UpdateTimestampField(filterContext, entityFromDbProperties, databaseValues); filterContext.Result = new ViewResult() { ViewData = filterContext.Controller.ViewData }; return; } UpdateTimestampField(filterContext, entityFromDbProperties, databaseValues); //Get all public properties of the entity that have names matching those in our modelstate. foreach (var propertyInfo in entityFromDbProperties) { //If this value is not in the ModelState values, don't compare it as we don't want //to attempt to emit model errors for fields that don't exist. //Compare db value to the current value from the entity we posted. if (propertyNames.Contains(propertyInfo.Name)) { if (propertyInfo.GetValue(databaseValues, null) != propertyInfo.GetValue(clientValues, null)) { var currentValue = propertyInfo.GetValue(databaseValues, null); if (currentValue == null || string.IsNullOrEmpty(currentValue.ToString())) { currentValue = "Empty"; } filterContext.Controller.ViewData.ModelState.AddModelError(propertyInfo.Name, "Current value: " + currentValue); } } //TODO: hmm.... how can we only check values applicable to the model/modelstate rather than the entity we saved? //The problem here is we may only have a few fields used in the viewmodel, but many in the entity //so we could have a problem here with that. //object o = propertyInfo.GetValue(myObject, null); } filterContext.ExceptionHandled = true; filterContext.Result = new ViewResult() { ViewData = filterContext.Controller.ViewData }; } }
نظرات مطالب
ASP.NET MVC #1
با تشکر از مطلب جالب آقای نصیری.
در مورد کتاب ها سایت رسمی asp بخش جالب http://www.asp.net/mvc/books رو داره که من کتاب
Professional ASP.NET MVC 3 (Wrox Programmer to Programmer) رو انتخاب کردم و ازشم راضیم .
در مورد مدارک هم می دونم که فعلا فقط توی امتحان Exam 70-515 برای مدرک TS: Web Applications Development with Microsoft .NET Framework 4 تنها 20 درصد از امتحان رو بحث mvc تشکیل میده . ولی اولا که این مدارک 2 سال پیش تنظیم شده و ثانیا که مدرک گرفتن آدم رو برنامه نویس خوب نمیکنه .
در مورد کتاب ها سایت رسمی asp بخش جالب http://www.asp.net/mvc/books رو داره که من کتاب
Professional ASP.NET MVC 3 (Wrox Programmer to Programmer) رو انتخاب کردم و ازشم راضیم .
در مورد مدارک هم می دونم که فعلا فقط توی امتحان Exam 70-515 برای مدرک TS: Web Applications Development with Microsoft .NET Framework 4 تنها 20 درصد از امتحان رو بحث mvc تشکیل میده . ولی اولا که این مدارک 2 سال پیش تنظیم شده و ثانیا که مدرک گرفتن آدم رو برنامه نویس خوب نمیکنه .
اشتراکها