<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dmitry Kulagin</title>
    <description>The latest articles on DEV Community by Dmitry Kulagin (@dmitrykulagin2).</description>
    <link>https://dev.to/dmitrykulagin2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F269516%2F73d9624a-0714-4868-b569-621e370531fd.jpg</url>
      <title>DEV Community: Dmitry Kulagin</title>
      <link>https://dev.to/dmitrykulagin2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dmitrykulagin2"/>
    <language>en</language>
    <item>
      <title>How to Create a Mobile App for eCommerce from Scratch?</title>
      <dc:creator>Dmitry Kulagin</dc:creator>
      <pubDate>Mon, 07 Aug 2023 08:48:20 +0000</pubDate>
      <link>https://dev.to/dmitrykulagin2/how-to-create-a-mobile-app-for-ecommerce-from-scratch-34g</link>
      <guid>https://dev.to/dmitrykulagin2/how-to-create-a-mobile-app-for-ecommerce-from-scratch-34g</guid>
      <description>&lt;p&gt;In the highly competitive world of eCommerce, having an exceptional mobile app is crucial for achieving success. With industry behemoths such as Amazon and Alibaba reigning supreme in the online retail arena, new enterprises must discover their unique advantage. But the challenge lies in creating an eCommerce mobile app that not only stands out but is also budget-friendly.&lt;/p&gt;

&lt;p&gt;This article will take you through the journey of crafting a stylish and intuitive mobile eCommerce app. The core nopCommerce team shares their insights by delving into essential aspects like key functionalities, technology stack, design, and architecture, using their own mobile app development as a real-life example.&lt;/p&gt;

&lt;h2&gt;
  
  
  7 steps to create an eCommerce app for your existing website
&lt;/h2&gt;

&lt;p&gt;The primary objective is to break down mobile app development into easily comprehensible steps. This guide should answer the question “how to create a mobile app for eCommerce?”, considering time and money expenses. &lt;/p&gt;

&lt;p&gt;It aims to be helpful for developers seeking to replicate the process or gain insider perspectives on the nopCommerce development experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Laying the foundation with research
&lt;/h3&gt;

&lt;p&gt;Before embarking on the exciting journey of developing your mobile eCommerce app, it is essential to establish a solid foundation through competitor analysis and market research. This step will analyze successful industry strategies and potential opportunities for your app to flourish.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Analyzing the competition&lt;/strong&gt;: Start with identifying both direct and indirect competitors in the mobile eCommerce space. Thoroughly examine their app features, user experience, pricing strategies, marketing tactics, and customer feedback. This analysis will highlight their strengths and weaknesses, paving the way for your app to showcase its distinctive advantages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Researching market trends&lt;/strong&gt;: Stay abreast of ever-changing consumer preferences, emerging technologies, and the latest essential features expected from an eCommerce app. Armed with this knowledge, you will be well-prepared to align your app with evolving user needs and expectations, positioning yourself ahead of the competition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understanding your target audience&lt;/strong&gt;: Gain a deep understanding of your users' demographics, preferences, and pain points. This information will empower you to customize your app to cater to their specific needs, delivering a personalized and delightful user experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Establishing measurable goals&lt;/strong&gt;: Consider objectives such as increasing sales, enhancing customer engagement, boosting conversion rates, or expanding your customer base. These goals will provide a tangible framework to evaluate the app's performance and achievements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prioritizing user-centric features&lt;/strong&gt;: Focus on core functionalities that add value to users and align with your unique selling proposition. Key features, including a user-friendly interface, seamless navigation, secure payment options, efficient product search capabilities, and personalized recommendations, should take precedence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future-proofing your app&lt;/strong&gt;: Plan for scalability and growth by ensuring that your app's architecture and infrastructure can handle increasing user traffic, seamlessly incorporate new features, and integrate with third-party systems like payment gateways and inventory management.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you embark on this journey, keep in mind that adopting a user-friendly and professional approach during the development process will pave the way for a successful and captivating &lt;a href="https://www.nopcommerce.com/en/ecommerce-mobile-app"&gt;mobile eCommerce app&lt;/a&gt;. Such an app is poised to leave a lasting impression on your target audience, ensuring a rewarding and memorable user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Selecting the proper technology stack
&lt;/h3&gt;

&lt;p&gt;To create an eCommerce app, one of the most crucial decisions for a developer's team is selecting the appropriate platform/framework as the foundation. While technical factors play a significant role, it is equally essential to take into account the project's distinct business requirements.&lt;/p&gt;

&lt;p&gt;The primary consideration revolves around choosing between native app development and a cross-platform approach.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Native application&lt;/strong&gt;: Developing separate native apps for Android and iOS requires dedicated teams for each platform, leading to extended development time and subsequent updates. This approach is best suited for large-scale enterprises with sufficient resources to support two teams working on Swift (iOS) and Kotlin (Android) development. Despite higher costs, the end result is an application well-optimized and tailor-made for each platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform approach&lt;/strong&gt;: Conversely, the cross-platform approach employs a single codebase that functions on both Android and iOS devices. This option is more cost-effective and time-efficient, potentially saving up to 50% of a company's resources. While cross-platform apps may experience slight delays in adopting new features compared to native apps, most eCommerce functionalities can be covered using third-party libraries and plugins. This approach enables a quicker time to market without compromising on functionality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you opt for the cross-platform route, several frameworks like React Native, Ionic, and Flutter are worth considering. For the nopCommerce mobile app, our team engaged in comprehensive discussions before ultimately choosing Flutter.&lt;/p&gt;

&lt;p&gt;Here are some compelling reasons for selecting Flutter, which can help you lean towards this framework if you are still grappling with the decision:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a. Easy learning curve and technical maturity&lt;/strong&gt;: Flutter provides a user-friendly learning curve and has reached a high level of technical maturity, enabling developers to quickly master the framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;b. Seamless integration with code editors&lt;/strong&gt;: Flutter seamlessly integrates with popular code editors like VSCode, widely used among .NET developers. This integration simplifies coding, debugging, viewing the widget tree, and analyzing memory leaks, greatly enhancing the development experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;c. Instant hot reload feature&lt;/strong&gt;: Flutter's hot reload feature allows developers to instantly view changes made to the code within the app. This significantly speeds up the development process compared to native app development, where the build process can be time-consuming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;d. Exceptional performance&lt;/strong&gt;: Flutter boasts lightning-fast performance, delivering 60 FPS. This empowers developers to create mobile applications that rival native ones in terms of performance, even when using Swift and Kotlin.&lt;/p&gt;

&lt;p&gt;Through a careful evaluation of business requirements, time to market, and development resources, the nopCommerce team opted for Flutter. This decision enabled us to create a flexible and fully-functional product in a shorter timeframe while enjoying the benefits of a well-rounded development environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Building a stable and scalable architecture
&lt;/h3&gt;

&lt;p&gt;When embarking on the journey of creating an eCommerce app from scratch, the right architecture becomes paramount. The chosen architecture not only influences the initial development process but also plays a significant role in the project's scalability, code structure, and the coordination within the team.&lt;/p&gt;

&lt;p&gt;To ensure a maintainable and scalable codebase, we initially followed generally accepted standards as outlined in the &lt;a href="https://developer.android.com/topic/architecture"&gt;Android documentation&lt;/a&gt;. However, when dealing with Flutter, we faced a challenge as the standard architecture did not fully account for its unique characteristics.&lt;/p&gt;

&lt;p&gt;As a result, we made necessary modifications and introduced an additional level, achieving a well-organized and efficient architecture tailored to our app's specific needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T5OIkGpC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uipbea8e08hw4qlduqbs.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T5OIkGpC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uipbea8e08hw4qlduqbs.jpg" alt="Image1" width="489" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UI Layer&lt;/strong&gt;: At this level, the focus lies on managing the user interface components and their interactions. It encompasses widgets and logic directly related to the app's user interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain Layer&lt;/strong&gt;: The domain layer is dedicated to housing the application's essential business logic, effectively isolating it from the UI layer. It comprises controllers and business logic objects responsible for handling core functionalities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Layer&lt;/strong&gt;: Within the data layer, the primary emphasis is on data access and management. This involves repositories and data sources responsible for handling data retrieval and storage operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ecCFGsQs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/330vanzc9vax61jymbl6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ecCFGsQs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/330vanzc9vax61jymbl6.jpg" alt="Image2" width="611" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To customize the architecture for Flutter, we introduced the Riverpod library for state management and dependency injection, resulting in a modified structure as outlined below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UI Layer&lt;/strong&gt;: This level remains unchanged and encompasses UI components along with their interactions with the user. It relies on widgets and UI-related logic to manage the user interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Layer&lt;/strong&gt;: In our revised architecture, the Application layer takes on a role similar to that of the original Domain layer in the Android architecture. It houses service classes responsible for defining methods used by controllers. This becomes especially valuable when multiple widgets manage the same logic or when logic depends on multiple repositories. However, it's important to note that the Application layer might not always be necessary. In some cases, controllers can directly request data from repositories without the intermediary Application layer. If the services merely redirect method calls from controllers to the repository, the Application layer might not be required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain Layer&lt;/strong&gt;: In the updated architecture, this level corresponds to domain models that can be modified by services at the Application layer and filled with data from repositories at the Data layer. These models are applied to the business logic objects accessed through repositories.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By reorganizing the architecture to suit Flutter's unique requirements and integrating Riverpod, we achieved a structure that not only promotes code maintainability and scalability but also enhances team collaboration. The domain-specific modifications enabled a seamless development process while ensuring that the app remains adaptable to future expansions and enhancements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Designing a user-friendly interface
&lt;/h3&gt;

&lt;p&gt;One of the key factors that led us to choose Flutter for the nopCommerce mobile app was its extensive widget library, providing a significant advantage. Leveraging this powerful library made the creation of a user-friendly interface exceptionally efficient. Flutter's unique rendering engine, Impeller, ensures consistent visual appeal across all devices, further enhancing the user experience.&lt;/p&gt;

&lt;p&gt;For the design of the nopCommerce app, we based our approach on Material 3 (also known as Material You), Google's latest design system version. Material 3 introduced refreshed component renderings, captivating animations, and fresh color palettes that seamlessly integrate with Flutter.&lt;/p&gt;

&lt;p&gt;To maintain a cohesive and consistent appearance across devices, we harnessed the potential of design tokens offered by Material 3. These tokens allowed us to store styles, fonts, and animation values, ensuring the same style values were used in both design files and code.&lt;/p&gt;

&lt;p&gt;Our primary goal was to create an eCommerce app with a unified visual identity across various devices, delivering a seamless and immersive experience to users. &lt;a href="https://m3.material.io/foundations/design-tokens/overview"&gt;Design tokens&lt;/a&gt; played a crucial role in achieving this objective.&lt;/p&gt;

&lt;p&gt;To generate design tokens, we utilized the &lt;a href="https://www.figma.com/community/plugin/1034969338659738588/Material-Theme-Builder"&gt;Material Theme Builder&lt;/a&gt;, which automatically created a theme with default colors and styles. Building upon this foundation, we crafted a custom theme tailored to the nopCommerce app's branding and specific requirements.&lt;/p&gt;

&lt;p&gt;When determining brand colors, we had two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manual Specification&lt;/strong&gt;: The first option involved manually specifying base colors to align with the brand's identity and desired visual aesthetics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Colors&lt;/strong&gt;: Alternatively, we experimented with dynamic colors, wherein the app selects a color scheme based on provided images, such as the company's logo. This dynamic approach added a touch of uniqueness to the app's visuals, adapting to the brand's distinct characteristics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_C8dOOhF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kaphn07yf5258131cee3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_C8dOOhF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kaphn07yf5258131cee3.jpg" alt="Image3" width="614" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Embracing the concept of Material You, the implementation of design tokens empowered us to generate a comprehensive palette of styles, ensuring consistent visual coherence across the entire app. Additionally, these design tokens offered the flexibility for personalized tweaks and adjustments, allowing to maintain a unique and tailored app design.&lt;/p&gt;

&lt;p&gt;A noteworthy advantage of design tokens lies in their inherent flexibility and versatility. This allowed the nopCommerce app to seamlessly adapt to various application modes, such as Dark mode and Light mode, without sacrificing style or compromising the user experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PuMDDQqZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hu5fnl3dp5gave9of38a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PuMDDQqZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hu5fnl3dp5gave9of38a.jpg" alt="Image4" width="611" height="690"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The utilization of design tokens offers several key benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Streamlined Design and Development&lt;/strong&gt;: Design tokens eliminate the need to delve into platform-specific design guides. By applying Material 3 to both design and development, time and effort are saved, resulting in a more efficient workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quick Redesign Capabilities&lt;/strong&gt;: If there arises a need to revamp the app's design, design tokens facilitate a remarkably efficient process, allowing for swift updates and changes to be made seamlessly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy Style Maintenance&lt;/strong&gt;: Design tokens simplify the maintenance of styles, making it hassle-free to update and manage the app's visual elements, ensuring consistency and coherence.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With these strategies in place, the creation of a visually appealing eCommerce app on any device became a seamless and rewarding process. Our choice of Flutter and Material 3, coupled with the implementation of design tokens, ensured a visually cohesive and delightful user interface for the nopCommerce mobile app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Ensuring high-level security
&lt;/h3&gt;

&lt;p&gt;In the nopCommerce mobile app, ensuring the security of transmitted data is of utmost importance, achieved through the implementation of Bearer tokens. These tokens are exclusively issued to authorized users and play a vital role in establishing secure communication between the client (mobile application) and the server.&lt;/p&gt;

&lt;p&gt;Bearer tokens serve as a robust mechanism for both authentication and authorization, granting clients access to protected resources on the server side. In the event of a security concern, the server-side token can be revoked, prompting the client to go through the authorization procedure once again to obtain a new token. This stringent approach guarantees that only authenticated users have access to sensitive data, bolstering the overall security of the application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--510awr3x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lvx6gv1b3fspr4xdkg2n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--510awr3x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lvx6gv1b3fspr4xdkg2n.jpg" alt="Image5" width="609" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Equally crucial is the secure storage of tokens on the client side. Since tokens do not require explicit proof of ownership from the bearer, it becomes imperative to safeguard this information effectively. To achieve this, Flutter incorporates secure local storage mechanisms.&lt;/p&gt;

&lt;p&gt;Local storage retains user data until the application is uninstalled. The implementation of local storage varies across different mobile operating systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Android Security&lt;/strong&gt;: Android adopts the Advanced Encryption Standard (AES) to secure local storage. The secret key is encrypted using the RSA algorithm, and the encrypted key is stored in the &lt;a href="https://developer.android.com/reference/java/security/KeyStore"&gt;KeyStore&lt;/a&gt;, ensuring a high level of protection for the tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iOS Security&lt;/strong&gt;: For iOS, the &lt;a href="https://developer.apple.com/documentation/security/keychain_services"&gt;KeyChain&lt;/a&gt; serves as a secure storage mechanism for accessing the application's cryptographic keys, providing a decent level of security for the stored tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By leveraging these secure local storage technologies, our eCommerce mobile app adheres to industry best practices, maintaining the utmost confidentiality of the security tokens. This approach enhances the overall security of the app, user’s data protection and privacy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Automating maintenance and updates
&lt;/h3&gt;

&lt;p&gt;Creating an eCommerce app that requires regular automatic updates calls for an effective solution in establishing a connection with an API. Our mobile app is thoughtfully designed to integrate with the nopCommerce ecosystem, encompassing its CMS, additional extensions, and the official &lt;a href="https://www.nopcommerce.com/en/web-api"&gt;Web API plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For enhanced flexibility and transparency, we have included the Web API plugin within the mobile app package, making the source code readily available.&lt;/p&gt;

&lt;p&gt;Interacting with an API can often pose challenges in implementing an access client and keeping it up to date. However, we have streamlined this process to simplify adaptation to changes in the API.&lt;/p&gt;

&lt;p&gt;To achieve this, we ensure that our API adheres to the OpenAPI 3 specification, enabling us to store a comprehensive description of the API in JSON format. By leveraging the &lt;a href="https://openapi-generator.tech/"&gt;OpenAPI Generator&lt;/a&gt;, we efficiently generate a client in the required language, which, in our case, is Dart for Flutter, using the &lt;a href="https://openapi-generator.tech/docs/generators/dart-dio/"&gt;dart-dio Generator&lt;/a&gt;. This approach ensures smooth and efficient communication with the API, facilitating regular updates and seamless integration with the nopCommerce platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YalaFdQV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ytcfxb6flj7kjbnn09ng.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YalaFdQV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ytcfxb6flj7kjbnn09ng.jpg" alt="Image6" width="417" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The API connection in Flutter is established by integrating the generated client as a package within the app. This setup grants access to all the methods supported by the API, and incorporating these methods into the repositories enables us to leverage their functionality seamlessly within the app.&lt;/p&gt;

&lt;p&gt;When modifications occur in the API methods, such as changes in their signature or structural adjustments, rebuilding the client becomes a straightforward process. This ensures seamless updates and guarantees that the mobile app operates smoothly.&lt;/p&gt;

&lt;p&gt;In addition to the "Web API Frontend" plugin, the "nopCommerce mobile app" plugin allows users to manage specific application settings directly from the admin panel of their nopCommerce webshop. For example, administrators can configure a slider on the main screen to showcase selected products or transfer certain settings to the mobile application. This plugin empowers users with more control and customization options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Testing and assessing performance
&lt;/h3&gt;

&lt;p&gt;Our main objective revolved around evaluating the speed and efficiency of the advertising slider rendering with animation. To accomplish this, we utilized the &lt;a href="https://www.gamebench.net"&gt;GameBench&lt;/a&gt; automation interface tool to monitor critical metrics like memory consumption, FPS, and CPU load.&lt;/p&gt;

&lt;p&gt;For the purpose of testing, we created an identical interface natively on an Android device using both React Native and Flutter. To ensure consistent conditions, we standardized the test duration and employed image caching libraries in each language. This setup enabled us to conduct fair and accurate comparisons between React Native and Flutter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JqnWOy5H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5m7ifcu6nxgktlwec9f9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JqnWOy5H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5m7ifcu6nxgktlwec9f9.jpg" alt="Image7" width="580" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The performance testing results are outlined below, where lower values indicate better performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FPS&lt;/strong&gt;: Across all cases, the FPS values were nearly identical, indicating that Flutter's declared capabilities align closely with its real performance indicators.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Consumption&lt;/strong&gt;: While Flutter required twice as much memory as native applications, it remained more efficient than React Native, which experienced greater fluctuations in memory usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CPU Load&lt;/strong&gt;: React Native showed noticeable disparities in CPU load, primarily due to the utilization of a JS Bridge between JavaScript and Native code. In contrast, Flutter demonstrated more stable CPU performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although native Android apps demonstrated better battery consumption optimization, Flutter's stable performance and efficiency made it a strong runner-up in this aspect.&lt;br&gt;
It is essential to acknowledge that this comparison reveals significant differences in the performance characteristics of these approaches. However, while performance is crucial, it is not the sole determinant in eCommerce mobile app development. Other factors like development speed, user experience, and overall app functionality also play pivotal roles in the final decision-making process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;If you're looking for guidance on how to create an eCommerce app for your online store, our comprehensive guide covers all the essential steps taken during the development process, offering valuable insights into creating the final product.&lt;/p&gt;

&lt;p&gt;Alternatively, you have the option to use our ready-made nopCommerce mobile app, complete with its source code, which you can easily customize to align with your business needs.&lt;/p&gt;

