Pages - Menu

nopCommerce - Create New SQL Table / Domain Entity in the Cleanest Way

Scope

As our nopCommerce project advance through, finally it comes to a point where I need to create a new SQL table, a domain object to hold the properties and a service to perform CRUD (Create Read Update Delete) actions.

I am creating a Color class that will hold a SAP Color Code and Color Name. I need to create a service to lookup the Color Name by the Color Code. And this information needs to be available in the database.

Similar to my previous posts here and here, I am trying to do it the 100% clean way so my future upgrade path will be painless (by taking away the pain now). And I am not going to stress why this is important again. Different developers take different points of view in this topic and ideas could be extremely diverted.

Implementation

Domain Object

I create a new project follows the Nop.Core structure called TA.Core.


I create my domain class as usual. This one is straight forward.


Service Class

I need to create my color service that will utilize the domain object, I won't bother with the actual implementation but it looks something like this. It was fairly a Copy and Paste from Nop.Services.Catalog.ManufacturerService with Find and Replace job.


This class sits in my Custom Project, in my case, TA.Services.


Don't forget your dependency register, it sits inside the Service call, wherever you put the file in, you register in that project.


In my calling code, I will have to include this in my ctor. I am using the same example as my previous post, my TAImportManager.


SQL Table

We now need to create this SQL table ourselves. I call it dbo.color, but it is probably worth to prefix all the tables to group all the custom tables together, and avoid possible table name collision in future upgrades.

Mapping File

One of the file that is easily missed out in nopCommerce DAL area is this mapping file. If you forget this step, your code will compile but you will get runtime error when you try to access the object.

The entity type <type> is not part of the model for the current context.




We will put the mapping file in a blink blink shiny new project and you may guessed right - TA.Data


In the mapping file, we tell EF which table to look up and any fields mapping etc. 

ObjectContext

NopObjectContext - This is the object context that basically does all the database operations. I wouldn't go too technical about this, but the only thing we need to do is to make sure it knows where to find our ColorMap. If you look at this file carefully, in the OnModelCreating(), this line of code is to register all the types within the assembly.

var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()

Since our mapping is not inside the same assembly, we will create our custom NopObjectContext!!



DependencyRegister

We will register everything in the base by calling base method, then register everything in our custom assembly!!

Last but not least, we need to now register our TANopObjectContext in IoC.

We create a new DependencyRegistrar in TA.Data as follow. I copy and pasted the relevant sections from the Nop.Web project. Since we don't use http, I have changed the InstancePerHttpRequest() to InstancePerLifetimeScope().


Add reference

At this stage, if you build and run, you will still get error about entity not found.

The entity type <type> is not part of the model for the current context.

Didn't nop promise to register everything that implement the IDependencyRegistrar? Well, it might help if you tell it to. In the web project, we need to add reference to TA.Data.

Conclusion

As you can see, quite a big circle to do something very simple. The quickest and dirtiest approach would be adding files in the Nop.Data project, but if you are going to use nopCommerce intensively and with prospect of upgrading to future version, I rather take the pain away now.

In this exercise, I have touched no code / logics in the Nop Platforms. In my check-ins, the only thing I am checking into the Nop is Nop.Web.csproj (Added new references to the new projects) and NopCommerce.sln (Added new projects to the solution.

Fear not about passing your peer code review. :)


nopCommerce - Custom Service inherit from Core Service by Dependency Injection and new interface

Scope

Previously I was doing customization to override a method in custom class from core class. It provided the cleanest way to inherit from nopCommerce core services without any tampering to the core files. One thing that I couldn't do with the previous approach is to create new method or to overload a method. Today, we will go through this implementation.

Implementation

To begin with, we will create a new custom interface, and put the new methods (or overloading methods) that we need. The custom interface will inherit from the core interface.


The Custom class will implement the core class and inherit from the custom interface instead of the usual core interface. This part is quite crucial as we will see in a minute. Don't forget your usual ctor.



From my calling class (in this example, I am using a custom class of the ImportManager), instead of the usual property assignment, we will need to cast the concrete class to the custom interface, so it can be assigned to the private / protected property.


In IoC, we will need to register the custom class for the core interface!!


Wait a minute, how does it work? Shouldn't I register as custom interface? If you do, you will get an error, when there are other core classes that are still using the core interface with the private property still referencing the core interface. Something like a ctor doesn't match up exception.

The fact that custom class implement custom interface but custom interface also inherit from core interface, we created this chain in polymorphism that if I am a type of RacingCar, then I must be a type of Car. That's why the explicit cast in  _productService = (TAProductService) productService; would work.

Build the solution and run the code, my new method is being called.


Conclusion

As you can see, this is not very difficult but a little tricky when we are dealing with IoC and MVC at the same time. As demonstrated, it only required a little code to do the work.

