Enterprise Framework

Software Solutions in the Enterprise

NopCommerce Plugin Best Practices

NopCommerce Plugin Best Practices

NOTE:  DRAFT IN PROGRESS

Versions:    3.7

What are NopCommerce Plugins?

  • NopCommerce plugin are components that can extend the core NopCommerce E-Commerce platform.
  • NopCommerce Plugins can be used to add, extend or replace features within NopCommerce
  • NopCommerce Plugins are mini MVC components, it can use Controllers, Views, Routes, Models, and JavaScript that you would typically include within a ASP.NET MVC App.
  • NopCommerce Plugins are registered within NopCommerce dynamically and then installed through configuration for use within your NopCommerce application.

What types of NopCommerce Plugins can you create?

  • Admin Plugin - Allow you create new screens for use on the Admin page or even replace existing Admin features.
  • Retail Product Plugin - Allows you create new screens or overwrite existing retail product page features
  • Checkout Plugin - Allows you add to checkout or replace existing checkout.
  • Web Service Plugin - Create a RESTful Web API end point.
  • Mixed plugin - A combination of plugins for use in your NopCommerce site.
  • more...

What are the advantages of using a Plugin
  • A isolated set of features that are distribute-able (Assuming you kept references clean)
  • A isolated set of features that aren't impacted by a NopCommerce upgrade (hopefully)
  • You can replace NopCommerce endpoint calls and replace it with a custom one in your plugin.
  • Easy way to add non-core tables to the system

What are the dis-advantages of using a Plugin

  • Replacing the Checkout process with a plugin is probably not the greatest idea.
  • No Referential Integrity against existing Nop tables
  • Plugins that intercept core NopCommerce endpoints can become a maintenance nightmare.
 
Custom Plugin development challenges. 

You should follow these rules to try and minimize work

  1. Use the same Nuget package versions across all projects.  Follow NuGet Best Practices.  By not managing NuGet Packages correct could result in weird behaviors in your application.
  2. DO NOT checkin the NuGet package into source control.
  3. No not directly reference DLL's in the Packges folder, use the NuGet to include
  4. Your plugin should encapsulate all the logic required to operate
  5. Minimize changes to the core NopCommerce code base
  6. Controllers should be responsible for hydrating the current session/request and then passing that information down to helper services.
  7. Controllers should not contain your business logic because it's hard to test
  8. Services that contain logic should not be aware of HTTP Context and Session.
  9. Minimize size of methods (SOLID principle)
  10. Using a Plugin to override the Checkout Workflow is a complex task and should be a last resort.

Custom Plugin Endpoints

  • Do NOT start custom route End Points with the word Admin
routes.MapRoute("Plugins.MyCompany.Admin.Product.List",
     "Admin/MyPlugin/Product/List",
     new { controller = "MyProduct", action = "Admin" },
     new[] { "MyCompany.Plugin.Misc.PluginName.Controllers" }
);
  • Start custom routes End Points with the word Plugins
routes.MapRoute("Plugins.MyCompany.Admin.Product.List",
     "Plugins/MyPlugin/Product/List",
     new { controller = "MyProduct", action = "Admin" },
     new[] { "MyCompany.Plugin.Misc.PluginName.Controllers" }
);

Base Plugin Folder and File Structure

  • \Content
  • \Controllers
  • \Extensions
  • \Infrastructure
  • \Models
  • \Scripts
  • \Services
  • \Validators
  • \Views
  • \Sql
  • Description.txt
  • Logo.png
  • Notes.Txt
  • packages.config
  • RouteProvider.cs

Routes



Resources

How To : NopCommerce Best Practices for Order table

How To:  NopCommerce Best Practices for Order table

NopCommerce Versions:  3.7


NOTE:  DRAFT IN PROGRESS


Description:

The Order table captures both registered Customers and Guest orders.  

Registered Customer Orders will use the Customer.Id associated to them at registration.  During checkout, users are prompted for the billing and shipping address.  The billing and shipping Address records are duplicated as point in time records and associated to the Order.