&lt;p&gt;Stay tuned for future updates as we continuously enhance our mobile app, adding even more value and functionality. Be sure to explore our &lt;a href="https://docs.nopcommerce.com/en/developer/mobile-app/index.html"&gt;detailed mobile app documentation&lt;/a&gt; for additional support and information. We are committed to providing the best possible experience to our users.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>api</category>
      <category>mobile</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The best 5 auto parts eCommerce platforms</title>
      <dc:creator>Dmitry Kulagin</dc:creator>
      <pubDate>Wed, 15 Feb 2023 18:59:15 +0000</pubDate>
      <link>https://dev.to/dmitrykulagin2/the-best-5-auto-parts-ecommerce-platforms-25d9</link>
      <guid>https://dev.to/dmitrykulagin2/the-best-5-auto-parts-ecommerce-platforms-25d9</guid>
      <description>&lt;p&gt;The automotive market has changed over the past 20 years as an outcome of a rise in online auto sales, due to the impact of automotive marketplaces.&lt;/p&gt;

&lt;p&gt;The market for online car parts in North America reached $26 billion in 2021, an increase of more than 30% from 2020. Furthermore, the percentage of US consumers who purchase cars online nearly increased from 32% to 61%. The COVID-19 pandemic's consequences and ongoing investments in eCommerce make this an excellent moment for business owners to start an online auto parts store.&lt;/p&gt;

&lt;p&gt;Automotive businesses embrace a more omnichannel approach, allowing customers to research and buy products through both physical and digital channels. Similar to many other digital commerce sectors, companies create or redesign their online auto parts stores using eCommerce platforms.&lt;/p&gt;

&lt;p&gt;Therefore, auto parts eCommerce store owners are recommended to choose the best auto parts eCommerce platform that enables them to cover all business requirements and to seamlessly scale a business.&lt;/p&gt;

&lt;p&gt;To understand what eCommerce platform is the one that fits your automotive online store, we have put together an overview of the 5 most commonly-used auto parts eCommerce platforms. Before we dive deep into discussing their pros and cons of them, we will look at features that are essential for an eCommerce platform to provide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features that make auto parts eCommerce store outstanding
&lt;/h2&gt;

&lt;p&gt;Your auto parts online store may offer a wide range of functionality, depending on the company strategy and customer requirements. However, there are a few key features you should consider when choosing an eCommerce platform:&lt;/p&gt;

&lt;h3&gt;
  
  
  Robust auto parts filtering
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IUzs7OAQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pj5yxvp7zlthb9udkvig.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IUzs7OAQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pj5yxvp7zlthb9udkvig.jpg" alt="advanced search for auto parts eCommerce store" width="880" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When looking for auto parts, various types of customers rely on different criteria. The most usable parameter:&lt;/p&gt;

&lt;p&gt;YMM search (Year-Make-Model) enables customers to find required parts based on their vehicle model;&lt;br&gt;
VIN search (vehicle identification number) may help your auto parts website visitors look for spare parts that match specific batch produced at a particular plant;&lt;br&gt;
Manufacturer filter allows you to link many items under one brand and look for related items by brand. It may also help for cross- and up-sells since merchants can create manufacturer profiles and product suggestions.&lt;/p&gt;

&lt;p&gt;Finally, you may provide other specifications for advanced searching by including custom fields from pre-filled product characteristics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Product description automation
&lt;/h3&gt;

&lt;p&gt;With a chosen eCommerce platform, you should be able to work with product data without losses. Therefore, checking if preferred PIM, CRM, or other systems can be seamlessly integrated is recommended.&lt;/p&gt;

&lt;p&gt;As most auto parts are complex, an eCommerce platform has to allow adding guidance and instructions to a product page. In addition, custom product attributes should be allowed to be created since they provide customers the ability to filter products by various parameters.&lt;/p&gt;

&lt;p&gt;There are buyers who require sets of auto parts for certain vehicle systems. So creating product combinations or kits has to be enabled on an eCommerce platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrations with other online selling platforms and marketplaces
&lt;/h3&gt;

&lt;p&gt;With a chosen eCommerce software automotive businesses should be able to support dropshipping, as it can help distributors and retailers. Moreover, it can be valuable for a business if merchants can integrate their auto parts online store with popular marketplaces, like eBay, Amazon, Google Shopping, and other selling platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  The list of top 5 automotive eCommerce platforms
&lt;/h2&gt;

&lt;p&gt;Many numerous options for auto parts eCommerce software are presented now and most of them provide similar functionalities. However, there are 5 automotive eCommerce platforms that have advanced features and are leaders in the automotive sector. &lt;/p&gt;

&lt;h3&gt;
  
  
  nopCommerce
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sohovwYE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v0wc10iylkb1nlejgwe6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sohovwYE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v0wc10iylkb1nlejgwe6.jpg" alt="nopCommerce automotive eCommerce platform" width="880" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scalable &lt;a href="https://www.nopcommerce.com/en/solutions/automotive-ecommerce"&gt;automotive eCommerce platform, nopCommerce&lt;/a&gt;, is free and open-source software. This solution helps auto parts sellers to market their products effectively. nopCommerce is the most recognizable ASP.NET eCommerce platform having a large community of over 250’000 members.&lt;/p&gt;

&lt;p&gt;The automotive business, especially Enterprise-level one, usually deals and manages with an enormous number of SKUs. It is not a challenge for eCommerce stores that are powered by nopCommerce, this platform can be easily integrated with inventory and information management systems. Moreover, the nopCommerce software provides a high-level customer experience thanks to YMM, VIN, and other types of searches.&lt;/p&gt;

&lt;p&gt;Here is the list of other features that enable auto parts store owners to sell online more effortlessly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;popular payment and shipping methods and providers are available out-of-the-box and on the &lt;a href="https://www.nopcommerce.com/en/marketplace"&gt;Marketplace&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;automation of routine processes (tax management, operational and marketing notifications, etc.);&lt;/li&gt;
&lt;li&gt;multi-store and multi-vendor functionalities;&lt;/li&gt;
&lt;li&gt;innovative SEO features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The nopCommerce platform is powerful and scalable, however, it requires technical skills to install and deploy an eCommerce store. Moreover, choosing a hosting option for an online store can be challenging but there are hosting partners that are cost- and data-effective. nopCommerce has a collaboration with Everleap that helps improve website performance by &lt;a href="https://www.nopcommerce.com/en/private-cloud-hosting-solution"&gt;deploying an eCommerce store&lt;/a&gt; on a custom fully managed private cloud hosting solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Magento (Adobe Commerce)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z4Uu1rK4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/een8qwgw1olpnd4q93up.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z4Uu1rK4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/een8qwgw1olpnd4q93up.jpg" alt="Magento automotive eCommerce platform" width="880" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Acquired by Adobe, Magento is a free open-source auto parts eCommerce platform with broad functionality. There is also a self-hosted version, Adobe Commerce, that has two packages: “Commerce Pro” and more advanced “Managed Services”.&lt;/p&gt;

&lt;p&gt;Because of Magento’s open-source nature, most of the components of an eCommerce store powered by this platform may be customized, provided you have the necessary expertise. Magento has many features that enable merchants to effectively sell auto parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ability to integrate with many auto parts databases;&lt;/li&gt;
&lt;li&gt;“fit my car” searching tools;&lt;/li&gt;
&lt;li&gt;mobile-friendly interface and payment systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also install new features and extensions from the Magento Marketplace.&lt;/p&gt;

&lt;p&gt;Although Magento may seem an appropriate platform with many features, it has a complicated setup process. Without the assistance of a developer, merchants are unlikely to set up their Magento-powered websites. Moreover, another issue of this eCommerce platform is website performance, which heavily relies on a chosen hosting solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  BigCommerce
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CIV0Oywt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhgkranam8t0sqokw7pa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CIV0Oywt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhgkranam8t0sqokw7pa.jpg" alt="BigCommerce automotive eCommerce platform" width="880" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Small auto parts eCommerce firms can benefit from using BigCommerce, a SaaS solution. The BigCommerce platform is available to merchants through subscription plans, the least expensive of which is priced at $29.95/month.&lt;/p&gt;

&lt;p&gt;First of all, there are a lot of simple drag-and-drop tools and themes available in BigCommerce. An auto parts website may be launched essentially without effort. The aid of web professionals or technical support is unwanted, you can easily construct a website with the help of this SaaS platform. BigCommerce comes with useful features out of the box, like faceted search, multi-store, and bulk pricing.&lt;/p&gt;

&lt;p&gt;On the contrary, it is challenging to switch from BigCommerce to another platform because it is a closed, proprietary platform. Merchants are forced to use it unless they want to spend money on expensive data migration. The fact that some features of BigCommerce are only available with higher-tier plans is another drawback. Entry-level plans, for instance, are unable to use the functionality for preserving abandoned carts. Additionally, each plan tier has a sales cap, so a merchant needs to switch to a more advanced subscription plan. Finally, there are very few customization options accessible on the front end and none at all on the back end.&lt;/p&gt;

&lt;h3&gt;
  
  
  WooCommerce
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rfYauLA2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4d0ntwwh4dkgvyugpzh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rfYauLA2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4d0ntwwh4dkgvyugpzh.jpg" alt="WooCommerce automotive eCommerce platform" width="880" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compared to other eCommerce website builders, WooCommerce is a WordPress plugin that is free and open-source. Especially for startups and small businesses, WooCommerce is a flexible eCommerce solution that can help create auto parts stores.&lt;/p&gt;

&lt;p&gt;If you have the resources, WooCommerce may be altered and personalized greatly. It is based on WordPress, therefore it has some sophisticated blogging and SEO tools. WooCommerce offers a wide variety of premium and free plugins and themes. This eCommerce plugin offers multi-store functionality. Additionally, a WooCommerce single-vendor store might incorporate the multi-vendor feature using external plugins.&lt;/p&gt;

&lt;p&gt;Despite the fact that WooCommerce is a free eCommerce plugin, merchants still need to pay for their own web hosting. Furthermore, businesses must set up their own shipping and payment systems. To fully utilize its capabilities, you'll need a lot more technical know-how or money to hire developers. This is especially true if you require a lot of extra features and customization. At last, on WooCommerce, performance issues can be more frequent and more challenging to fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Squarespace
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eBJA1plM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbvj4ta1oamrq6yz2js7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eBJA1plM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbvj4ta1oamrq6yz2js7.jpg" alt="Squarespace automotive eCommerce platform" width="880" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Squarespace is a website-building company with “drag-and-drop” tools that enables you to create a website without touching a single piece of code. The platform provides eCommerce functionality with the Basic Commerce plan that starts at $26/month.&lt;/p&gt;

&lt;p&gt;Choosing Squarespace for your auto parts eCommerce store can be beneficial for merchants who require an easy website setup, customization, and launch using a drag-and-drop editor. Squarespace also handles hosting, domain names, and other technical things with support for new customers who are setting up their stores. Squarespace provides over 100 templates with the support of mobile devices that you can use.&lt;/p&gt;

&lt;p&gt;There are a few useful features included in the Basic Commerce plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS/JavaScript customizability;&lt;/li&gt;
&lt;li&gt;promotional pop-ups and banners;&lt;/li&gt;
&lt;li&gt;a variety of eCommerce analytics tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In terms of drawbacks, with Squarespace, it can be challenging to accelerate loading times past 3–5 seconds. Poor overall performance could lead to lost sales. Moreover, this software only supports Stripe, PayPal, Afterpay, and ApplePay, which can be constricting if you wish to use another payment processor, especially if you want to sell your products worldwide. You could struggle to place your website content exactly where you want it because of the "content block" architecture used to develop websites, especially if you're an advanced user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which platform is the best for a moto and car parts eCommerce store?
&lt;/h2&gt;

&lt;p&gt;The answer to this question is not certain and universal for every auto parts retailer. Automotive eCommerce businesses need to pick software that covers their requirements and enables future growth.&lt;/p&gt;

&lt;p&gt;Features, like advanced parts search, product information management, and integrations with marketplaces and selling platforms are pretty much installed or can be integrated into the platforms we have presented in this ranking.&lt;/p&gt;

&lt;p&gt;At the same time, nopCommerce powers solutions for auto parts online sellers more effectively, due to out-of-the-box features and great performance. Moreover, it is not only free to use, but your data remains your own and free of proprietary platforms. Finally, it is scalable and enables auto parts merchants to sell an infinite number of products without additional fees.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>dotnet</category>
      <category>performance</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How to develop and customize a shopping cart based on ASP.NET with nopCommerce</title>
      <dc:creator>Dmitry Kulagin</dc:creator>
      <pubDate>Wed, 15 Feb 2023 14:45:09 +0000</pubDate>
      <link>https://dev.to/dmitrykulagin2/how-to-develop-and-customize-a-shopping-cart-based-on-aspnet-with-nopcommerce-22a1</link>
      <guid>https://dev.to/dmitrykulagin2/how-to-develop-and-customize-a-shopping-cart-based-on-aspnet-with-nopcommerce-22a1</guid>
      <description>&lt;p&gt;An essential component of the online store is the shopping cart. It might also be among the hardest components to create for an eCommerce website. Customers can choose things, evaluate their choices, edit them, add more items if necessary, and then buy the items using a shopping cart. During checkout, the program normally produces an order total that takes into account postage, packing, and handling fees as well as taxes, if applicable.&lt;/p&gt;

&lt;p&gt;In the case of nopCommerce, a shopping cart, as well as a wishlist is built-in. If enabled in the admin area, each product can be put into the shopping cart or wishlist. The nopCommerce shopping cart and wishlist can be disabled, the access control list page can be used to configure it. Simply look for the "Enable shopping cart" and "Enable wishlist" permissions for the required customer profiles, and uncheck them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fao3g4ie55umaxcu3y1l1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fao3g4ie55umaxcu3y1l1.jpg" alt="Shopping cart configuration" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To learn more about shopping cart functionality, as well as how other components of nopCommerce work, we recommend taking the &lt;a href="https://www.nopcommerce.com/en/training" rel="noopener noreferrer"&gt;nopCommerce training course&lt;/a&gt;, which has been composed by the core developers team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shopping cart structure of the .NET eCommerce platform
&lt;/h2&gt;

&lt;p&gt;To start with, open and look at the shopping cart page. At first, it has a summary view of the order with selected checkout attributes, price, quantity (which can be adjusted), the total for each added item, and a remove button.&lt;/p&gt;

&lt;p&gt;Next, a customer can add estimated shipping, as well as select checkout attributes, and enter discount codes and gift cards. Finally, an order is calculated and it is offered to proceed to checkout, where customers enter billing and shipping information and pay.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuqt3d2zg6iclh6kg1b8s.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuqt3d2zg6iclh6kg1b8s.jpg" alt="nopCommerce shopping cart" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the way, the shopping cart related action methods are all placed in the ShoppingCartController from Nop.Web. These methods allow a customer to interact with all page components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2661ygij06db1vmmu1r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2661ygij06db1vmmu1r.jpg" alt="ShoppingCartController" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s break down all the main components of the nopCommerce shopping cart functionality. It consists of checkout attributes, discount, and gift card boxes, the totals step, and the checkout process. It is significant to consider a database structure as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checkout attributes
&lt;/h3&gt;

&lt;p&gt;Starting with checkout attributes, they are displayed on the shopping cart page and provide the opportunity to offer more services to customers, i.e. gift wrapping, before placing the order. &lt;/p&gt;

&lt;p&gt;Checkout attributes have their own service classes in the Nop.Services.Orders folder and are stored as XML in the CheckoutAttributesXml column of the Order table. They all have almost the same functionality as product attributes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;CheckoutAttributeService class&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dvwlo3rlrlqxtf9phhj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dvwlo3rlrlqxtf9phhj.jpg" alt="CheckoutAttributeService class" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;CheckoutAttributesXml column&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q1syfeb5cvl3lbp23kd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q1syfeb5cvl3lbp23kd.jpg" alt="CheckoutAttributesXml column" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Discount box
&lt;/h3&gt;

&lt;p&gt;The next feature of the shopping cart is the discount box. It allows a customer to enter a coupon code to apply a discount to order totals or shipping. The discount feature has its own folder that is located in the Nop.Services section. Any rules and logic of a discount implementation process can be adjusted there.&lt;/p&gt;

&lt;p&gt;Talking about the database, the discounts themselves are stored in the Discount table. There are also a few mapping tables that store how the discounts are mapped to products, manufacturers, and categories. The DiscountRequirement table stores the discount requirements determining in which case a discount should be applied. The last table is the DiscountUsageHistory table which stores information about the discounts applied to orders.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhqrh3zzj3yxjvsrt0o1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhqrh3zzj3yxjvsrt0o1.jpg" alt="Discount table" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Gift card box
&lt;/h3&gt;

&lt;p&gt;The other shopping cart component is the gift card box. A customer should enter the code here to apply a gift card. A gift card is a special type of product. You can mark a product as a gift card on the product details page and sell gift cards that customers can use this way. After customers complete purchases with the gift card products, you can then search and view the list of all the purchased gift cards here.&lt;/p&gt;

&lt;p&gt;In case you need to configure gift card functionality, you may use the GiftCardService class, which is located in the Nop.Service.Orders folder. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6iiyx3f4lvvpxtrorvj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6iiyx3f4lvvpxtrorvj.jpg" alt="GiftCardService class" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Considering the database, the gift cards that are manually created or bought as products are stored in the GiftCard table. Once a gift card is used in the order, the appropriate record is inserted into the GiftCardUsageHistory table of the database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44e2tns4utehdsghkph1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44e2tns4utehdsghkph1.jpg" alt="GiftCard table" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A customer can apply both a discount and a gift card on the shopping cart page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Totals
&lt;/h3&gt;

&lt;p&gt;The last interesting block on the shopping cart page is the shopping cart totals. They are calculated based on the shopping cart items, applied discounts and taxes, chosen shipping, and payment methods. A total consists of a few rows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sub-Total. The subtotal is the total of all items and quantities in the shopping cart, including applied item promotions;&lt;/li&gt;
&lt;li&gt;Shipping is the shipping cost calculated based on the customer’s shipping address;&lt;/li&gt;
&lt;li&gt;Then, the applied taxes are displayed. nopCommerce provides tax plugins that allow to manually or automatically apply taxes to products;&lt;/li&gt;
&lt;li&gt;The payment method additional fee row is self-descriptive. It can be set up on the payment method configuration page;&lt;/li&gt;
&lt;li&gt;Then, it displays the applied discounts and gift cards;&lt;/li&gt;
&lt;li&gt;The reward points if applied;&lt;/li&gt;
&lt;li&gt;And finally comes the order total.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wyaylgeo1pgmb2gl4fd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wyaylgeo1pgmb2gl4fd.jpg" alt="totals" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The order totals are calculated in the OrderTotalCalculationService from Nop.Services.Orders. As you can see, it contains such methods as GetShoppingCartShippingTotal, GetTaxTotal, the methods related to reward points, discounts, and others. In its turn, the payment method additional fee is calculated in the payment service of Nop.Services.Payments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5fk16ncu438x7okhorzz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5fk16ncu438x7okhorzz.jpg" alt="OrderTotalCalculationService class" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Checkout process
&lt;/h2&gt;

&lt;p&gt;For a more clear view of the whole buying experience, It is important to consider the checkout process. Checkout starts when the customer leaves the shopping cart to proceed to payment and shipping.&lt;/p&gt;

&lt;p&gt;nopCommerce provides two types of checkout process design. They are one-page checkout, which is set by default, and checkout with multiple pages. One-page checkout allows a customer to go through the whole checkout process using a single page. You can set this up on the Order settings page. By the way, you can temporarily disable checkout for your customers if needed in the same Checkout section.&lt;/p&gt;