Talking about racing cars, who went to the 2013 Sydney 500 Super Car last week?





How to Deploy nopCommerce solutions

Important Note  This document is obsoleted from nopCommere 3.3. 

The following document was created with nopCommerce 3.1.

Trying to deploy my local development site to staging, and the process seems not that straight away.

For my first time deployment with nopCommerce, I did the following and got my site working :)

  1. Build the project.
  2. Publish Nop.Web to the ~\Deploy directory.
  3. Publish Nop.Admin to the ~\Deploy\Administration\ directory.
  4. Move ~\Deploy\Administration\bin to ~\Deploy\bin. Replace exist files.
  5. Change database connection at ~\Deploy\Presentation\Nop.Web\App_Data\Settings.txt
  6. Copy all files from ~\Presentation\Nop.Web\Plugins to ~\Deploy\Plugins
  7. Copy images from ~\Presentation\Nop.Web\Content\Images to ~\Deploy\Content\Images
  8. Reinstall Plugins. Apparently all the plugins that were installed in dev are not installed on staging. It means the status of the installs, or any configurations I have applied to plugins on dev are not stored in the database but somewhere else. 
  9. Copy ~\Presentation\Nop.Web\App_Data\InstalledPlugins.txt to ~\Deploy\Nop.Web\App_Data\InstalledPlugins.txt to 

Really hated the step 8, wish there are better ways doing it. This post saved step 8. 


nopCommerce - Create Customized Service that inherit from Core Service by Dependency Injection

Scope

Provide the cleanest way to inherit from nopCommerce core services without any tampering to the core files.

In this exercise, the Nop.Services project is 100% untouched. Why is it so important? It benefits in many different ways but mostly for the reason of keeping the code neat and manageable. A nice and clear separation between custom code and core code.

This is written base on nopCommerce 3.1, the latest version as of today it is written.

Business Requirements


  • Discount should be calculated based on RRP (Product.Price), not the SpecialPrice or TierPrice. 
  • PriceCalculationService.GetFinalPrice() should still return the cheapest price out of the following 3 conditions:-
    • Cheapest discount off RRP
    • Special Price
    • Tier Price (if eligible / available)

Setup

We setup the price for the "Cooking for Two" book as follow. RRP $19, and Speical $15.



 We then apply discount rule 50% to the category.


50% discount applied to special price and now selling for $7.5. This is the default behavior of nopCommerce out of the box.


Implementation

I created a new project and added to the solution called TA.Services. It follows the same folder structure as Nop.Services and I created a new class CustomPriceCalculationService.



In TA.Services, I added reference Nop.Core and Nop.Services.



Similarly, in Nop.Web.Framework, I need to add reference to TA.Services.

Under Nop.Web.Framework, I changed this magic line in DependencyRegistrar.cs.

[EDIT] In TA.Services, I create a new DependencyRegistrar that implements IDependencyRegistrar. I will register my service to the interface. What it does is now everywhere in the project that require IPriceCalculationService, the IoC will return a concrete class CustomPriceCalculationService instead of the old PriceCalculationService.




In CustomPriceCalculationService, I need to inherit PriceCalculationService and implement IPriceCalculationService. Some usual Ctor methods override.

For those who interested in the actual implementation, I only made small changes to GetDiscountAmount and GetFinalPrice.




Now we are selling for $9.5 (50% off from $19). Hopefully still a happy customer!!


Conclusion

There are maybe better ways to implement this than what I have done here. eg. Use the plugin approach and make the use of Special Price configurable in the admin etc, but I couldn't wait to try this 100% clean way of customizations. 

Now I have a complete control on how I like to plug and play this customizations in a multi-tenant environment, or I can put more custom inheritance on top of already custom inheritance. The only thing I need to change is the DI which is pretty much given. 

As you can see, customization on services is a lot easier than controllers. Note how much troubles I had with the nopCommerce Customization with Controller Inheritance.

SEO - Site, Fetch and Submit

Scope

I have a developer background and know absolute nothing about how Google SEO works, besides the fact that I know it is important to get a site become popular in a Google search.

This is an important topic for me to learn in 2014, and I will slowly uncover the technical parts one after one by blogging them.

Search within Site

This is a very useful function to keep track of what pages are currently indexed by Google. This can help identify what pages are missing or what the page result will look like to users in a google search.

To perform a site search, the syntax is like this, type the whole string in a google search box.

site:www.domain.com

A result will look like this, it returns all pages of a domain. It works for both naked domain or subdomain.



Fetch and Submit

Fetch will force Google to crawl a URL. This is useful when you have just updated meta information or you have a new page that you might want Google to reindex.

Submit will force Google to reindex the page. Think of it like a commit command for your source code repository. After your fetch, you must submit to Google or Google will not reindex it.

These are done via the WebMaster Tool.
https://www.google.com/webmasters/tools