Guest Checkout Orders orders will leave a cookie on the client with a generated ID that was created in the Customer table.  The Next time the registered Guest user comes to the site and places an order as Guest, it will use the originally created Customer.Id to use and associate to the Order.  If the user decides to register at this point, it will use the originally created Customer record associated to the cookie.ID to be used for the newly registered account.

You will find that any time an order is placed, a point in time copy of a billing and shipping address record is created and associated to the order.  This allows the order to have historical records at that point in time.  The created address will not by modified again.

Table:  Order

  • AffiliateId (int, 4)
  • AllowStoringCreditCardNumber (bit, 1)
  • AuthorizationTransactionCode (nvarchar, not null)
  • AuthorizationTransactionId (nvarchar, not null)
  • AuthorizationTransactionResult (nvarchar, not null)
  • BillingAddressId (int, 4)
  • CaptureTransactionId (nvarchar, not null)
  • CaptureTransactionResult (nvarchar, not null)
  • CardCvv2 (nvarchar, not null)
  • CardExpirationMonth (nvarchar, not null)
  • CardExpirationYear (nvarchar, not null)
  • CardName (nvarchar, not null)
  • CardNumber (nvarchar, not null)
  • CardType (nvarchar, not null)
  • CheckoutAttributeDescription (nvarchar, not null)
  • CheckoutAttributesXml (nvarchar, not null)
  • CreatedOnUtc (datetime, 8)
  • CurrencyRate (decimal, 9)
  • CustomerCurrencyCode (nvarchar, not null)
  • CustomerId (int, 4)
  • CustomerIp (nvarchar, not null)
  • CustomerLanguageId (int, 4)
  • CustomerTaxDisplayTypeId (int, 4)
  • CustomValuesXml (nvarchar, not null)
  • Deleted (bit, 1)
  • ForEndCustomerCompanyAddressId (int, 4, not null)
  • ForEndCustomerCompanyId (int, 4, not null)
  • ForResellerCompanyAddressId (int, 4, not null)
  • ForResellerCompanyId (int, 4, not null)
  • ForResellerCompanyTypeId (int, 4, not null)
  • Id (PK, int, 4)
  • IsTransferable (bit, 1)
  • MaskedCreditCardNumber (nvarchar, not null)
  • OrderDiscount (decimal, 9)
  • OrderGuid (uniqueidentifier, 16)
  • OrderShippingExclTax (decimal, 9)
  • OrderShippingInclTax (decimal, 9)
  • OrderStatusId (int, 4)
  • OrderSubTotalDiscountExclTax (decimal, 9)
  • OrderSubTotalDiscountInclTax (decimal, 9)
  • OrderSubtotalExclTax (decimal, 9)
  • OrderSubtotalInclTax (decimal, 9)
  • OrderTax (decimal, 9)
  • OrderTotal (decimal, 9)
  • PaidDateUtc (datetime, 8, not null)
  • PaymentMethodAdditionalFeeExclTax (decimal, 9)
  • PaymentMethodAdditionalFeeInclTax (decimal, 9)
  • PaymentMethodSystemName (nvarchar, not null) - This is the name of the plugin system name used to do the payment.  Example:  Payments.PurchaseOrder (Nop.Plugin.Payments.Manual), Payments.Manual (Nop.Plugin.Payments.Manual). 
  • PaymentStatusId (int, 4)
  • PickUpInStore (bit, 1)
  • RefundedAmount (decimal, 9)
  • RewardPointsWereAdded (bit, 1)
  • ShippingAddressId (int, 4, not null)
  • ShippingMethod (nvarchar, not null)
  • ShippingRateComputationMethodSystemName (nvarchar, not null)
  • ShippingStatusId (int, 4)
  • StoreId (int, 4)
  • SubscriptionTransactionId (nvarchar, not null)
  • TaxRates (nvarchar, not null)
  • VatNumber (nvarchar, not null)

Related Tables:

  • OrderItem
  • OrderNote
  • GenericAttribute