&lt;p&gt;A customer goes through the following steps during the checkout process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A customer needs to enter a billing address;&lt;/li&gt;
&lt;li&gt;Then shipping address;&lt;/li&gt;
&lt;li&gt;Then, they need to choose a shipping method;&lt;/li&gt;
&lt;li&gt;And a payment method;&lt;/li&gt;
&lt;li&gt;Then, we show the required payment information to the customer;&lt;/li&gt;
&lt;li&gt;And finally, it’s the confirmation step where the customer confirms the order.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are more conditions that impact the number of checkout steps. They can be adjusted in the Order settings.&lt;/p&gt;

&lt;p&gt;All the action methods related to the checkout process are placed in the CheckoutController. As you can see, this controller has separate regions: for the multistep checkout and one-page checkout. Multistep checkout methods return views, while one-page checkout methods return JSON as a result. There is no separate service that manages the checkout process. All work is done in the controller.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdnflmghnq7tuo7e97vhc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdnflmghnq7tuo7e97vhc.jpg" alt="CheckoutController" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Database operation of the ASP.NET shopping cart
&lt;/h2&gt;

&lt;p&gt;In nopCommerce, a shopping cart and a wishlist both have the same structure. For more understanding, it is better to look at the database. In the ShoppingCartItem table, both shopping cart and wishlist items are stored together. However, they are distinguished by ShoppingCartTypeId.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3uliwi40lon1pg93gr7m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3uliwi40lon1pg93gr7m.jpg" alt="ShoppingCartItem table" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Moreover, looking at the “Domain – Orders” folder, there could be seen ShoppingCartType enumeration that provides two types: ShoppingCart and Wishlist with appropriate numeric values. These are the values that are stored in the ShoppingCartTypeId column in the database. This is the only difference between a shopping cart and a wishlist in the database terms;&lt;/p&gt;

&lt;p&gt;The other fields represent the customer identifier as a foreign key. It is an ID of a customer that has added this item to the shopping cart. Also, there is ProductId that identifies which product has been added to the cart, which is followed by StoreId representing the store of that product. Further, ShoppingCartTypeId that we already discussed determines whether the customer has added this item to the shopping cart or wishlist.&lt;/p&gt;

&lt;p&gt;Then comes the AttributesXml column. This column stores the product configuration added to the cart with all attributes chosen by a customer. Here is the process of how this XML works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;open any product page and choose two attributes of this product;&lt;/li&gt;
&lt;li&gt;add the product to the cart;&lt;/li&gt;
&lt;li&gt;select Top 1000 rows of ShoppingCartItem;&lt;/li&gt;
&lt;li&gt;click on a cell under AttributesXML;&lt;/li&gt;
&lt;li&gt;you will see the first attribute that is with ID 9 and a value of 21, as well as the second with ID 10 and a value of 25.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the way how chosen product attributes are stored. To parse such XMLs, there is a special class named ProductAttributeParser located in the Nop.Services Catalog folder. This class manipulates product attribute XMLs. For example, using the ParseProductAttributes method, it creates an XML based on the chosen attributes when a customer adds the product to the cart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7k1n95ghyyteww3okxi8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7k1n95ghyyteww3okxi8.jpg" alt="ProductAttributeParser class" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, this method accepts a form from the product details page. It is also used to recalculate the product price when the attribute values are changed on the product details page (&lt;u&gt;GetUnitPriceAsync method&lt;/u&gt;). So, in this class, you can find the methods operating with product attribute XML records. It also manipulates product attribute combinations, rental dates, and gift card attributes.&lt;/p&gt;

&lt;p&gt;Another important column is the CustomerEnteredPrice. It is used when the “Customer enters price” option is available for a certain product. The Quantity column is quite self-descriptive, as it shows chosen quantity of a product. The RentalStartDate and RentalEndDate columns are used when the product type is rental. Finally, CreatedOn and UpdatedOn dates are self-explanatory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The nopCommerce shopping cart is a structured and flexible component. Based on information from this article, you can effectively create and configure a website with an advanced shopping cart and a wishlist using the nopCommerce platform.&lt;/p&gt;

&lt;p&gt;There is no need to build everything from scratch, so it allows you to focus only on the specific requirements of your eCommerce business. One of the reasons for it is that the nopCommerce shopping cart has the most used features, like shipping estimation, checkout attributes, and a gift card and discount implementation.&lt;/p&gt;

&lt;p&gt;Finally, the database structure enables you to update what kind of shopping cart data is required to be stored and managed. You can also find these details in our documentation under the &lt;a href="https://docs.nopcommerce.com/en/developer/tutorials/db-schema.html#shopping-cart" rel="noopener noreferrer"&gt;Default database schema&lt;/a&gt; section.&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>Top 4 ASP.NET and .NET Open-Source Projects</title>
      <dc:creator>Dmitry Kulagin</dc:creator>
      <pubDate>Wed, 15 Feb 2023 12:49:35 +0000</pubDate>
      <link>https://dev.to/dmitrykulagin2/top-4-aspnet-and-net-open-source-projects-1cg8</link>
      <guid>https://dev.to/dmitrykulagin2/top-4-aspnet-and-net-open-source-projects-1cg8</guid>
      <description>&lt;p&gt;If you are a web developer, open-source projects can help not only expand your practical knowledge but build solutions and services for yourself and your clients. These software provide hands-on opportunities to implement existing approaches, patterns, and software engineering techniques that can be applied to projects further down the road.&lt;/p&gt;

&lt;p&gt;Since it is vital to securely create solutions that may be easily scaled, we will consider projects that are built on ASP.NET technology. It is a framework for building innovative cloud-based web applications using .NET that can be used for development and deployment on various operating systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open-source projects on ASP.NET and .NET
&lt;/h2&gt;

&lt;p&gt;Four open-source projects that would let you work with various architecture and code techniques have been compiled by our team.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. nopCommerce
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wi-3Iiht--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i162rtt2c9ng6ylvv54r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wi-3Iiht--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i162rtt2c9ng6ylvv54r.png" alt="nopCommerce GitHub" width="880" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;nopCommerce is an open-source eCommerce platform that is free and stands as the most powerful shopping cart built on ASP.NET Core in the world. Fully customizable, stable, secure, and extensible, nopCommerce provides a variety of built-in enterprise eCommerce features that may help you to develop a project of any complexity.&lt;/p&gt;

&lt;p&gt;To help you get quickly and effectively acclimated with its architecture, main design, system requirements, installation steps, and other aspects of setup, there is comprehensive &lt;a href="https://docs.nopcommerce.com/en/index.html"&gt;documentation&lt;/a&gt; that covers every aspect of an online store development of any kind and size. &lt;/p&gt;

&lt;p&gt;Furthermore, the nopCommerce team has introduced a &lt;a href="https://www.nopcommerce.com/en/training"&gt;training course for developers&lt;/a&gt; that may give you a significant boost to start building eCommerce solutions, even enterprise-level ones, for the existing and new nopCommerce clients.&lt;/p&gt;

&lt;h4&gt;
  
  
  Technologies associated with nopCommerce:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Redis, is an in-memory data store that allows developers to store, access, and use data in applications by writing complex code with fewer and simpler lines;&lt;/li&gt;
&lt;li&gt;LINQ to DB, a Language-Integrated Query (LINQ) library for database access that provides a light and fast layer between your database and your Plain Old CLR Objects (POCO);&lt;/li&gt;
&lt;li&gt;NUnit, an open-source testing framework, which is made for all .NET languages;&lt;/li&gt;
&lt;li&gt;Moq is a user-friendly mocking framework that is built for .NET.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://github.com/nopSolutions/nopCommerce"&gt;nopCommerce’s GitHub&lt;/a&gt; statistics:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Latest release: 4.50.3;&lt;/li&gt;
&lt;li&gt;Starred by 7,392;&lt;/li&gt;
&lt;li&gt;5,401 closed issues;&lt;/li&gt;
&lt;li&gt;Languages: C# - 58.5%, HTML - 15.6%, JavaScript - 11.6%, TSQL - 10.1%, Less - 2.2%, CSS - 2.0%.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. OrchardCore
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gTGvVqri--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1hdvnwc5lgcrbmb6cu2t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gTGvVqri--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1hdvnwc5lgcrbmb6cu2t.png" alt="OrchardCore GitHub" width="880" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OrchardCore is a modular ASP.NET Core application framework and CMS, which is additionally open-source and multi-tenant. If you are a developer looking to build SaaS applications, you are likely to be more interested in the modular framework. It is important to distinguish between the framework and the CMS, as the latter is best for building administrable websites. Developers will typically use the CMS for building modules to enhance their sites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.orchardcore.net/"&gt;The OrchardCore documentation&lt;/a&gt; and its &lt;a href="https://github.com/OrchardCMS/OrchardCore#readme"&gt;README&lt;/a&gt; file on GitHub might assist you in developing a web CMS by outlining the architectural decisions that were made to address the specific issue of obtaining both flexibility and a positive user experience.&lt;/p&gt;

&lt;h4&gt;
  
  
  Technologies associated with OrchardCore:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Docker, a software platform that virtualizes the operating system (OS) of the computer on which it is installed and running, streamlining the process of building, running, managing, and distributing applications;&lt;/li&gt;
&lt;li&gt;Redis;&lt;/li&gt;
&lt;li&gt;SignalR is an ASP.NET software package that enables JavaScript server-side code to instantly transfer content to associated web clients.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  OrchardCore’s GitHub statistics:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Latest release: 1.4.0;&lt;/li&gt;
&lt;li&gt;Starred by 6,040;&lt;/li&gt;
&lt;li&gt;4,468 closed issues;&lt;/li&gt;
&lt;li&gt;Languages: C# - 51.8%, CSS - 20.6%, JavaScript - 15.7%, HTML - 9.5%, SCSS - 1.4%, Pug - 0.4%, Other - 0.6%.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. eShopOnWeb
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9aOgetJH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/plvrgybvtdvzs0h5hz8p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9aOgetJH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/plvrgybvtdvzs0h5hz8p.png" alt="eShopOnWeb GitHub" width="880" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;eShopOnWeb is a sample application powered by Microsoft, but it can serve as a starting point for developers who might feel overwhelmed by the complexity of the previously mentioned projects. Layered architecture with a monolithic deployment pattern is demonstrated in this project, which focuses on container-based application architecture.&lt;/p&gt;

&lt;p&gt;There is no certain documentation file or website page. However, you may find all helpful information from the &lt;a href="https://github.com/dotnet-architecture/eShopOnWeb/blob/main/README.md"&gt;README file&lt;/a&gt; on its repository. It has necessary links to other useful articles and videos that may assist you.&lt;/p&gt;

&lt;h4&gt;
  
  
  Technologies associated with eShopOnWeb:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Docker;&lt;/li&gt;
&lt;li&gt;MediatR;&lt;/li&gt;
&lt;li&gt;JWT Tokens, is an open standard that defines a compact and self-contained way for securely transmitting information between parties.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  eShopOnWeb’s GitHub statistics:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Latest release: one and only release;&lt;/li&gt;
&lt;li&gt;Starred by 7,808;&lt;/li&gt;
&lt;li&gt;296 closed issues;&lt;/li&gt;
&lt;li&gt;Languages: C# - 72.5%, HTML - 20%, CSS - 3.3%, SCSS - 3%, Dockerfile - 1.2%&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Miniblog.Core
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TPfIaoPy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ahcdb4hiu3xhxcykcwv3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TPfIaoPy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ahcdb4hiu3xhxcykcwv3.png" alt="Miniblog.Core GitHub" width="880" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Miniblog.Core is a full-featured blog software. Simple but modern, Miniblog.Core is a performance-focused ASP.NET Core engine for blogging, earning a 100/100 score on Google PageSpeed Insights on both desktop and mobile. As an open-source platform, it can be adapted to work with other .NET Core framework versions, as well.&lt;/p&gt;

&lt;p&gt;You may notice that there isn't a lot of documentation when you look at the GitHub repository that houses Miniblog. However, you can install a template so you can build it using Visual Studio by following the steps in the &lt;a href="https://github.com/madskristensen/Miniblog.Core"&gt;readme&lt;/a&gt; file, but be careful because this won't give you the most recent version. You can also check out its features by visiting a &lt;a href="https://miniblogcore.azurewebsites.net/"&gt;link to an example site&lt;/a&gt; created with Miniblog and published on Azure.&lt;/p&gt;

&lt;h4&gt;
  
  
  Miniblog.Core’s GitHub statistics:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Latest release: one and only release;&lt;/li&gt;
&lt;li&gt;Starred by 1,297;&lt;/li&gt;
&lt;li&gt;43 closed issues;&lt;/li&gt;
&lt;li&gt;Languages: JavaScript - 39.4%, C# - 35.6%, HTML - 12.0%, SCSS - 8.8%, CSS - 4.2%.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start Your Own eCommerce Project
&lt;/h2&gt;

&lt;p&gt;What are you waiting for? When it comes to developing solutions and expanding your knowledge with practical experience, there is no time like the present. Now that you have four potential projects to choose from, it’s up to you to carve out time to dedicate to self-development.&lt;/p&gt;

&lt;p&gt;If you want to start an eCommerce career, you can start exploring the world's most popular shopping cart that is built on ASP.NET Core — nopCommerce. &lt;a href="https://www.nopcommerce.com/download-nopcommerce"&gt;Download&lt;/a&gt; the powerful open-source eCommerce software based on .NET.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>dotnet</category>
      <category>github</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The nopCommerce eCommerce platform: brief of architecture and MVC pattern</title>
      <dc:creator>Dmitry Kulagin</dc:creator>
      <pubDate>Wed, 15 Feb 2023 11:33:07 +0000</pubDate>
      <link>https://dev.to/dmitrykulagin2/the-nopcommerce-ecommerce-platform-brief-of-architecture-and-mvc-pattern-3c7k</link>
      <guid>https://dev.to/dmitrykulagin2/the-nopcommerce-ecommerce-platform-brief-of-architecture-and-mvc-pattern-3c7k</guid>
      <description>&lt;p&gt;nopCommerce is an open-source project based on .NET technologies with understandable architecture. It is easily customizable and pluggable that enables the development and integration of any features, extensions, and themes. Every .NET developer may &lt;a href="https://www.nopcommerce.com/en/download-nopcommerce"&gt;download the source code&lt;/a&gt; and start creating any eCommerce project. &lt;/p&gt;

&lt;p&gt;To help developers navigate through the source code, the article has been prepared based on &lt;a href="https://www.nopcommerce.com/en/training"&gt;the training course&lt;/a&gt; that the nopCommerce team has developed. Here we consider an architecture and source code overview, as well as an MVC pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of the eCommerce platform architecture and source code organization
&lt;/h2&gt;

&lt;p&gt;The nopCommerce source code and architecture are quite easy to understand. The solution structure is pretty clear, so you should have no difficulties learning the codebase.&lt;/p&gt;

&lt;p&gt;Our architecture follows the separation of concerns principle, which helps us keep the growing codebase organized so that developers can easily find where a particular functionality is implemented.&lt;/p&gt;

&lt;p&gt;Most of the projects, directories, and files are named so that you can get a rough idea of their purpose. For example, there is no need to look inside the project named “Nop.Plugin.Payments.PayPalStandard” to guess what it does.&lt;/p&gt;

&lt;p&gt;So, when the nopCommerce solution is opened in Visual Studio, first, you see the following folder structure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ih5FLhWi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qwgckayrrbypate3sju0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ih5FLhWi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qwgckayrrbypate3sju0.png" alt="folders structure" width="880" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;nopCommerce is a multi-project solution, which means each project is considered to reside in a particular layer of the application. Next, a scheme that illustrates the relationships will be provided. &lt;/p&gt;

&lt;h3&gt;
  
  
  Libraries
&lt;/h3&gt;

&lt;p&gt;The directory “Libraries” contains 3 projects: "Nop.Core", "Nop.Data", and "Nop.Services". This is the innermost layer in the nopCommerce architecture system. It is the core of the application. All the data access logic and business classes reside inside this directory.&lt;/p&gt;

&lt;h4&gt;
  
  
  Nop.Core
&lt;/h4&gt;

&lt;p&gt;The "Nop.Core" project contains a set of core classes, such as domain entities, caching, events, and helper classes. &lt;/p&gt;

&lt;p&gt;In the “Domain” folder, the business objects can be seen. For example, the “Customer” entity or “Order” entity. So, here, you can find all the entities we use. In this directory, you can also see setting classes, such as “CatalogSettings” or “ForumSettings”.&lt;/p&gt;

&lt;p&gt;The "Nop.Core" project is kind of a centre of the nopCommerce architecture. This project has no dependencies on other projects in the solution. The classes of "Nop.Core" are shared with the entire solution.&lt;/p&gt;

&lt;p&gt;A scheme representing the nopCommerce architecture is presented. In this scheme, you can see that the "Nop.Core" project is related to the Business logic layer. All the other parts of nopCommerce are connected to this layer.&lt;/p&gt;

&lt;h4&gt;
  
  
  Nop.Data
&lt;/h4&gt;

&lt;p&gt;The next project is "Nop.Data". The "Nop.Data" project contains a set of classes and functions for reading from a database and writing to it. Therefore, it represents the Data access layer. The "Nop.Data" project helps us separate data-access logic from the business objects, which can be seen in "Nop.Core".&lt;/p&gt;

&lt;p&gt;In the “DataProviders” folder, you can see all the data providers we use in nopCommerce: Microsoft SQL Server, MySql, and PostgreSQL data providers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Uhn0NODp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dapzfr1cfedemxewvdw1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Uhn0NODp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dapzfr1cfedemxewvdw1.png" alt="database providers" width="880" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;nopCommerce uses the Linq2DB Code-First approach. Code-First allows us to define entities in the source code (they can be seen in the "Nop.Core" project) and then use “Linq2DB” and “FluentMigrator” to generate the database from these C# classes. That's why it's called “Code-First.” You can then query your objects using LINQ which is Language Integrated Query. LINQ translates to SQL behind the scenes and is executed against the database.&lt;/p&gt;

&lt;p&gt;nopCommerce uses fluent code API to customize the persistence mapping. You can see all the mapping classes in the “Builder” folder of the “Mapping” directory. If you open, for example, the ”BlogPostBuilder” class, you may see this class allows us to configure each column of the “BlogPost” table in the database as we need. Each entity has such a builder class.&lt;/p&gt;

&lt;p&gt;Then, the next important part of the "Nop.Data" project is Migrations. As you know, migrations allow us to alter the database schema. This is an alternative to creating lots of SQL scripts that have to be executed manually to apply any database changes.&lt;/p&gt;

&lt;p&gt;The "Nop.Data" project also contains the other interfaces and classes used to connect to the database. Unlike the "Nop.Core" project, "Nop.Data" has no dependencies on any other project in the solution.&lt;/p&gt;

&lt;h4&gt;
  
  
  Nop.Services
&lt;/h4&gt;

&lt;p&gt;The other important part is the "Nop.Services" project. It contains a set of core services, business logic, validations, and calculations related to the data. So the "Nop.Services" project is related to the Business logic layer. This project has a dependency on all other projects that belong to the Application Core: "Nop.Core" and "Nop.Data".&lt;/p&gt;

&lt;p&gt;This project provides data access functionality for the Presentation layer. This layer contains service classes and follows a repository pattern to expose its functionalities.&lt;/p&gt;

