Proposal of a modified nopCommerce architecture to extend entities

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
9 years ago
Hey folks,

often I got problems to extend an entity with a custom attribute in nopCommerce while developing plugins. The tutorial https://www.nopcommerce.com/docs/73/updating-an-existing-entity-how-to-add-a-new-property.aspx is editing the core of nopCommerce. To make use of a new version of nopCommerce I need to backup all edited files and manually merge them onto the new version.

Another solution is to the usage of generic attributes. In this case I can’t use validations or database mappings. Additionally all attributes are saved as a string and slows down the performance of nopCommerce if a huge of these generic attributes is used.

My proposal for extending entities is described into four steps and shows up a modified architecture of nopCommerce that could be used in next major versions of nopCommerce:

Step 1: Extending Entities

First I need to declare the extending properties to make use of them in the business logic.
All entities are located in nop.core.Domain and all these classes are partial classes.
So it is possible to extend an entity with a partial class without touching the original file.


Step 2: Extending Entity-Maps

When I have to declare some properties or relationships for the database I need a look at the entity maps. They are located in Nop.Data.Mapping. In this scenario I can’t extend the class with a partial-class because all declarations are done in a constructor.

The only solution is to create a separate abstract base class, let’s say NopCommerceTypeConfiguration, because the mapping classes extend  EntityTypeConfiguration<T>. So now I can declare a protected virtual void PostInitialize() method in the new base-class which is calling the method in the constructor.


namespace Nop.Data.Customization.Mapping.Configuration
{
  public abstract class NopCommerceTypeConfiguration<T> : EntityTypeConfiguration<T> where T : class
  {
    protected NopCommerceTypeConfiguration()
    {
      PostInitialize();
    }
  
    protected virtual void PostInitialize() { }
  }
}


Furthermore I can go ahead and create partial-extended classes and make use of the PostInitialize method to declare additional restrictions. E.g. Nop.Data.Mapping.Catalog.ProductMap:


namespace Nop.Data.Mapping.Catalog
{
  public partial class ProductMap : NopCommerceTypeConfiguration<Product>
  {
    public ProductMap()
    {
      this.ToTable( "Product" );
      this.HasKey( p => p.Id );
      /* ... */
    }
  }
}


the partial extended classes could be looks like:


namespace Nop.Data.Mapping.Catalog
{
  public partial class ProductMap
  {
    protected override void PostInitialize()
    {
      this.Property( p => p.CustomProperty).HasMaxLength( 5 );
      /* ... */
    }
  }
}


The only important thing is to change the type from EntityTypeConfiguration to NopCommerceTypeConfiguration in the OnModelCreating method of the class Nop.Data.NopObjectContext.


protected override void OnModelCreating( DbModelBuilder modelBuilder )
    {
      /* ... */
      var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
      .Where( type => !String.IsNullOrEmpty( type.Namespace ) )
      .Where( type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof( NopCommerceTypeConfiguration<> ) );
      /* ... */
    }



Step 3: Extending Entity-Models

Now I need to edit the entity model. Unfortunately all entity models are not partial and also make use of declarations in constructors. So I need to edit all models located in Nop.Admin.Models, Nop.Web.Models and Nop.Framework.Models to be partial.

In this case all models extend the Nop.Web.Framework.Mvc.BaseNopEntityModel class. This class is partial and does not have a constructor. So this allows me to create a partial-extended class and create a PostInitialize Method like in Step 2 and call the method in the created constructor.


namespace Nop.Web.Framework.Mvc
{
  public partial class BaseNopEntityModel
  {
    public BaseNopEntityModel()
    {
      PostInitialize();
    }
  
    protected virtual void PostInitialize() { }
  }
}


Next I can simply partially extend the models.


Step 4: Extending Validators

If I need to declare some validation rules for my additional properties I need to extend the validators located in Nop.Admin.Models, Nop.Web.Models and Nop.Framework.Models.

Here I also need to edit all validators to be partial and insert a new abstract base class that contains a PostInitialize method called in the constructor between the validator class and the extending AbstractValidator<T> class of the Fluent Validation Framework like step 2.


To put all in a nutshell

If the following things are given it is much easier and much safer to extend and entity in nopCommerce. This make it uncomplicated to use continuous integration for updating new versions of nopCommerce without backup all edited files and manual merging them back to the new version.

- Entity-Maps, Validators needs an abstract base class between the actual extending with a constructor that is calling a protected virtual method.

- Change using type in OnModelCreating method in class Nop.Data.NopObjectContext.

- Declare all Entity-Models as partial.

- Create a PostInitialisze method that is called in the created constructor in class Nop.Web.Framework.Mvc.BaseNopEntityModel.
9 years ago
It all looks very good. The work item is here. Thanks a lot for suggestions.
9 years ago
This proposal looks pretty cool. At the moment, it is really curcuitous to extend entities in a "clean" way. (as I described in my post https://www.nopcommerce.com/boards/t/29527/extending-entities-without-editing-the-nopcommerce-330-core.aspx). This solution looks much better. I hope it will find it's way into the nopCommerce core.
9 years ago
I agree that this seems more elegant than using Generic attributes.  It just feels right.
9 years ago
This is a really cool suggestion. It would be very nice if this would be realized in nopCommerce.
9 years ago
Done. Please see changeset 430d037b2ef4.

Again thanks a lot for this contribution!
9 years ago
awesome Andrei. thank you.
9 years ago
I do a lot of work on NopCommere (Andrey it's fantastic thank you) and this sounds fantastic, but I am having trouble wrapping my head around how it works from the plugin writer's point of view. If somebody could explain it that would be great.
9 years ago
Sure wish somebody could do a write up on this
9 years ago
a.m. wrote:
Done. Please see changeset 430d037b2ef4.

Again thanks a lot for this contribution!


I proposed something very similar almost 2 years ago. If this is done, you can close this item: https://nopcommerce.codeplex.com/workitem/11274
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.