Uses:

  • Check Out > Registered User: 
  • Checkout > Guest:
  • Admin > Order: 
Extending:




How To : NopCommerce Best Practices for Address table

How To:  NopCommerce Best Practices for Address table

NopCommerce Versions:  3.7


Note:  This is a draft version in progress.

Description:  The Address table is used for capturing Address information for Registered Customers, Guest Checkout Customers and Orders.  

  • The best way to think about the Address table is that it has multi-state data meaning the Address data can be point in time created and associated with an Order and won't change again or current data that represents the current Address of a customer. Every time an order is created a snapshot point in time Address record is created (Duplicated) for the Billing Address and Shipping Address.

Table:  Address

  • Address1 (nvarchar, not null)
  • Address2 (nvarchar, not null)
  • City (nvarchar, not null)
  • Company (nvarchar, not null)
  • CompanyId (int, 4, not null)
  • CountryId (int, 4, not null)
  • CreatedOnUtc (datetime, 8)
  • CustomAttributes (nvarchar, not null)
  • Email (nvarchar, not null)
  • FaxNumber (nvarchar, not null)
  • FirstName (nvarchar, not null)
  • Id (PK, int, 4)
  • LastName (nvarchar, not null)
  • PhoneNumber (nvarchar, not null)
  • StateProvinceId (int, 4, not null)
  • ZipPostalCode (nvarchar, not null)

Related Tables:

  • AddressAttribute
  • AddressAttributeValue
  • CustomerAddresses

Uses:

  • Retail Site > My Account > Addresses
  • Customer Entity > Checkout
  • Admin > Customer > Addresses
Extending:

  • The Address table already contains an extensive common set of information relating to addresses.  Instead of modifying the table, you should add a table that has a foreign key to the Address table but is also the primary key.

How To : NopCommerce Best Practices for Customer table

How To:  NopCommerce Best Practices for Customer table

NopCommerce Versions:  3.7


NOTE:  DRAFT IN PROGRESS

Description:

The Customer table is used for capturing both registered and non registered users of the system.  For registered users, it only contains a small set of information usch as Username, Email, and password as shown below. Additional information relating to the customer such as First Name, Last Name and Gender is captured in the GenericAttribute table.  The different way Customer information is entered are:

  • When a user goes through the Retail > Register process a record is created in the Customer table.    
  • When a user does a Checkout > Guest Checkout (not logged in) they will have a generic empty record added.  The Username, Email, Password, PasswordSalt, AdminContent, SystemName, LastLoginDateutc fields are set as NULL.  The Id, CustomerGuid, PasswordFormatId, IsTaxExempt, AffiliateId, VendorId, HasShoppingCartItems, Active, Deleted, IsSystemAccount, LastIpAddress, CreatedOnUtc, LastActivityDateUtc, BillingAddress_id, ShippingAddress_Id are set.  New Billing and Shipping Address records are created and then associated to the new Customer record. 
  • Guest Checkout Orders orders will leave a cookie on the client with a generated ID that was created in the Customer table.  The Next time the registered Guest user comes to the site and places an order as Guest, it will use the originally created Customer.Id to use and associate to the Order.  If the user decides to register at this point, it will use the originally created Customer record associated to the ID to be used for the new registered to the account.
  • Admins going through Administration > Customer > New and

Table:  Customer

  • Active (bit, 1)
  • AdminComment (nvarchar, not null)
  • AffiliateId (int, 4)
  • BillingAddress_Id (int, 4, not null)
  • CompanyId (int, 4, not null)
  • CreatedOnUtc (datetime, 8)
  • CustomerGuid (uniqueidentifier, 16)
  • CustomerTypeId (int, 4, not null)
  • Deleted (bit, 1)
  • Email (nvarchar, 2000, not null)
  • HasShoppingCartItems (bit, 1)
  • Id (PK, int, 4)
  • IsSystemAccount (bit, 1)
  • IsTaxExempt (bit, 1)
  • LastActivityDateUtc (datetime, 8)
  • LastIpAddress (nvarchar, not null)
  • LastLoginDateUtc (datetime, 8, not null)
  • Password (nvarchar, not null)
  • PasswordFormatId (int, 4)
  • PasswordSalt (nvarchar, not null)
  • ShippingAddress_Id (int, 4, not null)
  • SystemName (nvarchar, 800, not null)
  • Username (nvarchar, 2000, not null)
  • VendorId (int, 4)