&lt;p&gt;The folder names are self-descriptive. If we look at the Blogs folder, for example, it contains a service that helps us get, insert, and delete blog posts in the repository. It also contains an interface for this service, allowing to change the implementation without updating the code. The idea is that when you need to change how the service handles some functions, all you have to do is create a new class that implements IBlogService and then register it with the NopStartup. So you don’t have to touch any of the controllers that use that interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Plugins
&lt;/h3&gt;

&lt;p&gt;To look at plugin projects the “Plugins” folder should be opened. A plugin is a set of components adding specific capabilities to a nopCommerce store. In other words, plugins are used to extend the functionality of nopCommerce. Each plugin is a separate project containing a feature.&lt;/p&gt;

&lt;p&gt;Plugins are classified into different groups. For example, payment methods (such as PayPal), tax providers (like Avalara), shipping rate computation methods (such as UPS or ShipStation), widgets (such as google analytics), and many others. As it can be seen, there are a lot of plugins coming out of the box. Any feature that is necessary to be added to a nopCommerce store, can be implemented in a plugin without touching the source code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MTBFAFVS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n0f37f0nxh35zfoneovp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MTBFAFVS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n0f37f0nxh35zfoneovp.jpg" alt="plugins" width="734" height="852"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Physically, plugins are located at the root of the solution. But plugins’ DLLs are automatically copied to the “Presentation\Nop.Web\Plugins” directory, which is used for already deployed plugins. This allows plugins to contain some external files, such as static content (CSS or JS files), without having to copy files between projects to run the project.&lt;/p&gt;

&lt;p&gt;On our scheme, you can see that plugins touch all the layers. This is because each plugin can potentially include its own data access, business logic, and UI layers at the same time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YYwvHKH---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bshic39e3alrj0xiukjm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YYwvHKH---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bshic39e3alrj0xiukjm.png" alt="architecture scheme" width="880" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Presentation
&lt;/h3&gt;

&lt;p&gt;Have a look at the “Presentation” folder. Its content represents the presentation or user interface layer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0muDi02a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ym4b2ps5k3qb0sx9ad1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0muDi02a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ym4b2ps5k3qb0sx9ad1.png" alt="folders structure presentation" width="880" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Nop.Web
&lt;/h4&gt;

&lt;p&gt;"Nop.Web" is an MVC web application project. "Nop.Web" provides a public interface and an administration panel included as an area. This is the application that you actually run. It is the startup project of the nopCommerce solution.&lt;/p&gt;

&lt;p&gt;This project contains controllers, factories, models, views, and other stuff related to the presentation layer. The "Nop.Web" project connects to "Nop.Services" to retrieve data, make calculations, and use business logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v8Du208C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dip4bscvbx49vn6evkck.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v8Du208C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dip4bscvbx49vn6evkck.png" alt="nop web plugins" width="880" height="869"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Nop.Web.Framework
&lt;/h4&gt;

&lt;p&gt;And "Nop.Web.Framework" is a class library project containing some common presentation things for the "Nop.Web" project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tests
&lt;/h3&gt;

&lt;p&gt;The last folder obviously contains tests. Unit tests are automated tests written and run by developers to ensure that an application section meets its design and behaves as intended. During development, we code criteria or results known to be good into the test to verify the application function’s correctness. During test case execution, the framework logs tests that fail any criterion and report them in a summary. The testing system in nopCommerce includes tests for the "Nop.Core", "Nop.Data", "Nop.Services", and "Nop.Web" projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kwRKfyAI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2pm97nicnkhhxyiwjnt6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kwRKfyAI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2pm97nicnkhhxyiwjnt6.png" alt="tests" width="880" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Asynchronous programming
&lt;/h3&gt;

&lt;p&gt;We also would like to note that nopCommerce utilizes asynchronous programming. Each controller or service method is asynchronous. And the “async” modifier indicates that. Such methods return “Task” objects that represent asynchronous operations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0tWCpGdK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xaghiluh7mhbzm13kwma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0tWCpGdK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xaghiluh7mhbzm13kwma.png" alt="async" width="880" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When an asynchronous method is called, it requires the “await” operator placed in front of it. This “await” operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand is complete. The “await” operator doesn't block the thread that evaluates the async method. When the asynchronous operation is complete, the “await” operator returns the result of the operation, if any. “Await” can only be used inside an async method.&lt;/p&gt;

&lt;p&gt;This way, by implementing the task asynchronous programming model in nopCommerce, we achieved great performance enhancements.&lt;/p&gt;

&lt;h2&gt;
  
  
  MVC pattern in nopCommerce
&lt;/h2&gt;

&lt;p&gt;nopCommerce implements the “Model-View-Controller” (MVC) design pattern in its architecture. This design pattern allows us to decouple the user interface (which is View), data (which is Model), and application logic (which is Controller). This pattern helps achieve the separation of concerns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8vsrlxhG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/chyiun9n4hcsrbekrx7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8vsrlxhG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/chyiun9n4hcsrbekrx7j.png" alt="MVC scheme" width="880" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the MVC pattern means that requests are routed to the Controller responsible for working with the Model to perform actions and/or retrieve data. The Controller chooses the View to display and provides it with the Model. The View renders the final page based on the data in the Model.&lt;/p&gt;

&lt;p&gt;This delineation of responsibilities helps us scale the nopCommerce app in terms of complexity because it's easier to code, debug, and test something (Model, View, or Controller) that has a single job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example of applying MVC
&lt;/h3&gt;

&lt;p&gt;Here we open the “BlogPost” view from the “Nop.Web” project. This view uses the Razor view engine to embed .NET code in HTML markup. It contains minimal logic, but any logic in this view is related to presenting content. We don’t perform a great deal of logic in view files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aD2gpYd0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ezdr167lusjsifdca72y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aD2gpYd0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ezdr167lusjsifdca72y.png" alt="blog post view" width="880" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to the MVC pattern, all the logic related to the View should be placed in the appropriate controller. Controllers are the components that handle user interaction, work with models, and ultimately select a view to render. In an MVC application, a view only displays information; a controller handles and responds to user input and interaction. In the MVC pattern, the Сontroller is the initial entry point responsible for selecting which model types to work with and which view to render.&lt;/p&gt;

&lt;p&gt;The name of the controller, in this case, is “BlogController”. Here we can find the “BlogPost” action method that retrieves data, populates the model with this data, and then returns the needed view.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HzOtc0Eb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yxwyr8p7adf634exgo2r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HzOtc0Eb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yxwyr8p7adf634exgo2r.png" alt="blog post controller" width="880" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This view uses the received model to present content. For example, this iteration displays the tags cloud on the page.&lt;/p&gt;

&lt;p&gt;Let’s go back to our controller. Controllers shouldn't be overly complicated with too many responsibilities. So we keep controller logic from becoming overly complex and push business logic out of the controller. If we dig deeper and fall into the “BlogService,” we will see that the business logic related to retrieving data from the database is actually placed here. The “GetBlogPostById” method accesses the repository to get data. But this method is not as huge as “GetAllBlogPosts,” for example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dkqI-UvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/papjb7ytu7h4iiq55k89.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dkqI-UvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/papjb7ytu7h4iiq55k89.png" alt="get blog post" width="880" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This way, we briefly considered the implementation of the MVC pattern in nopCommerce. During the training course, we will talk about it in more depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;From the article, you got acquainted with the nopCommerce structure and gained insight into its architecture. This is the core of the nopCommerce platform and contains essential functionalities and business logic. These basics are explained in detail and completed with information about additional functionalities and principles in our training course. &lt;/p&gt;

&lt;p&gt;The nopCommerce online course is compiled by the nopCommerce core developers based on our 13 years of experience, and questions frequently asked within the premium support services and community forums. &lt;/p&gt;

&lt;p&gt;It has been designed to cover the main aspects of developing an eCommerce store and teach to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;quickly run and configure websites of any complexity on nopCommerce;&lt;/li&gt;
&lt;li&gt;customize nopCommerce to fit unique business requirements;&lt;/li&gt;
&lt;li&gt;create integrations with third-party software;&lt;/li&gt;
&lt;li&gt;create and customize plugins and themes;&lt;/li&gt;
&lt;li&gt;avoid common mistakes when setting up and customizing nopCommerce;&lt;/li&gt;
&lt;li&gt;follow the best practices and use the latest development experience on nopCommerce.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visit &lt;a href="https://www.nopcommerce.com/en/training/course-levels"&gt;the training course content page&lt;/a&gt; to look at topics that are covered, as well as the conditions of starting the course. If you are ready to start deploying and customizing you may &lt;a href="https://www.nopcommerce.com/en/get-started"&gt;get started&lt;/a&gt; by downloading the package with the source code.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>dotnet</category>
      <category>architecture</category>
      <category>opensource</category>
    </item>
    <item>
      <title>eCommerce replatforming: how to survive the switch and success in the process?</title>
      <dc:creator>Dmitry Kulagin</dc:creator>
      <pubDate>Wed, 15 Feb 2023 08:25:09 +0000</pubDate>
      <link>https://dev.to/dmitrykulagin2/ecommerce-replatforming-how-to-survive-the-switch-and-success-in-the-process-3ihm</link>
      <guid>https://dev.to/dmitrykulagin2/ecommerce-replatforming-how-to-survive-the-switch-and-success-in-the-process-3ihm</guid>
      <description>&lt;p&gt;Today's market is more competitive than ever before, and eCommerce retailers must stay up with the rapid technological developments. One of the possible ways is to switch to a platform that is more flexible and powerful. A little over 18% of the 93 shops surveyed by &lt;a href="https://www.digitalcommerce360.com/article/ecommerce-platforms/"&gt;Digital Commerce 360&lt;/a&gt; said they were considering switching to eСommerce systems. &lt;/p&gt;

&lt;p&gt;If you're scaling your online store and beginning to experience noticeable increases in traffic and sales, you'll need a backend system to maintain your website and meet the goals set. The website uptime, user experience, scalability, and capacity for quick adjustments will be directly impacted by the choice of an eCommerce platform. However, replatforming an eCommerce site is a big task. It is a great chance to make some important changes, but the work can be very difficult if you don't know where to begin.&lt;/p&gt;

&lt;p&gt;In this article, we'll discuss what is eCommerce replatforming, how to understand when to replatform, why it's significant and what checklist to follow, and how it may benefit you before getting into the specifics of how to migrate store data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The meaning of eCommerce replatforming
&lt;/h2&gt;

&lt;p&gt;eCommerce replatforming involves moving from your current platform to one that is more appropriate for your company's needs. The reasons for eCommerce platform migration can range anywhere from – increasing site or operation speed, adding new functions or non-supported features, experimenting with new business models, or adding more layers on top of your customer experience (e.g. localization, personalization, and more).&lt;/p&gt;

&lt;p&gt;There are multiple ways businesses think of replatforming:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The first approach&lt;/strong&gt; is to switch from one monolithic platform to another platform that offers additional plugins and features. In this case, replatforming can be accompanied by redesign.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The second (modular) approach&lt;/strong&gt; is to divide the platform transition into several stages to prevent upfront costs. For example, if the main problem is an outdated CMS, it is the first component to adjust and then decide what other elements should be updated within the existing tech stack.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The third (plug-and-play) approach&lt;/strong&gt; is to migrate from a monolithic platform to microservices which are discrete applications with different functionalities. This approach essentially refers to &lt;a href="https://www.nopcommerce.com/en/headless-ecommerce"&gt;headless eCommerce&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These three types of replatforming have countless variations. To ensure that you fulfill all of your migration requirements and commercial objectives, each project (website) is unique and requires a special strategy. But first of all, you should make sure that you can’t do without replatforming. Below you’ll find typical “red flags” that mean your eCommerce platform calls for more than a planned update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Main reasons for migration
&lt;/h2&gt;

&lt;p&gt;If you see some of the following issues in your current website, we strongly recommend you consider eCommerce site migration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F1WEJt7c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8amba0mf1ph8reg3550k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F1WEJt7c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8amba0mf1ph8reg3550k.png" alt="eCommerce migration reasons" width="640" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://www.forbes.com/sites/forbestechcouncil/2018/03/23/the-best-cloud-migration-path-lift-and-shift-replatform-or-refactor/?sh=5bb79fc64f51"&gt;Forbes&lt;/a&gt;]&lt;/p&gt;

&lt;h3&gt;
  
  
  A need to cut development costs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;High maintenance costs.&lt;/strong&gt; It could be time to switch if maintaining your existing platform is a constant burden. Maintenance also takes time (and much money) and might divert focus from other corporate processes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Major costs of ownership.&lt;/strong&gt; To increase the functionality of your store, you need to add more plugins, make security patches and fix UI issues with the help of professional developers. eCommerce migration can decrease infrastructure or design costs by shifting the burden to a platform provider with a greater level of performance.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Technical limitations of a chosen eCommerce platform
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Poor performance.&lt;/strong&gt; The platform can have difficulties maintaining a large number of physical SKUs for your catalog database or lack omnichannel support.These restrictions cannot be always overcome with a simple update or upgrade.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Slow speed and no mobile optimization.&lt;/strong&gt; An outdated website can become unstable during peak traffic conditions, which causes poor loading time, high bounce rates, and loss of customers. Let alone that &lt;a href="https://www.oberlo.com/statistics/mobile-internet-traffic#:~:text=As%20of%20August%202022%2C%2053.74,46.26%20percent%20coming%20from%20desktops."&gt;53% of the total web visits are currently mobile&lt;/a&gt;, eCommerce replatforming will make your website run more quickly, and have a richer user interface.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limited scalability.&lt;/strong&gt; Your current platform might only be able to store a particular amount of attributes, a certain number of product-related assets, a given number of API calls per second, or even lack the support of some content types, for example, video. An online store can be scaled with personalized features like tailored customer content, personalized pricing, product selections, credit limit, order history, and personalized payment options for each customer segment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Marketing limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streamlined analytics.&lt;/strong&gt; To gain important insights and improve your eCommerce site, proper analytics is required. A new platform should allow data collection and measurement so you can provide the greatest customer experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lack of social media capabilities.&lt;/strong&gt; Integration with social network accounts is one of the most crucial services your platform can offer. This allows users to buy products from brands without leaving the social media app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Poor customer experience.&lt;/strong&gt; If too many plugins slow down your website or if your present platform doesn't offer the most recent technological capabilities, creating the best user experience should be a top concern.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to make eCommerce replatforming seamless
&lt;/h2&gt;

&lt;p&gt;eCommerce migration is typically a laborious, time-consuming, and resource-intensive process (be it financial costs or human resources). So it’s better to consider the following factors before switching to another eCommerce platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Estimate required output of eCommerce platform migration
&lt;/h3&gt;

&lt;p&gt;Most businesses think about replatforming when they have multiple problems that need to be resolved simultaneously, or when they are scaling, or adding new payment options or language versions. Whatever the cause, switching eCommerce platforms won't be worth it if it doesn't increase the output in terms of performance, functionality, design, and overall quality. &lt;/p&gt;

&lt;p&gt;Therefore, before you begin the process, you should understand the business objectives that replatforming should satisfy and whether the chosen solution can match your expectations.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Analyze the time and resources needed
&lt;/h3&gt;

&lt;p&gt;Replatforming is a significant organizational change that takes time and effort to complete. To cut down on time, it is important to keep the team that has already been involved in the migration and has the skills required to move it forward. You should also choose the best time to migrate to a new platform, which is typically when there is little traffic.&lt;/p&gt;

&lt;p&gt;Keep in mind that replatforming is a long-term investment and not a quick fix. The solution you intend to use should be flexible enough to move with the market while also responding to your company's needs as it expands.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Identify eCommerce platforms that have required integrations
&lt;/h3&gt;

&lt;p&gt;Your current eCommerce system is likely integrated with extensions, plugins, and programs that assist with fundamental operations like marketing, sales, CRM, and subscription billing. Not to worry about reducing performance and spending too much time on development to get everything together, make sure the platform you chose integrates effortlessly into your current infrastructure.&lt;/p&gt;

&lt;p&gt;The customer shopping experience is influenced by the functionality of your eCommerce store. The more features your website has, the more likely you will enhance conversion rates and boost consumer loyalty.&lt;/p&gt;

&lt;h2&gt;
  
  
  10 steps to effective eCommerce replatforming
&lt;/h2&gt;

&lt;p&gt;As you can see, replatforming is more significant than merely switching platforms to improve performance. After migrating to a new platform, many eCommerce companies saw a big rise in conversion rates and income. That’s why you need to develop a full-fledged eCommerce migration strategy and follow 10 simple steps to complete the task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1. Determine what features, customizations, and integrations you need
&lt;/h3&gt;

&lt;p&gt;List the flaws in your existing platform and determine your most pressing needs, the biggest gaps, and what you plan to gain from the website replatforming. These include automatable processes, information to be moved (i.e. products, existing customers, orders, etc.), resources to facilitate the migration, and the best time to begin and finish the process.&lt;/p&gt;

&lt;p&gt;In addition, you should do a realistic assessment, taking into account the overall expenditures, the labor needed to finish the job, and any upcoming deadlines. You want to be aware of both the advantages and any potential drawbacks of replatforming.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6EO2Gpw4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w6baxu93i5aho085g9u3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6EO2Gpw4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w6baxu93i5aho085g9u3.jpg" alt="Bounce rate vs page load time" width="625" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://www.digitalinformationworld.com/2021/01/ecommerce-replatforming-why-when-and-how.html"&gt;Digital Information World&lt;/a&gt;]&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2. Choose a new eCommerce platform based on your requirements
&lt;/h3&gt;

&lt;p&gt;Make a shortlist of potential eCommerce platforms and arrange them according to features, pricing, and other factors. To compare the tools you are evaluating, it is better to use a table or spreadsheet. &lt;/p&gt;

&lt;p&gt;Although you can check several listicles, it’s always better to get your hands on the real demo and try each platform yourself. This way you can discover more about the benefits and drawbacks of each option. After testing the demo, you should know more about how the system works, how simple it is to use, and how well it performs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;During replatforming you often need to redesign your website’s front-end, which is a great time to go headless. After all, you wouldn’t have thought of switching your tech stack if your system could have provided the best shopping experience. And it’s always better to do all the major changes in one go than implement them step-by-step and risk your website being out of operation for too long.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you currently use a monolithic commerce platform, you’re recommended to replatform to a solution that supports headless commerce to make sure you can keep up with the demands of modern online retail. Check out our list of the &lt;a href="https://www.nopcommerce.com/en/blog/best-headless-ecommerce-platforms"&gt;15 best headless eCommerce platforms&lt;/a&gt; and decide whose features will bring your online business to a new level.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3. Create a plan for the whole eCommerce migration process
&lt;/h3&gt;

&lt;p&gt;A strong pre-migration plan is crucial if you want to minimize customer inconvenience and website downtime. The next step is to create a reasonable task scope and migration schedule. You should draw out all milestones and resources needed at each stage.&lt;/p&gt;

&lt;p&gt;Most migration processes will require you to plan tasks in order to tackle the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a new site hierarchy&lt;/li&gt;
&lt;li&gt;Auditing and migrating content&lt;/li&gt;
&lt;li&gt;Front-end design&lt;/li&gt;
&lt;li&gt;Back-end development&lt;/li&gt;
&lt;li&gt;Pre- and post-launch training&lt;/li&gt;
&lt;li&gt;SEO optimization&lt;/li&gt;
&lt;li&gt;Data migration through a third-party service, API plugin, app, or manual CSV transfer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4. Save your eCommerce data before replatforming
&lt;/h3&gt;

