Pages - Menu

Showing posts with label inheritance. Show all posts
Showing posts with label inheritance. Show all posts

nopCommerce - Hello World to Multi Tenancy

Finally, it is about time to do some multi tenant works. Let's recap my goal and how I am travelling so far.

Scope

  • Build a multi tenant e-commerce platform for 15 or more international brands.
  • They are completely different brands and target consumers are completely different.
  • They will share some similarities in terms of functionalities but there will be customizations for individual brands
  • Administrator of a brand can only see stuff about their own brand.
  • Easy to develop
  • Easy to deploy
  • Easy to maintain
  • There is NOT necessary a super admin role to overlook ALL the brands.
  • Customers are NOT shared across brands.

Preparation

So far, I have built my first brand with the above scope in mind, the following strategies were implemented.

Specification

Since our first site went live, we are now ready for our next brand and the following needs to bear in mind with the followings:
  • Separate the brand specific codes in separated dll.
  • Database to be considered at later stage.
  • No brand specific code at controller level.
  • Views for different brands are handled by Nop Theme.
  • We will deploy for all the brands from the same Visual Studio Solutions.

Decision Making

Get Dirty with Code


Project Setup

Notice I am skipping some of the steps and only covering the key points. (eg. Add references, routes, register dependency etc...) They are covered previously.


I created a Misc Service and an interface for our platform.


Then I create brand specific services in the relevant projects.







Build Configuration

With the default Debug configuration, we will clone a set of new build configs per projects. In each build, we only include one brand dll at a time. (Only service dll in this example)






Only for the relevant build configurations of the brand projects, we will set the dll output path to the same as the Nop.Web project.



Controller

We setup a controller that will consume ITAMiscService and return the brand name. Notice how the controller does not have knowledge to the concrete class but only the ITAMiscService interface.



Hello World to Multi Tenancy


Who am I?

By changing the build configuration, I am able to change my workspace to the corresponding brand.







This is achieved by a MS Build trick.
  • The brand projects are not referenced anywhere (very important)
  • Therefore, when we build Debug, there will not be any brand specific dlls.
  • We used build configurations to trick MS Build to dump the brand dll to the web project for relevant brands.
  • Autofac reflection will pick up the brand dll if they exists, because of the way how Nop Dependency Register works.

Caution

  • Use clean solution in between the building of different brand environments (to delete any brand dlls).
  • Pay attention to the dlls in the Nop.Web bin folder. Occasionally the clean solution do not delete everything in the bin folder. Then we will need to manually clear this folder.
  • Always check Who am I to make sure you are working in the right brand.

Further Improvement

  • It is not so easy to deploy, as I need to build the project once per brand (so it will generate different dlls)
  • Unfortunately in practice, clean solution doesn't always work. For anyone who familiar with MS Build, we could create pre-build event to properly clean up the solutions (instead of relying on Visual Studio)
  • Combine MS Build with PowerShell, we can implement a way to "quick switch" dlls for different brands.

nopCommerce - Register route for admin controller

In nopCommerce, if you ever create a new or customize a controller in admin and getting 404 not found error? Read on...

With the approach of inheritance of customized controller, it is essential to register a new route for the new controller.

As a developer, it is natural to look at the RouteProvider.cs in the Admin project.



The original Nop Code looks like this which seems promising, but it doesn't do it.



The actual place to look at is hinted by the first image. The correct place to look at should be the Nop.Admin.AdminAreaRegistration.cs.


You can add your routes in here. In my example, it is registering a route for PictureController.


nopCommerce Customization with Controller Inheritance

Scope


The following article is based on the latest platform of nopCommerce as of today, version 3.1.

Scenario


Trying to customize the view by adding some extra custom text, the goal is ideally we will not change the existing controller in the nopCommerce platform to allow easier future upgrade.

Normally, we will create a widget plugin to do that, nice and neat that plugin is isolated from the page controller and allow easy plug and play. This would work fine for most people.

However, in certain scenarios, if the works required to be done in the controller is more complicated like introducing new services, security control etc, then we will need to modify the controller.

The gotcha is we don't want to change the existing controller for future upgrade, with IoC and OO architecture, one would think it is easy just to create a new controller to inherit from the existing controller for custom works, but life is not that easy.

Customization


CoreController

In CustomerController, we need to set the Register() as virtual. And that is only change required in our core controller, so that you can override its functionalities.

VirtualRegister


CustomController

Let's take CustomerController as an example and I want to override the Register().

I created my own CustomerController file inside a folder called Custom.

 CustomerController in Explorer

I gave it a namespace of Nop.Web.Controllers.Custom, and inherit from the Nop.Web.Controllers.CustomController.

controller

You will need to set your constructor same as the parent controller, and use the keyword base to inherit from the base controller, pretty standard C# inheritance syntax.

constructor

You may override the Register() like the following. In this example, we changed the value of the ViewBag.Title only, but as you can imagine, you can initialize new objects or call other services, all be achieved.

OverrideRegister

RouteProvider

RouteProvider

In the Nop.Web project, Infrastructure\RouteProvider.cs, we need to map the controller to a different namespace, so our custom controller will be used in place of the parent one.

RouteProvider Custom

View

For demonstration purpose, I updated the view as follow, but a more professional way by using theme can be found here.

view

Result

Compile the solution and head to register page, it should look like this.

result


IoC

What about IoC? How will the CustomController get instantiated? That is already handled by the Nop.Web.Framework.DependencyRegistrar that will register all controllers in the assembly.

Conclusion


As demonstrated, it is not horribly difficult to do customizations in nopCommerce, I only wish it could be easier with the RouteProvider part where I can instead just use the IoC to change the constructor only, then the Visual Studio would be smart enough to pick up the custom controller during compilation.