Related Tables:
  • Customer_CustomerRole_Mapping
  • CustomerAddressess
  • CustomerAttribute
  • CustomerAttributeValue
  • CustomerRole
  • CustomerType
Uses:

  • Retail Site > Register:
  • Checkout > Guest:
  • Admin > Customer:
Extending:
  • If you are need to capture information relating to the user, you can capture information in the GenericAttribute table, but beware of the GenericAttribute tables usages.  WARNING:  storing data in here does not enforce referential integrity.  You might want to consider the below option of creating a new table with foreign keys if you need data integrity.
  • If you need to enforce referential integrity, instead of adding columns to the Customer table, add a second Table with the foreign key to the Customer table and set it as the Primary Key and then add your custom columns to that new table.

How To : NopCommerce 3.7 Best Practices for GenericAttribute table

How To:  NopCommerce Best Practices for GenericAttribute table


NOTE:  DRAFT IN PROGRESS


Description: 

A generic storage table for long term and short term data for any entity in NopCommerce that inherits from BaseEntity.  But be careful how you use this table or what you do with it because it has both long term and short term data and not understanding the of the data that's stored could cause issues.. The GenericAttribute table can persist long term Customer data such as First Name, Last Name and Gender.  You can use the GenericAttribute table to persist the long term order data but the KeyGroup must be Order and the EntityId represents the OrderId

Table:  GenericAttribute

  • Id (int, identity, not null, pk)
  • EntityId (int, not null)
  • KeyGroup (nvarchar(400), not null)
  • Key (nvarchar(400), not null)
  • Value (nvarchar(max), not null)
  • StoreId (int, not null) - Settings Store to 0 means all stores

Related Tables:
  • CustomerType
Uses:

  • Customer Entity : Register : You'll find that it stores long term information like Gender, FirstName, LastName and more on customers who have registered.  Note that StoreId is set 0 which means it applies to all stores.  This is long term storage meaning it won't change unless change are made by the user specifically.
    • Long Term Records:
      • KeyGroup:  Customer
      • Key's are:
        • Gender
        • FirstName
        • LastName
        • DateOfBirth
  • Customer Entity : Checkout : You'll find that it temporarily stores short term data such as a a user selections as you transitions from one section to the next during the checkout process for that customer.  Any values stored during this will be overwritten by the next order for that customer.
    • Temporary Records:
      • KeyGroup:  Customer
      • Key's are:
        • SelectedPickUpInStore
        • UseRewardPointsDuringCheckout
Extending:
  • The GenericAttribute table is well, generic by nature.  Extending it probably is not necessary.  However be warned that it does not enforce referential integrity and I would probably try to avoid if possible..  

How To : NopCommerce Best Practices For Database Updates using Entity Framework.

How To : NopCommerce Best Practices for Database Updates using Entity Framework.

Compatible With:  NopCommerce 3.7

These instructions will help you setup Entity Framework Migrations on the Nop.Data project with the assumption that you did a NopCommerce 3.7 fresh default database setup install with or without sample data. The NopCommerce default database install will be the base used.

Enabling Entity Framework Migrations will allow you to apply Entity Framework Code First to the database to update the default NopCommerce schema.  The benefits are that you have a starting point database with NopCommerce and any changes that you apply after that are captured as individual database changes that are applied to the NopCommerce Default database.  The Migrations can be reapplied later on to a Fresh NopCommerce install using the migrations.

Prerequisites:  

  • Fresh NopCommerce 3.7 Source Code Installed
  • Fresh NopCommerce 3.7 Database Installed with or without sample data