&lt;p&gt;Make sure to backup everything, even if you have a clear plan for your data transfer. It can be challenging to move big volumes of data from one location to another, and mistakes can happen. If it seems implausible, the last thing you want when switching eCommerce platforms is to experience a significant problem while migrating your data.&lt;/p&gt;

&lt;p&gt;To avoid being caught in an uncomfortable scenario later on, back up everything to the cloud or an external hard drive. Basically, there are two approaches: manual export or automated backup using a special app. The valuable information you might want to keep includes website code, database, images, PDFs, videos, settings and configurations, designs, layouts, themes, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5. Create a UX-friendly checkout process
&lt;/h3&gt;

&lt;p&gt;Depending on the eСommerce platform you use, you may also be able to personalize and improve checkout (to process payments and complete orders). Connect any third-party applications to your new system and make every effort to improve the user flow. The main task is to ensure your consumers have a quick, secure, and effective new experience. The system should recognize, prevent and protect against fraudulent activity.&lt;/p&gt;

&lt;p&gt;If you choose a headless eCommerce platform, you can easily integrate checkout functionality from third-party eCommerce migration services. As a result, if you need some specific customizations to handle your checkout process, you can discuss this with your vendor and either work together to develop a solution or add functionality from another provider.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6. Plan how to keep your website’s search engine rankings
&lt;/h3&gt;

&lt;p&gt;Another concern is &lt;a href="https://www.nopcommerce.com/en/blog/how-to-migrate-your-ecommerce-store-without-losing-its-seo-ranks"&gt;SEO&lt;/a&gt; which is likely to take a hit when switching platforms. In the worst-case scenario, the domain authority and the other efforts made by the marketing team to increase traffic to your website may be lost during the migration. &lt;/p&gt;

&lt;p&gt;To level an impact on your search rankings, you'll need to conduct a thorough SEO analysis, redirect all of the pages and optimize off-page content. &lt;/p&gt;

&lt;p&gt;To mitigate such risks, make sure that you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Examine your current content and get rid of any outdated or duplicate material&lt;/li&gt;
&lt;li&gt;Make a thorough redirect scheme that matches pages one-to-one&lt;/li&gt;
&lt;li&gt;Optimize new material for your targeted keywords&lt;/li&gt;
&lt;li&gt;Delete your old site's metadata and schema markups&lt;/li&gt;
&lt;li&gt;Keep an eye on your organic traffic, search positioning, and technical SEO profile. &lt;/li&gt;
&lt;li&gt;Watch out for things like 404 errors, slow loading times, and any other warnings that can point to poor performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anyway, prior to the start of a new campaign or during a busy time for your company, we advise against replatforming since this may have an adverse effect on sales and operational bandwidth.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q4RpHPLd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0y0udnki3hl9m75cv2a1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q4RpHPLd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0y0udnki3hl9m75cv2a1.jpg" alt="SEO eCommerce migration" width="600" height="337"&gt;&lt;/a&gt;&lt;br&gt;
[&lt;a href="https://www.wolfgangdigital.com/blog/step-by-step-site-migration-roadmap/"&gt;WolfgangDigital&lt;/a&gt;]&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7. Transfer your old eCommerce store’s product and customer data
&lt;/h3&gt;

&lt;p&gt;eСommerce data migration is one of the biggest challenges when it comes to replatforming regardless of the method you choose: manually, through a migration app, or a third-party team. Some of your data (especially customer, category, and product data) could be lost permanently. Make sure you understand where your data is stored, whether you will have access to it during the transfer, and where it will be located on the new platform.&lt;/p&gt;

&lt;p&gt;Whichever option you go for, the pre-migration data plan will help you avoid costly pitfalls and possible data losses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Incorrectly mapped products&lt;/li&gt;
&lt;li&gt;Incorrectly displayed prices&lt;/li&gt;
&lt;li&gt;Limited personalization due to poorly synced customer data&lt;/li&gt;
&lt;li&gt;Negative impact on customer experience due to an improperly functioning website.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 8. Make changes to your online store
&lt;/h3&gt;

&lt;p&gt;Most time and resources will probably be spent during the design and development phase. That’s when your designers and developers will build the front and back ends of your new eCommerce website.&lt;/p&gt;

&lt;p&gt;It’s also a good time to make important adjustments to your website's navigation, content, user experience, design, or back-end operations, adapting them to a larger scale or completely changing their functioning. In the end, all of the data you back-upped and transferred over is placed into your newly redesigned website.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 9. Run tests on your website
&lt;/h3&gt;

&lt;p&gt;Run numerous tests before launching your new website to ensure that you can add items to your shopping cart, complete a transaction, and use custom features to make sure everything works as it should. It’s recommended to test each page, button, and form, even though it can seem excessive.&lt;/p&gt;

&lt;p&gt;Check that all of your pages are loading and functioning as intended by running your site through a performance auditing tool like Google PageSpeed Insights. By doing this, you can be sure that your website won't experience any speed or usability troubles.&lt;/p&gt;

&lt;p&gt;Ask internal stakeholders to work on the website's back end and fulfill particular tasks. Think about offering some of your most devoted clients beta or early access. Collect all of this feedback, and make the necessary adjustments before launch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 10. Launch your migrated eCommerce website
&lt;/h3&gt;

&lt;p&gt;Relaunching at a period of low traffic, such as early in the morning, could help you minimize any potential inconvenience. To give your users a better experience, you need to monitor analytics and constantly enhance your eCommerce website.&lt;/p&gt;

&lt;p&gt;Once your new site is live, keep checking for errors. It's not always a guarantee that doing everything perfectly in a test environment will protect you from mistakes in the real world. You shouldn't expect all the steps to go according to a plan. But if your team stays on top of any problems, you can minimize things like 404 errors and page misdirects.&lt;/p&gt;

&lt;h2&gt;
  
  
  eСommerce replatforming checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Check your current eCommerce platform and audit all issues&lt;/li&gt;
&lt;li&gt;Define your business needs, long-term goals, and platform requirements &lt;/li&gt;
&lt;li&gt;Decide what legacy information should be migrated&lt;/li&gt;
&lt;li&gt;Select the best time for migration (off-season, low-traffic, etc.)&lt;/li&gt;
&lt;li&gt;Consider the best platform options for your business&lt;/li&gt;
&lt;li&gt;Forecast your revenue and total cost of ownership&lt;/li&gt;
&lt;li&gt;Distinguish between built-in functionality and third-party apps&lt;/li&gt;
&lt;li&gt;Form a pre-migration website plan&lt;/li&gt;
&lt;li&gt;Migrate your data and content &lt;/li&gt;
&lt;li&gt;Create new website content&lt;/li&gt;
&lt;li&gt;Build website navigation and hierarchy&lt;/li&gt;
&lt;li&gt;Design front- and back-end (including mobile responsiveness)&lt;/li&gt;
&lt;li&gt;Conduct SEO and map URL redirects&lt;/li&gt;
&lt;li&gt;Relaunch in beta and QA with stakeholders&lt;/li&gt;
&lt;li&gt;Test before and after eCommerce replatforming&lt;/li&gt;
&lt;li&gt;Train personnel to help them use the new platform and its components&lt;/li&gt;
&lt;li&gt;Make your new website public &lt;/li&gt;
&lt;li&gt;Monitor traffic and search rankings&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Examples of effective eCommerce replatforming with instruments
&lt;/h2&gt;

&lt;p&gt;To actually start the migration, you need a new eCommerce platform meeting your requirements and is capable of storing your current data. When evaluating candidates, we recommend keeping the following key factors in mind:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ease of use:&lt;/strong&gt; You should learn beforehand how much time and effort you and your team will spend to master the new platform. This criterion will determine the scope and overall cost of replatforming.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Functionality:&lt;/strong&gt; Make sure the chosen platform has the features and tools you need to run your business effectively. The implemented time and smooth transition are also significant factors to consider. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security:&lt;/strong&gt; The platform must protect confidential information using PCI DSS compliance, fraud detection, and data encryption. It’s better to opt for a solution that offers such features out-of-the-box. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Support:&lt;/strong&gt; In case you need professional support, make sure you’ll get the necessary assistance 24/7.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; The overall pricing and cost of ownership can be trickier to calculate. To understand whether a particular platform fits within your budget, learn more about its maintenance and any other hidden costs (e.g. outsourcing expertise, consultants, monthly fees).  &lt;/p&gt;

&lt;p&gt;Considering these factors, nopCommerce is a perfect candidate for migration from your current eCommerce solution. It offers rich functionality, pluggable architecture, and compatibility with third-party apps and integrations – all these give you enough flexibility to create a personalized eCommerce experience for your customers. &lt;/p&gt;

&lt;p&gt;In addition, B2B eCommerce replatforming to nopCommerce is smooth and efficient, with no need to compromise any quality or functionality. This platform is made for scalability, so you can easily add new features and functions as your business grows.&lt;/p&gt;

&lt;p&gt;And don’t take our words for granted. To see for yourself that nopCommerce can help you solve critical issues and further develop business activity, take a look at a few eCommerce replatforming examples below.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zoomer: electronics retailer with successful store migration and redesign
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.nopcommerce.com/en/zoommer-store-migration-and-re-design-for-electronics-retailer"&gt;Zoomer&lt;/a&gt; is one of the biggest electronics retail chains in Georgia. Their online store had been using CS-Cart for about 8 years before switching to nopCommerce. Due to the vendor's absence from the local market and ineffective communication, maintenance was slow and difficult to manage.&lt;/p&gt;

&lt;p&gt;One of the main difficulties was integration with the accounting system. The website data updates also required a lot of work. The design was outdated, and adding features and supporting basic modules was almost impossible. In response to these issues, the business decided to replatform its website.&lt;/p&gt;

&lt;p&gt;As part of replatforming, the following changes were made:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A fresh, appealing, and mobile-friendly design was developed&lt;/li&gt;
&lt;li&gt;The home page now includes all search results under “Browsing History”&lt;/li&gt;
&lt;li&gt;Custom filters for the menu were added to only see the filter characteristics that are compatible with the chosen brand&lt;/li&gt;
&lt;li&gt;The new methods of payment were developed since online installment loans are very popular in Georgia&lt;/li&gt;
&lt;li&gt;More than 30k products have been transferred from the previous version of the website.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--85b0xETe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hrzv3vhnkbbmcuolcho3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--85b0xETe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hrzv3vhnkbbmcuolcho3.png" alt="Zoomer products list" width="850" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a result, the renewed website got a seamless integration with its price management system and customer-oriented design. 80,000 users visit the marketplace per week and the average session duration has increased to 4 minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Doors Direct: website migration from nopCommerce 4.30 to 4.40
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.nopcommerce.com/en/doors-direct-a-website-improvement-and-migration-journey-from-nopcommerce-43-to-44"&gt;Doors Direct&lt;/a&gt; is a popular online retailer of wooden windows and doors in South Africa. The company needed to upgrade its website to nopCommerce 4.40 while also making some changes to its custom solutions. &lt;/p&gt;

&lt;p&gt;In the process, the development team faced a number of challenges: the identification of all customizations, migration of the existing features, the implementation of new features, and lack of access to parts of the customized source code.&lt;/p&gt;

&lt;p&gt;While working on the new website, the team did the following: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developed a functional UX to enhance the website style, brand identity, and user experience&lt;/li&gt;
&lt;li&gt;Realized a more practical address selection and quote request to increase accessibility and functionality&lt;/li&gt;
&lt;li&gt;Showcased products to potential clients and directed them to physical stores&lt;/li&gt;
&lt;li&gt;Increased the number of new users by 10%.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p4Crt-_Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jwis5o6cvr8372p3an2m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p4Crt-_Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jwis5o6cvr8372p3an2m.jpg" alt="Doors Direct catalog" width="850" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Even if there are numerous reasons to be concerned about eCommerce replatforming, let's focus on the positive sides and what to expect when migration is done well.&lt;/p&gt;

&lt;p&gt;With replatforming, your product, category, and customer data is better organized, which enables more data-driven product development, gives cross and upsell opportunities, and even creates a more personalized shopping experience for your customers.&lt;/p&gt;

&lt;p&gt;A modern eCommerce solution will help your visitors enjoy faster load times and a custom checkout process tailored to their language, currency, and preferences. Going headless with replatforming also gives you access to other options for site performance and functionality (integrate new features and new services fairly quickly and easily) you wouldn't have with a monolithic platform.&lt;/p&gt;

&lt;p&gt;So as you can see, with a proper replatforming plan and a detailed roadmap, eCommerce migration doesn’t have to be a huge burden.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>tutorial</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to run the nopCommerce eCommerce platform on Linux VPS. Part 2</title>
      <dc:creator>Dmitry Kulagin</dc:creator>
      <pubDate>Mon, 13 Feb 2023 11:17:56 +0000</pubDate>
      <link>https://dev.to/dmitrykulagin2/how-to-run-the-nopcommerce-ecommerce-platform-on-linux-vps-part-2-5bcp</link>
      <guid>https://dev.to/dmitrykulagin2/how-to-run-the-nopcommerce-ecommerce-platform-on-linux-vps-part-2-5bcp</guid>
      <description>&lt;p&gt;Welcome to part 2 of Running nopCommerce on Linux, which has been composed by &lt;a href="https://www.pronopcommerce.com/"&gt;Woon Cherk Lam&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before we begin, please read &lt;a href="https://www.pronopcommerce.com/running-nopcommerce-on-linux-vps-1"&gt;part 1 of this tutorial&lt;/a&gt; if you have missed it. In part 1, we went over the basics of &lt;a href="https://docs.nopcommerce.com/en/installation-and-upgrading/installing-nopcommerce/installing-on-linux.html"&gt;installing nopCommerce on Linux&lt;/a&gt; in detail. And created a basic setup of nginx and MySQL to run nopCommerce on Linux.&lt;/p&gt;

&lt;p&gt;In this part of the tutorial, you will learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deploy a custom-developed application. How to build DLLs in Visual Studio that will run on Linux;&lt;/li&gt;
&lt;li&gt;run multiple instances of nopCommerce on the same server;&lt;/li&gt;
&lt;li&gt;convert an existing nopCommerce application that is running on Microsoft SQL to MySQL;&lt;/li&gt;
&lt;li&gt;install a free SSL certificate for your nopCommerce store.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start with the deployment of a custom-developed solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to deploy a custom-developed eCommerce project
&lt;/h2&gt;

&lt;p&gt;Whether you are developing a custom version of nopCommerce or just making slight changes, modifying any C# part of the code will require the application to be compiled again before uploading to the Linux server. Specifically, you have to compile for Linux. Because code compiled for Windows will not run on Linux servers.&lt;/p&gt;

&lt;p&gt;Let's start with the modified code in Visual Studio. In the Solutions Explorer window, select the Nop.Web project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1sUeJQ6V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wvef6ydcwn2tq3i2menh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1sUeJQ6V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wvef6ydcwn2tq3i2menh.png" alt="Nop.Web project" width="880" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right-click on Nop.Web. And in the context menu that appears, click "Publish".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DJnbpHEr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0rv41y7xu12ly1owc3vw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DJnbpHEr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0rv41y7xu12ly1owc3vw.png" alt="Publish" width="650" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's create a new publish profile by clicking on "New".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8-exV_Ko--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/auyd1ixgzc0hzz5oaslh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8-exV_Ko--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/auyd1ixgzc0hzz5oaslh.png" alt="new publish profile" width="550" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A window will pop up. Select "Folder" as the Target and click "Next".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KKWLp90X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dbm5svbgvl52onbsnqr2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KKWLp90X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dbm5svbgvl52onbsnqr2.png" alt="new publish profile folder" width="536" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next page, do not change the Folder Location. Click "Finish".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m_lBs5PS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1j29gqwfq7ms6gecqbcb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m_lBs5PS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1j29gqwfq7ms6gecqbcb.png" alt="new publish profile created" width="536" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can configure the publish profile for Linux. Click on the pencil icon to edit Target Runtime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q8__ZGQM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fazmlvj3d9coul9knq7p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q8__ZGQM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fazmlvj3d9coul9knq7p.png" alt="Target Runtime edit" width="550" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the window that pops up, set Deployment Mode to "Framework-dependent". And change Target Runtime to "linux-x64". As a side note, most VPS use linux-x64. Click "Save".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7uTreKa2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0z6o7d4u6f30ji6fx6x4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7uTreKa2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0z6o7d4u6f30ji6fx6x4.png" alt="linux-x64" width="650" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click "Publish".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TpKTp63C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cftg2n7nzx3g6l8pwm6m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TpKTp63C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cftg2n7nzx3g6l8pwm6m.png" alt="profile folder published" width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once it finishes publishing, a folder path containing the published files will be provided.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uvLyJOTL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wb26rykjfn63wuw7h3jz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uvLyJOTL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wb26rykjfn63wuw7h3jz.png" alt="folder path" width="871" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is always in the "bin" folder of the project you are working on. Unless you have changed the Folder Location for the publish profile. Then it will be in the folder you specified.&lt;/p&gt;

&lt;p&gt;As you can see the published files are in the "bin" folder. In Release &amp;gt; net6.0.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GBzvBD_S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/latf4oz4vvxmp945j5mc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GBzvBD_S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/latf4oz4vvxmp945j5mc.png" alt="bin folder" width="650" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We do not have to upload everything. We only need to upload the files that we have made changes to.&lt;/p&gt;

&lt;p&gt;For this tutorial, let's assume that I made some changes to the code in Nop.Web. Therefore after publishing, I only need to upload the Nop.Web.dll file to my server.&lt;/p&gt;

&lt;p&gt;As usual, I will use MobaXterm to upload the file. But feel free to use any SFTP client you are comfortable with.&lt;/p&gt;

&lt;p&gt;In the terminal, move into the nopCommerce application's folder. For me, it will be:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd /var/www/nop430-1-demo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, I'll make sure the "Follow terminal folder" is enabled and check if the SFTP path is correct.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4gWIJSTt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z11hpwjxxbadhwp7r30p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4gWIJSTt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z11hpwjxxbadhwp7r30p.png" alt="enabled Follow terminal folder" width="880" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I will drag and drop the Nop.Web.dll file into the SFTP tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sfPuCRn_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fz26j937swzdth1ahba9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sfPuCRn_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fz26j937swzdth1ahba9.png" alt="Nop.Web.dll file into the SFTP tab" width="880" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the DLL file has been uploaded, we will need to restart the nopCommerce instance. The command to do that is "sudo systemctl restart" followed by your nopCommerce service name. In my case, it is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo systemctl restart nop430-1-demo.service&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After restarting, nopCommerce will take in the updated DLL file. The custom-developed version of nopCommerce is now deployed and running on Linux.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run multiple eCommerce stores on a single server
&lt;/h2&gt;

&lt;p&gt;In this section, we will go through the setup required to run several instances of nopCommerce on the same server. It is a convenient way of running any combination of nopCommerce versions. You can have some instances running the same version and others running different versions on the same server.&lt;/p&gt;

&lt;p&gt;In this example, I have three instances of nopCommerce installed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oerO2MG0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zsmte5p01e2agpxo9pfk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oerO2MG0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zsmte5p01e2agpxo9pfk.png" alt="three instances of nopCommerce" width="450" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first instance I set up is nop430-demo. I left it as it was and did not change the listening port. Therefore, nopCommerce will by default listen on port 5000.&lt;/p&gt;

&lt;p&gt;Now, let's pretend I'm in the process of setting up the next instance which is nop430-1-demo. And I have completed all the steps in part 1 of this tutorial. At this point, nop430-1-demo is ready but cannot run because the first application is also listening on the same default port 5000.&lt;/p&gt;