Getting Started:

  1. Open NopCommerce Solution
  2. Change Presentations\Nop.Web\Web.Config to point to the database you want to use.

      <connectionStrings>
        <add name="NopCommerce" providerName="System.Data.SqlClient" connectionString="Data Source=.;Initial Catalog=NopCommerce37;Integrated Security=False;User Id=sa;Password=yourpassword;MultipleActiveResultSets=True" />
      </connectionStrings>
    
  3. Open Package Manager Console
  4. Set Default project: Nop.Data 
  5. Type:  Enable-Migrations - this will create a folder called Migrations under the Nop.Data project.
  6. Type:  Add-Migration “InitialDatabaseSnapshot” –IgnoreChanges

    Note: The –IgnoreChanges switch will tell our Nop.Data project to not compare the Nop.Data Migrations against the current database. Because we don't have any existing migrations, by not using the "-IgnoreChanges", it would create all the code to regenerate all the tables from NopCommerce duplicating what NopCommerce will create by default. Our Migrations are meant to capture changes moving forward after the default NopCommerce 3.7 Database is setup and be easily reapplied.

After you have ran this, the Nop.Data project will have a new folder called Migrations with two new files:

  • Configuration.cs
  • {DateTimeStamp}_InitialDatabaseSnapshot.cs – Initial place holder for Migrations.  This has an empty Up() and Down() method intentionally because we used the "-IgnoreChanges" switch in our command earlier.  It assumes that Created the tables and sample data and anything moving forward is newly added.

Next Steps:  Applying the changes to the database 

In Package Manager Console:

  • Type:  Update-Database 

You should receive a confirmation that the project is ready.

This will enable Migrations in the database by adding a __Migrations table.  The __Migrations table is a history of Migration changes applied to it.  The newly added migration we created earlier InitialDatabaseSnapshot will to be applied to the default NopCommerce 3.7 database we setup earlier, but since we did the -IgnoreChanges switch, the {DateTimeStamp}_InitialDatabaseSnapshot.cs contains no code in Up() or Down(), so no changes are applied to the database with the exception of the newly added __Migrations table which is what we want.  If you open the new __Migrations table, it will have a single record for the InitialDatabaseSnapshot migration that was applied to the database.  Now that a record was captured for InitialDatabaseSnapshot, it will never be applied again to the database..

Your database and solution is now ready for Code First Updates using Migrations.


Next Steps:

ADDING A NEW ENTITY AND RUNNING ADD-MIGRATION

Important:  These instructions will create Migrations code to generate tables after the adding of new Entities 

Prerequisite:  

  • Added a newly Entity Person (Example:  Person.cs) to the Nop.Core\Domain\Custom;
Steps:
  • Open Presentations\Nop.Web\Web.Config and verify the connection string that it is pointing to the database you want to use.
  • Open:  Package Manager Console
  • set Default Project: Libraries\Nop.Data
  • Type:  Add-Migration “SomeDescriptionHere” 
  • Example:  Add-Migration Add New Person Entity

NOTE:  After you run “Add-Migration”, the Nop.Data project will have a new file called

  • {DateTimeStamp}_AddNewPersonEntity.cs

This new file contains code to generate any new tables from new Entities you have defined in Entity Framework in the Up () method and code to remove tables in the Down() method.

Type:  Update-Database

This will cause the newly added migration AddNewPersonEntity to be applied to the database and a new table called Person tables to be created.  The AddNewPersonEntity migration will be tracked in the __Migrations table so it’s never applied again.


How To : C# Numbers To Words Generator

Numbers To Words Generator - C# Project


Numbers to Words is a C# project that takes a number and converts it to a word.


NOTE:  THIS WAS DONE IN 3 HOURS FOR A TEST SO IT IS IN NO WAY SHAPE OR FORM CLEANED OR OPTIMIZED OR BEST PRACTICES.  HOWEVER, THIS PROJECT WAS BUILT DOING TDD (Test Driven Development) SO THERE A LOT OF TESTS TO VALIDATE IT AND SHOWS THE VALUE OF UNIT TESTS.


Example: (1000000 generates the words "One Million")

Project examples:

Ones (Example:  3 returns "Three")

Tens (Example:  14 returns "Fourteen")

Hundreds (Example:  397 returns "Three Hundred Ninety Seven")

Thousands (Example: 7923 returns "Seven Thousand Twenty Three")

Ten Thousands (Example:  22222 returns "Twenty Two Thousand Two Hundred Twenty Two")

Hundred Thousands (Example:  832935 returns "Eight Hundred Thirty Two Thousand Nine Hundre Thirty Five")

Millions (Example:  9882235 returns "Nine Million Eighty Hundred Eighty Two Thousand Two Hundred Thirty Five")

Billions (Example:  1012345678 returns "One Billion Twelve Million Three Hundred Fourty Five Thousand Six Hundred Seventy Eight")

Also supports decimal.  but in percent.  Have fun.

Source Code : https://github.com/Gigundo/Numbers-to-Words

How To : ASP.NET MVC With Microsoft Identity with a GUID Id and Unity Injection

How To : ASP.NET MVC With Microsoft Identity with a GUID Id and Unity Injection

This downloadable project contains the code to do ASP.NET Identity with a GUID ID and doing Unity Injection


Download File:  AspNet Identity Working with GUID ID and Unity Injection Final.zip (8.2MB)


Previous Version for default ASP.NET Identity is available :

http://www.enterpriseframework.com/post/how-to-asp-net-mvc-with-microsoft-identity-and-unity-bootstrapper


OpenStreetMap Mapnik Make Error: mapnik/version.hpp: No such file or directory

robert@robert-Ubuntu-Virtual-Platform:~/src/mod_tile$ make
Making all in iniparser3.0b
make[1]: Entering directory '/home/robert/src/mod_tile/iniparser3.0b'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/home/robert/src/mod_tile/iniparser3.0b'
make[1]: Entering directory '/home/robert/src/mod_tile'
g++ -DHAVE_CONFIG_H -I. -I./includes  -pthread -DSYSTEM_LIBINIPARSER=0   -g -O2 -MT src/renderd-gen_tile.o -MD -MP -MF src/.deps/renderd-gen_tile.Tpo -c -o src/renderd-gen_tile.o `test -f 'src/gen_tile.cpp' || echo './'`src/gen_tile.cpp
src/gen_tile.cpp:1:30: fatal error: mapnik/version.hpp: No such file or directory
 #include <mapnik/version.hpp>
                              ^
compilation terminated.
Makefile:1124: recipe for target 'src/renderd-gen_tile.o' failed
make[1]: *** [src/renderd-gen_tile.o] Error 1
make[1]: Leaving directory '/home/robert/src/mod_tile'
Makefile:1288: recipe for target 'all-recursive' failed
make: *** [all-recursive] Error 1


Fix the error by installing libmapnik-dev:


robert@robert-Ubuntu-Virtual-Platform:~/src/mod_tile$ sudo apt-get install libmapnik-dev

Auto Generate C# Client Proxy from Swagger Metadata from ASP.NET WEB API

Here are some tools for automatic C# client side proxy code generators from Swagger Metadata
 
Microsoft - Azure
Article:  Get started with API Apps and ASP.NET in Azure App Service
Section: Consume from a .NET client by using generated client code
Link : https://azure.microsoft.com/en-us/documentation/articles/app-service-api-dotnet-get-started/#codegen

Git Repository:  https://github.com/azure/autorest
Community - Swagger Code Generator
https://github.com/swagger-api/swagger-codegen
Community - NSwag
https://github.com/NSwag/NSwag
Microsoft - MSDN
Article:  Introducing the Azure API Apps Tools for Visual Studio 2013
Section:  Enable Developers with One-click Strongly-typed REST API Clients
Section:  One-click API App Client Code Generation in Visual Studio
Link:  http://blogs.msdn.com/b/visualstudio/archive/2015/03/24/introducing-the-azure-api-apps-tools-for-visual-studio-2013.aspx