&lt;p&gt;As part of setting up a new instance in part 1 of this tutorial, we had to create a nopCommerce service for nginx to route requests to. Let's go to the directory we placed the .service file in.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd /etc/systemd/system/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Do refer to the previous tutorial if you are unsure of anything. It is important that you understand and follow all the steps in part 1 of this tutorial.&lt;/p&gt;

&lt;p&gt;I will run the list command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ls&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a7GpTqX6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0u8oiwl98auu2azp9q3k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a7GpTqX6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0u8oiwl98auu2azp9q3k.png" alt="create a nopCommerce service for nginx" width="650" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the configuration file is named after the application it is for, we can easily spot it in the list: nop430-1-demo.service. Let's open the file in Nano.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo nano nop430-1-demo.service&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now we just have to add this line to specify the listening port.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Environment=ASPNETCORE_URLS=http://localhost:5031&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JplBXKJz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/55l31lpwmqftz7frz8kp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JplBXKJz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/55l31lpwmqftz7frz8kp.png" alt="listening port" width="450" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the previous tutorial, it was not necessary to set a specific listening port because there was only one nopCommerce running. It will automatically use the default port 5000. However, we are now adding a second instance to the same server. And only one application can be on a port at a time. So it is necessary to assign a different port to the additional instance.&lt;/p&gt;

&lt;p&gt;In my case, I have assigned nop430-1-demo, my second application to listen on port 5031. This leaves my first application to listen on the default port 5000.&lt;/p&gt;

&lt;p&gt;Press Ctrl+S to save the changes and Ctrl+X to exit Nano.&lt;/p&gt;

&lt;p&gt;The next step is to start the service with this command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo systemctl start nop430-1-demo.service&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;nop430-1-demo should now be listening to port 5031. We can run another command to check, which application is listening to which port.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo netstat -tunlp&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jctQ5Exo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kx0ipud0mgykkbhhd5bu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jctQ5Exo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kx0ipud0mgykkbhhd5bu.png" alt="check listening port" width="650" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will list all the listening ports and the applications that are listening on them. What I am interested in are the applications with the program name containing "dotnet" which means it is a nopCommerce application. As you can see, my nop430-1-demo is listening on port 5031.&lt;/p&gt;

&lt;p&gt;Repeat the above steps when installing additional instances on the same server. The applications will run fine as long as each application is assigned its own listening port.&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting a running eCommerce store from MS SQL to MySQL
&lt;/h2&gt;

&lt;p&gt;Converting the database from MS SQL to MySQL is a crucial step when moving an existing nopCommerce application from Windows hosting to Linux hosting. I have tested a few software tools and have found &lt;a href="https://www.convert-in.com/mss2sql.htm"&gt;MSSQL-to-MySQL by Intelligent Converters&lt;/a&gt; to be the most stable and straightforward.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e70nLf9X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zty0k2b2jmig5jzg9cq6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e70nLf9X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zty0k2b2jmig5jzg9cq6.png" alt="MSSQL-to-MySQL by Intelligent Converters" width="650" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what works for me may not work for you. Database system conversion is not an easy task. Especially if your database is a customized version of nopCommerce. You will have to test it to see if it works for your version of the database and application.&lt;/p&gt;

&lt;p&gt;For this tutorial, I will be using MSSQL-to-MySQL. It is not expensive and there are discounts available if you are thinking of getting it. I will explain more about how to get discounts later.&lt;/p&gt;

&lt;p&gt;Let's start by opening MSSQL-to-MySQL. We will be greeted by the migration wizard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y3E-gIvb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/798kq7pjekeupysjon6s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y3E-gIvb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/798kq7pjekeupysjon6s.png" alt="MSSQL-to-MySQL migration wizard" width="650" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select "Export into MySQL script file" and click “Next”.&lt;/p&gt;

&lt;p&gt;MSSQL-to-MySQL will then ask for information to connect to your MS SQL server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lL4zgR1x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b8sv6et3cv21xtptddv1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lL4zgR1x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b8sv6et3cv21xtptddv1.png" alt="MSSQL-to-MySQL step 1" width="650" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is my practice to work on a local database. Even for a production website where the database is hosted on a live server, I will back up and restore the database to my local server and make changes locally. It is much faster this way. So I will leave it as “localhost” to connect to my local server. And then click “Next”.&lt;/p&gt;

&lt;p&gt;On the next page, I will leave the default settings here unchanged except for "Insert 'CREATE TABLE' statements".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KjoYsxcF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7trgszn7jmqp5njofq2h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KjoYsxcF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7trgszn7jmqp5njofq2h.png" alt="MSSQL-to-MySQL step 2" width="650" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When enabled, the script will create all the tables when executing on the MySQL database. If you have not created a MySQL database in your destination server, you can tick the "Insert 'CREATE DATABASE' statement" and have the script do it for you. I will leave it unchecked as I have already created a MySQL database on my server.&lt;/p&gt;

&lt;p&gt;Next, we have to choose the MS SQL database that we want to convert and also where we want to save the MySQL script file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FYmFkVrh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cwkbp0z1r4bkhpfnruy8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FYmFkVrh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cwkbp0z1r4bkhpfnruy8.png" alt="MSSQL-to-MySQL step 3" width="650" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following that is the Conversion Settings page. Leave the default conversion settings unchanged and click “Next”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4eGRr0b7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cm9c1sv0p3kuy9o3pveg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4eGRr0b7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cm9c1sv0p3kuy9o3pveg.png" alt="MSSQL-to-MySQL step 4" width="650" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next page, select all the tables to be converted. Click on "Add All &amp;gt;" and then click “Next”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n-MbsYkL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ap0cs4alii2t4s91hed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n-MbsYkL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ap0cs4alii2t4s91hed.png" alt="MSSQL-to-MySQL step 5" width="650" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now MSSQL-to-MySQL will start converting the database and generate the MySQL script file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RnTAg0mV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ac4wdn01pjv79r31hzh8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RnTAg0mV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ac4wdn01pjv79r31hzh8.png" alt="MSSQL-to-MySQL step 7" width="650" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the conversion is done, we will have a MySQL script file that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9XSTgZpx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xcg9zru7ia70pj33oo6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9XSTgZpx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xcg9zru7ia70pj33oo6l.png" alt="MySQL script file" width="880" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step is to upload this script file to the server and execute it on the MySQL database.&lt;/p&gt;

&lt;p&gt;I will open the SSH terminal in MobaXterm and navigate to the folder I want to upload the MySQL script file. To keep things organized, I usually upload files to the App_Data folder of the application I'm working on. Right now, I am working on the MySQL database of my nop430-1-demo. So I will move to that directory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd /var/www/nop430-1-demo/App_Data&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Make sure the "Follow terminal folder" in the SFTP tab is enabled. And check if the path in the SFTP is correct.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5-KfO3l2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nx78ynupwup9lme66cry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5-KfO3l2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nx78ynupwup9lme66cry.png" alt="Follow terminal folder in the SFTP" width="880" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then just drag and drop the script file into the SFTP tab.&lt;/p&gt;

&lt;p&gt;Once the upload is complete, go back to the SSH terminal. In the folder that contains the script file, run this command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mysql&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will launch the MySQL process on Linux.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xNR6zfLu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vnle8ncpdhy34fnovvi2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xNR6zfLu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vnle8ncpdhy34fnovvi2.png" alt="the MySQL process on Linux" width="447" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have already created a MySQL database called "import_demo" to receive the data. So I will select it as the database I want to import into by entering the command "USE" followed by the database name.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;USE import_demo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Press Enter to run the command. Then you will see "Database changed".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PA8QLzJX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xv7q0gi3vodgjpuex7dt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PA8QLzJX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xv7q0gi3vodgjpuex7dt.png" alt="Database changed" width="346" height="55"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a reminder, it is crucial that you are running the MySQL shell in the same directory the uploaded script file is in. We can now tell MySQL to execute the script file on my selected database with this command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;source nophippie-partial.sql&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Just replace "nophippie-partial.sql" with the name of your script file when you are trying this. Press Enter to run the command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lbO5UMBY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w04f3ssa4w3pu3feynvl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lbO5UMBY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w04f3ssa4w3pu3feynvl.png" alt="your script file" width="491" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When all the queries are executed, the database has been successfully imported. We can quit the MySQL shell by typing "quit" and pressing Enter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yFq1CfSZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eo8zvup77dhu82l6mkoa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yFq1CfSZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eo8zvup77dhu82l6mkoa.png" alt="quitting the MySQL shell" width="392" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to check and make sure that the dataSettings.json is pointing to the database we have just imported. The location of dataSettings.json is in our nopCommerce application’s App_Data folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R5DqFkpN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xytf0zcqy87zv6ffmhpy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R5DqFkpN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xytf0zcqy87zv6ffmhpy.png" alt="nopCommerce application’s App_Data folder" width="579" height="61"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I am already in the correct directory, I can open the file in nano.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo nano dataSettings.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Under DataConnectionString, make sure it is pointing to the database we have just imported.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FO212ifH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ci6xjft0ojkldd2folbb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FO212ifH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ci6xjft0ojkldd2folbb.png" alt="DataConnectionString" width="842" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we are done. That is how to convert a database from MS SQL to MySQL from start to finish.&lt;/p&gt;

&lt;p&gt;If you are interested in a discount for MSSQL-to-MySQL by Intelligent Converters, you can go to their "&lt;a href="https://www.convert-in.com/discounts.htm"&gt;How to Get a Discount&lt;/a&gt;" page. Also, I can organize a group purchase deal to get 50% off under their "for 3rd and each following purchase" option. Just send me your email and we can purchase all the licenses in one go.&lt;/p&gt;

&lt;h2&gt;
  
  
  A free SSL certificate for running .NET eCommerce store
&lt;/h2&gt;

&lt;p&gt;In MobaXterm, run the following command in the terminal to install Certbot.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo apt install certbot python3-certbot-nignx&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You will be prompted to confirm if you want to install Certbot. Press "Y" to confirm.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hvvNW1U---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bbjm5orcb8pycz2nsnrp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hvvNW1U---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bbjm5orcb8pycz2nsnrp.png" alt="Certbot" width="650" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you already have it installed as I do, you will see this screen instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F1D1cPsK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h8jaffmf25b8w8den292.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F1D1cPsK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h8jaffmf25b8w8den292.png" alt="installed Certbot" width="650" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have multiple nopCommerce instances, you will need to do SSL binding for each individual instance.&lt;/p&gt;

&lt;p&gt;Now, let's have a look at the installations I have by going to the default server.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd /var/www/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And running the "ls" command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HgLST_k0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w8ilsc345cy4ttk3xq4a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HgLST_k0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w8ilsc345cy4ttk3xq4a.png" alt="SSL certificates for multiple nopCommerce instances" width="650" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, I have four installations here.&lt;/p&gt;

&lt;p&gt;For this tutorial, the application I want to bind SSL to is "nop430-demo". To do that we need to look for the hostname in the nginx configuration file for that application.&lt;/p&gt;

&lt;p&gt;Let's go to the "sites-available" folder.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd /etc/nginx/sites-available&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And run the "ls" command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XcQbuJB1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wv5if9ttu4xs7m32gigl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XcQbuJB1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wv5if9ttu4xs7m32gigl.png" alt='"sites-available" folder' width="698" height="138"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I have named my configuration file after the application it's for, I can tell at a glance that "nop430-demo" is the file I'm looking for. Let's open it in Nano.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nano nop430-demo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Under "server", "server_names" contains the hostnames we are looking for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0uE8UbPN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vyefwbvtbxw7pyuu7fc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0uE8UbPN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vyefwbvtbxw7pyuu7fc.png" alt="hostnames" width="880" height="773"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reason I have more than one hostname is that my application is a multi-store installation. With NopCommerce it is possible to have more than one store in an installation. You can even have multiple installations of different instances hosted on the same Linux hosting (more on this in the next section). Here's an example to illustrate the hierarchy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t03qAtfT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nfmt71f789nebjtywbya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t03qAtfT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nfmt71f789nebjtywbya.png" alt="the hierarchy of multiple installations" width="650" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, it is possible to have two instances running, each containing a multi-store installation. Additionally, it is also possible to run different versions of nopCommerce in each separate instance. In the example above, Instance (1) is running nop4.40 while Instance (2) is running nop4.50.&lt;/p&gt;

&lt;p&gt;Anyway, coming back to the hostnames. Now you can tell Let's Encrypt the domain names we want to bind the SSL to. So I will select the hostnames.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_wrE4MN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3rifdcw9qh5chnmj4uc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_wrE4MN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3rifdcw9qh5chnmj4uc2.png" alt="telling Let's Encrypt the domain names" width="880" height="85"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then left-click on the selection to copy. Do not press Ctrl+C to copy! In MobaXterm, pressing Ctrl+C will interrupt or kill the current task or program.&lt;/p&gt;

&lt;p&gt;Let's exit Nano by pressing CTRL+X.&lt;/p&gt;

&lt;p&gt;Next, you will use Certbot which we installed earlier to communicate with Let's Encrypt and complete the SSL binding. The command you need to use looks like this example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo certbot --nginx -d example.com -d www.example.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We have to enter the domains we want to bind with a "-d" followed by a space in front of each domain. So I will type out the command, right-click to paste the hostnames, and then add "-d" followed by a space before each hostname. The resulting command looks like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo certbot –nginx -d nop430-demo.pronopcommerce.com -d nopmoka-demo.pronopcommerce.com -d nopwinery-demo.pronopcommerce.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If your setup is not a multi-store setup, you should only have one hostname. But do note that if you wish to bind both the WWW URL and the non-WWW URL, you will have to enter both domains in the command like so:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo certbot --nginx -d yourstore.com -d www.yourstore.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Since all my hostnames are subdomains, I don't have any WWW URLs to add. I will proceed by pressing Enter to execute the command.&lt;/p&gt;

&lt;p&gt;Certbot will start running and prompt us for an email address.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YiYrqygY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4to9085wqdge759mpfs9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YiYrqygY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4to9085wqdge759mpfs9.png" alt="Certbot asks for email address" width="650" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The email address you enter here will be used by Let's Encrypt to send you notifications about your SSL renewal and security issues.&lt;/p&gt;

&lt;p&gt;After entering the email address, press Enter to submit.&lt;/p&gt;

&lt;p&gt;Next is Let's Encrypt's terms of service. Press "A" to agree to the terms of service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hyHo8F9q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/07smepegkzjo6fgh3url.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hyHo8F9q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/07smepegkzjo6fgh3url.png" alt="Let's Encrypt's terms of service" width="650" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following that we are asked if we want to share our email address with the Electronic Frontier Foundation. This is optional. I do not want to, so I will press "N" for no.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p0pg8cwK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4pl3fhqr373sugald2df.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p0pg8cwK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4pl3fhqr373sugald2df.png" alt="the Electronic Frontier Foundation" width="650" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Certbot will continue running for a while.&lt;/p&gt;

&lt;p&gt;Lastly, Certbot will ask if you want to redirect all HTTP traffic to HTTPS. Depending on your needs you may want to do that. If so, press "2" and hit Enter. For me, I don't want Certbot to make any changes. So I will press "1" for No, followed by Enter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jZmMf-Oi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rhbt90y4sqgs6bdztkq8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jZmMf-Oi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rhbt90y4sqgs6bdztkq8.png" alt="redirect all HTTP traffic to HTTPS" width="650" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Certbot will proceed to finish up. And you should see a message that says your SSL has been successfully enabled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gfoXya7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i4uvwlvfh7d1shhjjc36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gfoXya7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i4uvwlvfh7d1shhjjc36.png" alt="SSL successfully enabled" width="650" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step is to make a small change to the appsettings.json file for your nopCommerce website. This is important and will result in a redirect loop if you do not do it.&lt;/p&gt;

&lt;p&gt;Let's go to the root folder of the website, in my case it is "/var/www/nop430-demo".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OzIkpsYK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ogtnqxf0ntsivl5osl1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OzIkpsYK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ogtnqxf0ntsivl5osl1x.png" alt="root folder of the website" width="650" height="61"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you will open the appsettings.json file in Nano.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nano appsettings.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the "DOTNET_URLS" section, look for "UseHttpXForwardedProto".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vhkh4l5h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfms9n49ok2dpa2rj821.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vhkh4l5h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfms9n49ok2dpa2rj821.png" alt="UseHttpXForwardedProto" width="650" height="171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The default value for "UseHttpXForwardedProto" is false. We need to change that to true.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mw-E7TwX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ufs7wv7wynvdaom0t37v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mw-E7TwX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ufs7wv7wynvdaom0t37v.png" alt="true value for UseHttpXForwardedProto" width="650" height="171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press Ctrl+S to save the file and Ctrl+X to exit Nano.&lt;/p&gt;

&lt;p&gt;After editing appsettings.json, we need to restart our app for the changes to take effect. The restart command is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;systemctl restart yourappname.service&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For my case it is this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ITyPE0jh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9v18bq0tdtwja485zx8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ITyPE0jh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9v18bq0tdtwja485zx8i.png" alt="restarting your project" width="650" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press Enter to run the command. It can take around two minutes for the service to restart depending on your VPS. Using MobaXterm, you can tell it has finished restarting when the CPU usage has stopped spiking.&lt;/p&gt;

&lt;p&gt;Once successfully restarted, we need to tell nopCommerce to enable SSL for our stores. Log in to the nopCommerce Admin page or refresh it if you have it opened. And then go to Configuration &amp;gt; Stores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7M5Z-el7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s51tmtha7p8udenwppb8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7M5Z-el7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s51tmtha7p8udenwppb8.png" alt="nopCommerce Admin page" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on Edit for the store, for which you want to enable SSL. In my case, I will have to edit both NopWinery Demo and NopMoka Demo. I'll start with NopWinery Demo and click the Edit button for that store.&lt;/p&gt;

&lt;p&gt;In the Edit Store Details page, change the store URL from HTTP to HTTPS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k7JLFsRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gc80mkfty8zac1gsrbt4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k7JLFsRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gc80mkfty8zac1gsrbt4.png" alt="the Edit Store Details page" width="650" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then tick the "SSL enabled" checkbox.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W25sWqYd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kh1bpxvwjo2mow7ahyxt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W25sWqYd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kh1bpxvwjo2mow7ahyxt.png" alt="SSL enabled checkbox" width="650" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then click Save.&lt;/p&gt;

&lt;p&gt;I made the same changes to my other store. If you run a multi-store installation like me, you will have to edit each of your stores to enable SSL for them.&lt;/p&gt;

&lt;p&gt;With that, we have successfully installed and enabled our free SSL certificate on our nopCommerce stores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;As you follow these steps, any nopCommerce project may be deployed using the Linux operating system. From SSL Certificate installation to deploying customized parts can be easily performed.&lt;/p&gt;

&lt;p&gt;Moreover, many instances may be run on the same server which hugely optimizes the costs of creating and managing an eCommerce store by nopCommerce. Considering the first part of the tutorial, server expenses may be even less by using suggested Linux VPS, which are usually cheaper than Windows ones.&lt;/p&gt;

&lt;p&gt;Also, refer to the documentation where you can find more descriptive information about the initial settlement of nopCommerce using Linux OS.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>dotnet</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Migration from .NET5 to .NET 6: Performance benchmarks</title>
      <dc:creator>Dmitry Kulagin</dc:creator>
      <pubDate>Tue, 08 Feb 2022 10:03:02 +0000</pubDate>
      <link>https://dev.to/dmitrykulagin2/migration-from-net5-to-net-6-performance-benchmarks-5e95</link>
      <guid>https://dev.to/dmitrykulagin2/migration-from-net5-to-net-6-performance-benchmarks-5e95</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This post continues the series of articles (&lt;a href="https://www.nopcommerce.com/migration-from-net-core-22-to-net-core-31-nopcommerce-experience" rel="noopener noreferrer"&gt;Migration from .NET Core 2.2 to .NET Core 3.1&lt;/a&gt; and &lt;a href="https://dev.to/dmitrykulagin2/how-to-migrate-a-project-from-asp-net-mvc-to-asp-net-core-2368"&gt;How to Migrate a Project From ASP.NET MVC to ASP.NET Core&lt;/a&gt;) on upgrading the nopCommerce project – a free . net eCommerce CMS with an open source code for creating online stores. This article discusses why we continuously strive to upgrade our application’s platform. Here we tell you what we achieved with migration to .NET 6 in terms of performance and try to evaluate our results practically.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reasons for migration
&lt;/h2&gt;

&lt;p&gt;Those who monitor the nopCommerce upgrades know well that we do our best to upgrade the application core as promptly as possible. This is facilitated by the continuously evolving Microsoft technologies reflected in the .NET Core platform.&lt;br&gt;
One of the primary objectives is switching to the long-term support (LTS) release. This is because the current .NET 5 version is to be supported and debugged for 1.5 years since the release date. The upgraded nopCommerce 4.50 version is already based on the .NET 6 platform with a 3 years support, which is crucial for the end users and will provide the community with sufficient time to update their stores and plugins for them. That being said, expectations of the following version will be accompanied by regular upgrades, including security ones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xi23mn56hqtdsl294gd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xi23mn56hqtdsl294gd.png" alt=".NET LTS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the .NET 6 release, the Microsoft developers have brought .NET most closely to the single platform for developing all the application types present, starting with desktop and web applications, and ending with mobile apps development. Such a unification allows us to release a mobile application for nopCommerce in a single C# codebase, which will undoubtedly benefit our developer community in terms of immersion and understanding the entire nopCommerce ecosystem.&lt;br&gt;&lt;br&gt;
Also, we planned to raise the performance bar of our application with the switch to .NET 6. Going ahead, the measurement results revealed some interesting data regarding the nopCommerce evolution, and we would like to share them.&lt;/p&gt;

&lt;h2&gt;
  
  
  .NET 6 performance benchmarks in the application
&lt;/h2&gt;

&lt;p&gt;To measure load and scalability, we chose a quite popular SaaS service, loader.io, as the test tool. An average machine for development was used as the server with the deployed application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU - Intel(R) Core(TM) i5-7400 CPU 3.00 GHz&lt;/li&gt;
&lt;li&gt;RAM - 16.0 GB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tests were managed using Windows 10 (19044.1415) and IIS 10 (10.0.19041.1415). We used a standard database provided at the application setup. All of these were managed using MS SQL Server 2019 (19.0.2000). &lt;br&gt;
To simulate the load, we chose the test configuration that would connect 250 users during a minute. The users would visit a few random application pages. These would be sufficient to provide the general picture of the platform response and visualize the difference relative to the previous versions.&lt;/p&gt;

&lt;p&gt;Further, I will present the correspondence table with the nopCommerce application and .NET platform versions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nopCommerce&lt;/th&gt;
&lt;th&gt;4.30&lt;/th&gt;
&lt;th&gt;4.40.4&lt;/th&gt;
&lt;th&gt;4.50&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;.NET&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;.NET Core 3.1&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;.NET 5&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;.NET 6&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We made 3 consecutive load tests to gain insights into the load nature and memory consumption on the server. The average results from several runs are given below.&lt;br&gt;
Response time measurement (the less, the better). There is an evident trend in performance improvement compared to .NET 5 and .NET Core 3.1 versions: 46.1% and 65.3%, respectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl40ff0gjsnemkgmthjpr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl40ff0gjsnemkgmthjpr.png" alt="Average response time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Measurement of the memory usage by the application (the less, the better). The tests were run in succession for each platform without resetting the application pool. It should be considered that the .NET platform’s memory overhead has increased; this is apparently the inevitable cost of the enhanced performance. However, we must note that the optimization promised in .NET 6 can be seen here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmh2umjwoe493ui7vvmm5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmh2umjwoe493ui7vvmm5.png" alt="Memory consumption"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  .NET 6 vs .NET 5: Load distribution
&lt;/h3&gt;

&lt;p&gt;Let’s now consider the load distribution in the nopCommerce platform with .NET 6 in detail and compare it with the same test data for .NET 5. Importantly, here, we are showing the general trend for the system working on different platforms rather than revealing the limit values at which the system would go off balance because this is largely dependent on the technical data of the server under test.&lt;/p&gt;

&lt;h4&gt;
  
  
  Load distribution in .NET 6
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zm5bgmuihy0fbnwg5zz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zm5bgmuihy0fbnwg5zz.png" alt="Load distribution in .NET 6"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Load distribution in .NET 5
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fami8cxspn752uha89jsq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fami8cxspn752uha89jsq.png" alt="Load distribution in .NET 5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Analyzing this, we can see an interesting pattern from the .NET 6 benchmarks: with an increasing number of clients per second, the average response time in the plot maintains a sufficient distance from that of .NET 5. The upgraded version’s curve is smoother and has fewer bursts. Another good indicator is that the curves don’t cross each other during the entire test. This is indirect evidence of the enhanced performance. .NET 6 handles the test more easily than .NET 5 does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Once again, we have made sure that following the latest technological advances by Microsoft is beneficial. Apart from the improved performance, we got an upgraded platform that will allow us to move in a new direction. This is, of course, the mobile application: something we started heading to back in version 4.40 by implementing Web API that covers literally all the functions provided by our platform. We have slightly more memory overhead in favor of performance improvement: this should be considered when choosing the hosting provider. However, since the current version will be dominant among the others in our community in the mid-term, switching to it seems the most practical solution.&lt;/p&gt;

&lt;p&gt;Learn more on the &lt;a href="https://www.nopcommerce.com/download-nopcommerce" rel="noopener noreferrer"&gt;.net open-source platform's website&lt;/a&gt; or visit &lt;a href="https://github.com/nopSolutions/nopCommerce" rel="noopener noreferrer"&gt;our GitHub repository&lt;/a&gt;   &lt;/p&gt;

</description>
      <category>opensource</category>
      <category>dotnet</category>
      <category>performance</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Migrate a Project From ASP.NET MVC to ASP.NET Core</title>
      <dc:creator>Dmitry Kulagin</dc:creator>
      <pubDate>Tue, 12 Nov 2019 15:56:42 +0000</pubDate>
      <link>https://dev.to/dmitrykulagin2/how-to-migrate-a-project-from-asp-net-mvc-to-asp-net-core-2368</link>
      <guid>https://dev.to/dmitrykulagin2/how-to-migrate-a-project-from-asp-net-mvc-to-asp-net-core-2368</guid>
      <description>&lt;h2&gt;
  
  
  Step-by-step guide
&lt;/h2&gt;

&lt;p&gt;Here is a practical guide on migrating a project from ASP.NET MVC framework to ASP.NET Core. Step-by-step instruction written by the team of &lt;a href="https://www.nopcommerce.com/"&gt;nopCommerce&lt;/a&gt; open-source project can be easily applied to any ASP.NET MVC project. It also describes why you might need to upgrade, and why projects that do not yet keep up with pace should consider it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why porting to ASP.NET Core
&lt;/h2&gt;

&lt;p&gt;Before proceeding to the steps of porting from ASP.NET MVC to ASP.NET Core (using nopCommerce as an example), let's take the quick overview of this framework advantages.&lt;/p&gt;

&lt;p&gt;ASP.NET Core is already a fairly well-known and developed framework with several major updates making it quite stable, technologically advanced and resistant to XSRF/CSRF attacks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EA6RXEt9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3hr206qrfcsuyc6iveet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EA6RXEt9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3hr206qrfcsuyc6iveet.png" alt="Alt Text" width="818" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cross-platform is one of the distinguishing features making it more and more popular. From now on, your web application can run both in Windows and Unix environment.&lt;/p&gt;

&lt;p&gt;Modular architecture - ASP.NET Core comes fully in the form of NuGet packages, it allows optimizing the application, including the selected required packages. This improves solution performance and reduces the time it takes to upgrade separate parts. This is the second important characteristic that allows developers to integrate new features into their solution more flexible.&lt;/p&gt;

&lt;p&gt;Performance is another step towards building a high-performance application. ASP.NET Core handles 2.300% more requests per second than ASP.NET 4.6, and 800% more requests per second than node.js. You can check the detailed performance tests yourself &lt;a href="https://github.com/aspnet/benchmarks"&gt;here&lt;/a&gt; or &lt;a href="https://www.techempower.com/benchmarks/#section=test&amp;amp;runid=8ca46892-e46c-4088-9443-05722ad6f7fb&amp;amp;hw=ph&amp;amp;test=plaintext"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OkkiKrWv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/wqpoyno9yhz91hjh14v1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OkkiKrWv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/wqpoyno9yhz91hjh14v1.png" alt="Alt Text" width="880" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Middleware is a new light and fast modular pipeline for in-app requests. Each part of middleware processes an HTTP request, and then either decides to return the result or passes the next part of middleware. This approach gives the developer full control over the HTTP pipeline and contributes to the development of simple modules for the application, which is important for a growing open-source project.&lt;/p&gt;

&lt;p&gt;Also, ASP.NET Core MVC provides features that simplify web development. nopCommerce already used some of them, such as the Model-View-Controller template, Razor syntax, model binding, and validation. Among the new features are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Tag Helpers. Server-part code for participation in creating and rendering HTML elements in &lt;code&gt;Razor&lt;/code&gt; files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;View components. A new tool, similar to partial views, but of much higher performance. nopCommerce uses view components when reusing rendering logic is required and the task is too complex for partial view.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DI in views. Although most of the data displayed in views comes from the controller, nopCommerce also has views where dependency injection is more convenient.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, ASP.NET Core has much more features, we viewed the most interesting ones only.&lt;br&gt;
Now let's consider the points to keep in mind when porting your app to a new framework.&lt;/p&gt;
&lt;h2&gt;
  
  
  Migration
&lt;/h2&gt;

&lt;p&gt;The following descriptions contains large amount of links to the official ASP.NET Core documentation to give more detailed information about the topic and guide developers who face such a task the first time.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1. Preparing a toolkit
&lt;/h3&gt;

&lt;p&gt;The first thing you need is to upgrade Visual Studio 2017 to version 15.3 or later and install the latest version of .NET Core SDK.&lt;/p&gt;

&lt;p&gt;Before porting, we advise to use &lt;a href="https://docs.microsoft.com/dotnet/standard/analyzers/portability-analyzer"&gt;.Net Portability Analyzer&lt;/a&gt;. This can be a good starting point to understand how labor-intensive porting from one platform to another will be. Nevertheless, this tool does not cover all the issues, this process has many pitfalls to be solved as they emerge. Below we will describe the main steps and the solutions used in nopCommerce project.&lt;/p&gt;

&lt;p&gt;And the first and the easiest thing to do is to update links to the libraries used in the project so to they support .NET Standard.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2. NuGet package compatibility analysis to support .Net standard
&lt;/h3&gt;

&lt;p&gt;If you use NuGet packages in your project, check whether they are compatible with .NET Core. One way to do this is to use the &lt;a href="https://github.com/NuGetPackageExplorer/NuGetPackageExplorer"&gt;NuGetPackageExplorer&lt;/a&gt; tool.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3. The new format of csproj file in .NET Core
&lt;/h3&gt;

&lt;p&gt;A new approach for adding references to third-party packages was introduced in .NET Core. When adding a new class library, you need to open the main project file and replace its contents as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp2.2&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.AspNetCore.App"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"2.2.6"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;References to the connected libraries will be loaded automatically. For more information on comparing the &lt;code&gt;project.json&lt;/code&gt; and &lt;code&gt;CSPROJ&lt;/code&gt; properties, read the official documentation &lt;a href="https://docs.microsoft.com/dotnet/core/tools/project-json-to-csproj"&gt;here&lt;/a&gt; and &lt;a href="https://docs.microsoft.com/dotnet/core/tools/csproj"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4. Namespace update
&lt;/h3&gt;

&lt;p&gt;Delete all uses of &lt;code&gt;System.Web&lt;/code&gt; and replace them with &lt;code&gt;Microsoft.AspNetCore&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5. Configure the &lt;code&gt;Startup.cs&lt;/code&gt; file instead of using &lt;code&gt;global.asax&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;ASP.NET Core has a new way of loading the app. The app entry point is &lt;code&gt;Startup&lt;/code&gt;, and there is no dependency on the &lt;em&gt;Global.asax&lt;/em&gt; file. &lt;code&gt;Startup&lt;/code&gt; registers the middleware in the app. &lt;code&gt;Startup&lt;/code&gt; must include the &lt;code&gt;Configure&lt;/code&gt; method. The required middleware should be added to the pipeline in &lt;code&gt;Configure&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Issues to solve in &lt;code&gt;Startup.cs&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuring middleware for MVC and WebAPI requests&lt;/li&gt;
&lt;li&gt;Configuring for:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/aspnet/core/fundamentals/error-handling?view=aspnetcore-2.2"&gt;Exception handling&lt;/a&gt;
You will inevitably face various collisions during porting, thus be ready and set up exception handling in the development &lt;a href="https://docs.microsoft.com/aspnet/core/fundamentals/environments?view=aspnetcore-2.2"&gt;environment&lt;/a&gt;. With &lt;a href="https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.builder.developerexceptionpageextensions.usedeveloperexceptionpage?view=aspnetcore-3.0"&gt;UseDeveloperExceptionPage&lt;/a&gt;, we add middleware to catch exceptions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/aspnet/core/fundamentals/routing?view=aspnetcore-2.2"&gt;MVC routing&lt;/a&gt;.
&lt;a href="https://docs.microsoft.com/aspnet/core/mvc/controllers/routing?view=aspnetcore-2.2"&gt;Registration of new routes&lt;/a&gt; has also been changed. IRouteBuilder is now used instead of RouteCollection, as a new way to &lt;a href="https://docs.microsoft.com/aspnet/core/mvc/controllers/routing?view=aspnetcore-2.2#understanding-iactionconstraint"&gt;register restrictions&lt;/a&gt;  (IActionConstraint)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2"&gt;MVC/WebAPI filters&lt;/a&gt;. The filters should be updated in accordance with the &lt;a href="https://docs.microsoft.com/aspnet/core/mvc/controllers/filters?view=aspnetcore-3.0"&gt;new implementation of ASP.NET Core&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/aspnet/core/web-api/advanced/custom-formatters?view=aspnetcore-2.2"&gt;MVC/WebAPI Formatters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/aspnet/core/mvc/advanced/custom-model-binding?view=aspnetcore-2.2"&gt;Binding models&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//add basic MVC feature&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mvcBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMvc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;//add custom model binder provider (to the top of the provider list)&lt;/span&gt;
&lt;span class="n"&gt;mvcBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMvcOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelBinderProviders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;  &lt;span class="nf"&gt;NopModelBinderProvider&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Represents model binder provider for the creating NopModelBinder&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NopModelBinderProvider&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IModelBinderProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Creates a nop model binder based on passed context&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="context"&amp;gt;Model binder provider context&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;Model binder&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IModelBinder&lt;/span&gt; &lt;span class="nf"&gt;GetBinder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ModelBinderProviderContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;modelType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseNopModel&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;IsAssignableFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modelType&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;//use NopModelBinder as a ComplexTypeModelBinder for BaseNopModel&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsComplexType&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;!&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsCollectionType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;//create binders for all model properties&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;propertyBinders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Properties&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToDictionary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modelProperty&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;modelProperty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modelProperty&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBinder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modelProperty&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NopModelBinder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propertyBinders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;EngineContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resolve&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILoggerFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;//or return null to further search for a suitable binder&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/aspnet/core/mvc/controllers/areas?view=aspnetcore-2.2"&gt;Areas&lt;/a&gt; To include Area in an ASP.NET Core app, add a regular route to the &lt;code&gt;Startup.cs&lt;/code&gt; file. For example, this way it will look for configuring &lt;em&gt;Admin area&lt;/em&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseMvc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;routes&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"areaRoute"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"{area:exists}/{controller=Admin}/{action=Index}/{id?}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"{controller=Home}/{action=Index}/{id?}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When doing it, the folder with the name &lt;em&gt;Area&lt;/em&gt; with the &lt;em&gt;Admin folder&lt;/em&gt; inside, should be in the app root. Now the attribute &lt;code&gt;[Area("Admin")] [Route("admin")]&lt;/code&gt;. will be used to connect the controller with this area.&lt;br&gt;
It remains only to create views for all the actions described in the controller.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--urNQCoSp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/tvt4wro4ldf2d37knugv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--urNQCoSp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/tvt4wro4ldf2d37knugv.png" alt="Alt Text" width="167" height="177"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Admin"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdminController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Validation
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;IFormCollection&lt;/em&gt; should not be passed to the controllers since in this case, asp.net server validation is disabled. To solve the problem, this property might be added to the model, this will prevent us from passing directly to the controller method. This rule works only if a model is available, if there is no model, there will be no validation.&lt;/p&gt;

&lt;p&gt;Child properties are no longer automatically validated and should be specified manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6. Migrate HTTP handlers and HttpModules to Middleware
&lt;/h3&gt;

&lt;p&gt;HTTP handlers and HTTP modules are in fact very similar to the concept of &lt;a href="https://docs.microsoft.com/aspnet/core/migration/http-modules?view=aspnetcore-2.2"&gt;Middleware in ASP.NET Core,&lt;/a&gt; but unlike modules, the middleware order is based on the order in which they are inserted into the request pipeline. The order of modules is mainly based on the events of &lt;a href="https://docs.microsoft.com/previous-versions/ms227673(v=vs.140)?redirectedfrom=MSDN"&gt;application life cycle&lt;/a&gt;. The order of middleware for responses is opposite to the order for requests, while the order of modules for requests and responses is the same. Knowing this, you can proceed with the update.&lt;/p&gt;

&lt;p&gt;What should be updated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migration of modules for &lt;em&gt;Middleware&lt;/em&gt; (&lt;em&gt;AuthenticationMiddleware&lt;/em&gt;, &lt;em&gt;CultureMiddleware&lt;/em&gt;, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/aspnet/core/migration/http-modules?view=aspnetcore-2.2#migrating-handler-code-to-middleware"&gt;Handlers&lt;/a&gt; to &lt;em&gt;Middleware&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Use of new &lt;em&gt;middleware&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Authentication in nopCommerce does not use a built-in authentication system; for this purpose, AuthenticationMiddleware developed in accordance with the new ASP.NET Core structure is used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthenticationMiddleware&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;_next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AuthenticationMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IAuthenticationSchemeProvider&lt;/span&gt; &lt;span class="n"&gt;schemes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;Schemes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;schemes&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schemes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
       &lt;span class="n"&gt;_next&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IAuthenticationSchemeProvider&lt;/span&gt; &lt;span class="n"&gt;Schemes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Features&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IAuthenticationFeature&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;AuthenticationFeature&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;OriginalPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;OriginalPathBase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathBase&lt;/span&gt;
       &lt;span class="p"&gt;});&lt;/span&gt;

       &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;handlers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IAuthenticationHandlerProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
       &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scheme&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Schemes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetRequestHandlerSchemesAsync&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="k"&gt;try&lt;/span&gt;
           &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetHandlerAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;IAuthenticationRequestHandler&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HandleRequestAsync&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                   &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="k"&gt;catch&lt;/span&gt;
           &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="c1"&gt;// ignored&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;defaultAuthenticate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Schemes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDefaultAuthenticateSchemeAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defaultAuthenticate&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AuthenticateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defaultAuthenticate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
           &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Principal&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Principal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ASP.NET provides a lot of built-in middleware that you can use in your application, but the developer can also &lt;a href="https://docs.microsoft.com/aspnet/core/fundamentals/middleware/write?view=aspnetcore-2.2"&gt;create his own middleware&lt;/a&gt; and add it to HTTP request pipeline. To simplify this process, we added a special interface to nopCommerce, and now it’s enough to just create a class that implements it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;INopStartup&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Add and configure any of the middleware&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="services"&amp;gt;Collection of service descriptors&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="configuration"&amp;gt;Configuration of the application&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Configure the using of added middleware&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="application"&amp;gt;Builder for configuring an application's request pipeline&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Gets order of this startup configuration implementation&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you can add and configure your middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Represents object for the configuring authentication middleware on application startup&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthenticationStartup&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;INopStartup&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Add and configure any of the middleware&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="services"&amp;gt;Collection of service descriptors&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="configuration"&amp;gt;Configuration of the application&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//add data protection&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddNopDataProtection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;//add authentication&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddNopAuthentication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Configure the using of added middleware&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="application"&amp;gt;Builder for configuring an application's request pipeline&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//configure authentication&lt;/span&gt;
        &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseNopAuthentication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Gets order of this startup configuration implementation&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//authentication should be loaded before MVC&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7. Using built-in DI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2"&gt;Dependency&lt;/a&gt; injection is one of the key features when designing an app in ASP.NET Core. You can develop loosely coupled applications that are more testable, modular and as a result more maintainable. This was made possible by following the principle of dependency inversion. To inject dependencies, we used IoC (&lt;em&gt;Inversion of Control&lt;/em&gt;) containers. In ASP.NET Core, such a container is represented by the &lt;em&gt;IServiceProvider&lt;/em&gt; interface. Services are installed in the app in the &lt;code&gt;Startup.ConfigureServices()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Any registered service can be configured with three scopes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;transient&lt;/li&gt;
&lt;li&gt;scoped&lt;/li&gt;
&lt;li&gt;singleton
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApplicationDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DefaultConnection"&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Isingleton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;MySingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 8. Using WebAPI project compatibility shells (Shim)
&lt;/h3&gt;

&lt;p&gt;To simplify migration of an existing Web API, we advise using the NuGet package  &lt;a href="https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.WebApiCompatShim"&gt;Microsoft.AspNetCore.Mvc.WebApiCompatShim&lt;/a&gt;. It supports the following &lt;a href="https://docs.microsoft.com/aspnet/core/migration/webapi?view=aspnetcore-2.2#compatibility-shim"&gt;compatible features&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding &lt;em&gt;ApiController&lt;/em&gt; type;&lt;/li&gt;
&lt;li&gt;Enabling web API style model binding;&lt;/li&gt;
&lt;li&gt;Extending model binding so that controller actions can accept &lt;code&gt;HttpRequestMessage&lt;/code&gt; type parameters;&lt;/li&gt;
&lt;li&gt;Adding message formatters enabling actions to return results of &lt;code&gt;HttpResponseMessage type&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMvc&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;AddWebApiConventions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapWebApiRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"DefaultApi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"api/{controller}/{id?}"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 9. Porting Application Configuration
&lt;/h3&gt;

&lt;p&gt;Some settings were previously saved in the web.config file. Now we use a &lt;a href="https://docs.microsoft.com/aspnet/core/fundamentals/configuration/index?tabs=basicconfiguration&amp;amp;view=aspnetcore-2.2"&gt;new approach&lt;/a&gt; based on the key-value pairs set by &lt;em&gt;configuration providers&lt;/em&gt;. This is the recommended method in ASP.NET Core, and we use the &lt;code&gt;appsettings.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;You can also use the NuGet package 'System.Configuration.ConfigurationManager' if for some reason you want to continue using &lt;code&gt;*.config&lt;/code&gt;. In this case, the app cannot run on Unix platforms, but on IIS only.&lt;/p&gt;

&lt;p&gt;If you want to use the Azure key storage configuration provider, then you need to refer to &lt;a href="https://docs.microsoft.com/aspnet/core/security/key-vault-configuration?view=aspnetcore-2.2"&gt;Migrating content to Azure Key Vault&lt;/a&gt;. Our project did not contain such a task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 10. Porting static content to wwwroot
&lt;/h3&gt;

&lt;p&gt;To serve &lt;a href="https://docs.microsoft.com/aspnet/core/fundamentals/static-files?view=aspnetcore-2.2"&gt;static content&lt;/a&gt;, specify to web host the content root of the current directory. The default is wwwroot. You can configure your folder for storing static files by setting up middleware.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NV93ZCAi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5acm12vq68gv5okha66j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NV93ZCAi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5acm12vq68gv5okha66j.png" alt="Alt Text" width="167" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 11. Porting EntityFramework to EF Core
&lt;/h3&gt;

&lt;p&gt;If the project uses some specific &lt;a href="https://docs.microsoft.com/ef/efcore-and-ef6/"&gt;features of Entity Framework 6&lt;/a&gt;, that are &lt;a href="https://docs.microsoft.com/ef/efcore-and-ef6/"&gt;not supported&lt;/a&gt; in &lt;em&gt;EF Core&lt;/em&gt;, it makes sense to run the application on the &lt;em&gt;NET Framework&lt;/em&gt;. Though in this case, we will have to reject the multi-platform feature, and the application will run on &lt;em&gt;Windows&lt;/em&gt; and &lt;em&gt;IIS&lt;/em&gt; only.&lt;/p&gt;

&lt;p&gt;Below are the main changes to be considered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;System.Data.Entity&lt;/code&gt; namespace is replaced by &lt;code&gt;Microsoft.EntityFrameworkCore&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;The signature of the &lt;code&gt;DbContext&lt;/code&gt; constructor has been changed. Now you should inject &lt;code&gt;DbContextOptions&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)&lt;/code&gt; method is replaced by &lt;code&gt;ValueGeneratedNever()&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WillCascadeOnDelete(false)&lt;/code&gt; method is replaced by &lt;code&gt;OnDelete (DeleteBehavior.Restrict)&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OnModelCreating(DbModelBuilder modelBuilder)&lt;/code&gt; method is replaced by &lt;code&gt;OnModelCreating(ModelBuilder modelBuilder)&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HasOptional&lt;/code&gt; method is no longer available;&lt;/li&gt;
&lt;li&gt;Object configuration is changed, now &lt;code&gt;OnModelCreating&lt;/code&gt; is using since &lt;code&gt;EntityTypeConfiguration&lt;/code&gt; is no longer available;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ComplexType attribute&lt;/code&gt; is no longer available;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IDbSet&lt;/code&gt; interface is replaced by &lt;code&gt;DbSet&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ComplexType&lt;/code&gt; - complex type support appeared in EF Core 2 with the &lt;a href="https://docs.microsoft.com/ef/core/modeling/owned-entities"&gt;Owned Entity&lt;/a&gt; type, and tables without Primary Key with &lt;a href="https://docs.microsoft.com/ef/core/modeling/keyless-entity-types"&gt;QueryType&lt;/a&gt; in EF Core 2.1;&lt;/li&gt;
&lt;li&gt;External keys in EF Core generate &lt;a href="https://docs.microsoft.com/ef/core/modeling/shadow-properties"&gt;shadow properties&lt;/a&gt; using the [Entity]Id template, unlike EF6, that uses the &lt;code&gt;[Entity]_Id&lt;/code&gt; template. Therefore, add external keys as a regular property to the entity first.&lt;/li&gt;
&lt;li&gt;To support &lt;code&gt;DI&lt;/code&gt; for &lt;code&gt;DbContext&lt;/code&gt;, configure your &lt;code&gt;DbContex&lt;/code&gt; in &lt;code&gt;ConfigureServices&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Register base object context&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;param name="services"&amp;gt;Collection of service descriptors&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AddNopObjectContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDbContextPool&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NopObjectContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;optionsBuilder&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;optionsBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlServerWithLazyLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// SQL Server specific extension method for Microsoft.EntityFrameworkCore.DbContextOptionsBuilder&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;param name="optionsBuilder"&amp;gt;Database context options builder&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;param name="services"&amp;gt;Collection of service descriptors&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;UseSqlServerWithLazyLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;DbContextOptionsBuilder&lt;/span&gt; &lt;span class="n"&gt;optionsBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;nopConfig&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildServiceProvider&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NopConfig&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dataSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DataSettingsManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadSettings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;dataSettings&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dbContextOptionsBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optionsBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseLazyLoadingProxies&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nopConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseRowNumberForPaging&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;dbContextOptionsBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataConnectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRowNumberForPaging&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;dbContextOptionsBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataConnectionString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify that &lt;em&gt;EF Core&lt;/em&gt; generates a similar database structure as Entity Framework when &lt;a href="https://docs.microsoft.com/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli"&gt;migrating&lt;/a&gt;, use the  &lt;a href="https://www.red-gate.com/products/sql-development/sql-compare/"&gt;SQL Compare&lt;/a&gt; tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 12. Removing all HttpContext references and replacing obsolete classes and changing the namespace
&lt;/h3&gt;

&lt;p&gt;During the project migration, you will find that a sufficiently large number of classes have been renamed or moved, and now you should comply with the new requirements. Here is a list of the main changes you may encounter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HttpPostedFileBase&lt;/code&gt; 🡪 &lt;code&gt;FormFile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Access &lt;code&gt;HttpContext&lt;/code&gt; can now be accessed via &lt;code&gt;IHttpContextAccessor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HtmlHelper&lt;/code&gt; 🡪 &lt;code&gt;HtmlHelper&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ActionResult&lt;/code&gt; 🡪 &lt;code&gt;ActionResult&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HttpUtility&lt;/code&gt; 🡪 &lt;code&gt;WebUtility&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ISession instead of &lt;code&gt;HttpSessionStateBase&lt;/code&gt; accessible from &lt;code&gt;HttpContext.Session.&lt;/code&gt; from &lt;code&gt;Microsoft.AspNetCore.Http&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Request.Cookies&lt;/code&gt; returns &lt;code&gt;IRequestCookieCollection&lt;/code&gt;: &lt;code&gt;IEnumerable &amp;lt;KeyValuePair&amp;lt;string, string&amp;gt; &amp;gt;&lt;/code&gt;, then instead of &lt;code&gt;HttpCookie&lt;/code&gt; we use &lt;code&gt;KeyValuePair &amp;lt;string, string&amp;gt;&lt;/code&gt; from &lt;code&gt;Microsoft.AspNetCore.Http&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Namespace replacement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SelectList&lt;/code&gt; 🡪 &lt;code&gt;Microsoft.AspNetCore.Mvc.Rendering&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UrlHelper&lt;/code&gt; 🡪 &lt;code&gt;WebUtitlity&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MimeMapping&lt;/code&gt; 🡪 &lt;code&gt;FileExtensionContentTypeProvider&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MvcHtmlString&lt;/code&gt; 🡪 &lt;code&gt;IHtmlString&lt;/code&gt; and &lt;code&gt;HtmlString&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ModelState&lt;/code&gt;, &lt;code&gt;ModelStateDictionary&lt;/code&gt;, &lt;code&gt;ModelError&lt;/code&gt; 🡪 &lt;code&gt;Microsoft.AspNetCore.Mvc.ModelBinding&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FormCollection&lt;/code&gt; 🡪 &lt;code&gt;IFormCollection&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Request.Url.Scheme&lt;/code&gt; 🡪 &lt;code&gt;this.Url.ActionContext.HttpContext.Request.Scheme&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MvcHtmlString.IsNullOrEmpty(IHtmlString)&lt;/code&gt; 🡪 &lt;code&gt;String.IsNullOrEmpty(variable.ToHtmlString())&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[ValidateInput (false)]&lt;/code&gt; - does not exist anymore and is no longer needed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HttpUnauthorizedResult&lt;/code&gt; 🡪 &lt;code&gt;UnauthorizedResult&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[AllowHtml]&lt;/code&gt; - directive does not exist anymore and is no longer needed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TagBuilder.SetInnerText&lt;/code&gt; method is replaced by &lt;code&gt;InnerHtml.AppendHtml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;JsonRequestBehavior.AllowGet&lt;/code&gt; when returning Json is no longer needed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HttpUtility.JavaScriptStringEncode.&lt;/code&gt;🡪 &lt;code&gt;JavaScriptEncoder.Default.Encode&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Request.RawUrl.&lt;/code&gt; &lt;code&gt;Request.Path + Request.QueryString&lt;/code&gt; should be separately connected&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AllowHtmlAttribute&lt;/code&gt; - class no longer exists&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;XmlDownloadResult&lt;/code&gt; - now you can use just &lt;code&gt;return File(Encoding.UTF8.GetBytes (xml), "application / xml", "filename.xml")&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[ValidateInput(false)]&lt;/code&gt; - directive does not exist anymore and is no longer needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 13. Authentication and authorization update
&lt;/h3&gt;

&lt;p&gt;As was already mentioned above, nopCommerce project does not involve the built-in authentication system, it is implemented in a separate middleware layer. However, ASP.NET Core has its own system for credentials providing. You can view the &lt;a href="https://docs.microsoft.com/aspnet/core/security/?view=aspnetcore-2.2#authentication-vs-authorization"&gt;documentation&lt;/a&gt; to know about them in details.&lt;/p&gt;

&lt;p&gt;As for data protection, we no longer use  &lt;a href="https://docs.microsoft.com/aspnet/core/security/data-protection/compatibility/replacing-machinekey?view=aspnetcore-2.2"&gt;MachineKey&lt;/a&gt;. Instead, we use the built-in data protection feature. By default, keys are generated when the application starts. As the data storage can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;File system&lt;/code&gt; - file system-based keystore&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Azure Storage&lt;/code&gt; - data protection keys in &lt;em&gt;Azure BLOB&lt;/em&gt; object storage&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Redis&lt;/code&gt; - data protection keys in the Redis cache&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Registry&lt;/code&gt; - used if the application does not have access to the file system&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EF Core&lt;/code&gt; - keys are stored in the database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the built-in providers are not suitable, you can specify your own key storage provider by making a custom &lt;a href="https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.dataprotection.repositories.ixmlrepository?view=aspnetcore-3.0"&gt;IXmlRepository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 14. JS/CSS update
&lt;/h3&gt;

&lt;p&gt;The way of using static resources has changed, now they should all be stored in the root folder of the project &lt;code&gt;wwwroot&lt;/code&gt;, unless other settings are made.&lt;/p&gt;

&lt;p&gt;When using javascript built-in blocks, we recommend moving them to the end of the page. Just use the &lt;code&gt;asp-location = "Footer"&lt;/code&gt; attribute for your &lt;code&gt;script&lt;/code&gt; tags. The same rule applies to js files.&lt;/p&gt;

&lt;p&gt;Use the &lt;a href="https://github.com/madskristensen/BundlerMinifier"&gt;BundlerMinifier&lt;/a&gt; extension as a replacement for &lt;code&gt;System.Web.Optimization&lt;/code&gt; - this will enable bundling and minification. JavaScript and CSS while building the project (&lt;a href="https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification?view=aspnetcore-3.0&amp;amp;tabs=visual-studio"&gt;view the documentation&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 15. Porting views
&lt;/h3&gt;

&lt;p&gt;First of all, &lt;code&gt;Child Actions&lt;/code&gt; are no longer used, instead, ASP.NET Core suggests using a new high-performance tool - &lt;a href="https://docs.microsoft.com/aspnet/core/mvc/views/view-components?view=aspnetcore-2.2"&gt;ViewComponents&lt;/a&gt; called asynchronously.&lt;/p&gt;

&lt;p&gt;How to get a string from &lt;em&gt;ViewComponent&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Render component to string&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;param name="componentName"&amp;gt;Component name&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;param name="arguments"&amp;gt;Arguments&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;Result&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;RenderViewComponentToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;actionContextAccessor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IActionContextAccessor&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;IActionContextAccessor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actionContextAccessor&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"IActionContextAccessor cannot be resolved"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actionContextAccessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ActionContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;viewComponentResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ViewComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;viewData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ViewData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewData&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tempData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TempData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tempData&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringWriter&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;viewContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ViewContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;NullView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;viewData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;tempData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlHelperOptions&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="c1"&gt;// IViewComponentHelper is stateful, we want to make sure to retrieve it every time we need it.&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;viewComponentHelper&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IViewComponentHelper&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewComponentHelper&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;IViewContextAware&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nf"&gt;Contextualize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewComponentResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewComponentType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;
            &lt;span class="n"&gt;viewComponentHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewComponentResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewComponentName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewComponentResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;viewComponentHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewComponentResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewComponentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewComponentResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HtmlEncoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that there is no need to use &lt;code&gt;HtmlHelper&lt;/code&gt; anymore, ASP.NET Core includes many auxiliary built-in &lt;a href="https://docs.microsoft.com/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-2.2#built-in-aspnet-core-tag-helpers"&gt;Tag Helpers&lt;/a&gt;. When the application is running, the &lt;em&gt;Razor&lt;/em&gt; engine handles them on the server and ultimately converts to standard &lt;code&gt;html&lt;/code&gt; elements. This makes application development a whole lot easier. And of course, you can implement your own &lt;code&gt;tag helpers.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We started using dependency injection in views instead of enabling settings and services using &lt;code&gt;EngineContext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, the main points on porting views are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Convert &lt;code&gt;Views/web.config&lt;/code&gt; to &lt;code&gt;Views/_ViewImports.cshtml&lt;/code&gt; - to import namespaces and inject dependencies. This file does not support other Razor features, such as function and section definitions&lt;/li&gt;
&lt;li&gt;Convert &lt;code&gt;namespaces.add&lt;/code&gt; to &lt;code&gt;@using&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Porting any settings to the main application configuration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Scripts.Render&lt;/code&gt; and &lt;code&gt;Styles.Render&lt;/code&gt; do not exist. Replace with links to output data of &lt;code&gt;libman&lt;/code&gt; or &lt;code&gt;BundlerMinifier&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The process of migrating a large web application is a very time-consuming task which, as a rule, cannot be carried out without the pitfalls. We planned to migrate to a new framework as soon as its first stable version was released but were not able to make it right away, as there were some critical features that had not been transferred to .NET Core, in particular, those related to EntityFramework. Therefore, we had first to make our release using a mixed approach - the .NET Core architecture with the .NET Framework dependencies, which in itself is a unique solution. Being first is not easy, but we are sure we’ve made the right choice, and our huge community supported us in this.&lt;/p&gt;

&lt;p&gt;We were able to fully adapt our project after the release of .NET Core 2.1, having by that time a stable solution already working on the new architecture. It remained only to replace some packages and rewrite the work with EF Core. Thus, it took us several months and two released versions to completely migrate to the new framework.&lt;/p&gt;

&lt;p&gt;In this guide, we tried to put together the entire migration process in a structured form and describe various bottlenecks so that other developers could rely on this material and follow the roadmap when solving the same task.&lt;/p&gt;

&lt;p&gt;To learn more about nopCommerce visit our &lt;a href="https://github.com/nopSolutions/nopCommerce"&gt;GitHub&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>nopcommerce</category>
      <category>aspnetcore</category>
      <category>dotnet</category>
      <category>ecommerce</category>
    </item>
  </channel>
</rss>
