How to - Extending nopCommerce v2.0

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
12 years ago
I need to add a couple of tables/entities (a ProductKey and  ProductRigistration).  These need to 'relate' to existing entities (ie conceptually, order, customer etc) (seen article on adding entities, but I need to see how I can make them relate to existing entities)  I will be using these new entities from a web service (I see sample web service), Admin web mode and customer web mode.

ProductKey and ProductRegistration store both used and available to be sold product keys and registration ties product keys to user pc/customer.

Web service allows product registration from product.
Admin maintains the database and creating new product keys etc
Customer modes allows customers to unregister product keys etc, also sales process will select an available product key).

What's the best way to implement this in terms of plugins?  A separate pluging for database (and admin), web service, and customer mode operations, or can one plugin do it all?

Bernie
12 years ago
bpschoch wrote:
What's the best way to implement this in terms of plugins?  A separate pluging for database (and admin), web service, and customer mode operations, or can one plugin do it all?


I'm thinking that a single plugin should be able to handle the bulk of your work. Although I've only done one plugin so far, I was able to get  a single plugin to handle everything I was trying to do--it has its own database table, with admin crud and frontend search/display features, as well as tying into the existing nop address model for use by my own dealers information. Saying that however, I didn't need to display any of my information on other existing nop pages. The admin was it's own new section, and the frontend displays all had their own seperate pages.

If it helps you at all, I've included my plugin's structure below.

Project: Nop.Plugin.Misc.Dealers
Controllers
   DealerAdminController.cs
   DealerController.cs
   DealerLocatorController.cs
Data
   DealerObjectContext.cs
   DealerRecordMap.cs
Domain
   DealerRecord.cs
Models
   DealerAdminModel.cs
   DealerListModel.cs
   DealerLocatorModel.cs
   DealerModel.cs
Services
   DealerService.cs
   IDealerService.cs
Validators
   DealerValidator.cs
Views
   Dealer
      Index.cshtml
      List.cshtml
   DealerAdmin
      _CreateOrUpdate.cshtml
      Create.cshtml
      Edit.cshtml
      List.cshtml
    DealerLocator
      Index.cshtml
   web.config
DealerPlugin.cs
DependencyRegistrar.cs
Description.txt
HtmlExtensions.cs
RouteProvider.cs
web.config
12 years ago
Ok thanks, your post has demonstrated very well the flexiblity of plugins.

So I have my data entities mostly worked out that I need.  Some of these entities need to be 'anchored' off of existing nopCommerice entities e.g. Affliate, Product, Customer.  Rather than modify the database structure, I want to create parallel entities for each of them using "Shared Primary Key Association' concept, where my new entities share the same primary key as the existing entity (in a 0..1 relationship).  I'm thinking I may need to add some 'navigational' fields to the existing entities (but these don't cause new columns to be added to database), so I need to 'modify' the existing entity classes which I can do elegantly with partial classes.  However, I can't just place these partial classes in my plugin because you can't do partial classes across projects/assemblies.  So my questions is where is the appropriate places to add these partial class extentions to mimimize upgrade woes?  Should I create a new subdirectory under each of the major directors in the library classes (I.E.) MyExtentionClasses under Domain?  Or should I just for go any navigational help or is there a better way?  I'm still working out the finer details of EF 4.1, but now that I think about it, I wonder of extention functions would help here?

Bernie
12 years ago
The plugin I did did not have to extend any of the existing entities, so I have no experience doing this through a plugin only. The closest I came to something like this was trying to override the default address admin validator that was also used by my plugin. Unfortunately, I was unable to figure out how to do this either in the time I had to spend on it.

I have extended entities by editing the nop files directly, but this is easy enough to do (there is a tutorial on how to do that if you haven't seen it already at http://blog.csharpwebdeveloper.com/2011/09/10/updating-an-existing-entity-in-nopcommerce-2-0/)

If someone knows how to extend an existing nop entity/db table through a plugin, or even override an existing nop validator through a plugin only, I'd also love to hear how it's done.
12 years ago
And the question also, if any of my entities make reference back/forth to nopCommerce entities, I think that they (maps/entities) may need to be listed in NopObjectContext.cs.  NopObjectContext.OnModelCreating() dynamically looks for all the EntityTypeConfiguration<> types in nop.data assembly (ie it's own ***Map classes).

So the question is what is the best way to get maps/entities into nop.data cleanly (minimizing changes), or should/can there be a way for NopObjectContext.OnModelCreating() to be searching for the pluging assemblies also (maybe only those marked with an attribute)?

Then there is the question of extending existing entities (Domain) in a safe manner which can be done by creating a special directory for extended entities (ExtendedDomain?) using partial classes.  Then what to do about mapping?  Create say a new CustomerMapMine that subclasses CustomerMap and make sure that only the most derived class is loaded by NopObjectContext.OnModelCreating() (ie CustomerMapMine and not CustomerMap).  These partial classes have to be in the same assembly as its other half.

The modifications I'm talking about for existing entities are limited to adding ICollection<ProductLicense> Licenses to say a Customer entitie (which doesn't add a new field in SQL if I use a shared key on my entity).  This way I can have my ProductLicense table/entity and be able to use the collection (defined via addition partial class for Customer).

What do people think? Developers?

Bernie
12 years ago
bpschoch wrote:
And the question also, if any of my entities make reference back/forth to nopCommerce entities, I think that they (maps/entities) may need to be listed in NopObjectContext.cs.  NopObjectContext.OnModelCreating() dynamically looks for all the EntityTypeConfiguration<> types in nop.data assembly (ie it's own ***Map classes).

So the question is what is the best way to get maps/entities into nop.data cleanly (minimizing changes), or should/can there be a way for NopObjectContext.OnModelCreating() to be searching for the pluging assemblies also (maybe only those marked with an attribute)?

Then there is the question of extending existing entities (Domain) in a safe manner which can be done by creating a special directory for extended entities (ExtendedDomain?) using partial classes.  Then what to do about mapping?  Create say a new CustomerMapMine that subclasses CustomerMap and make sure that only the most derived class is loaded by NopObjectContext.OnModelCreating() (ie CustomerMapMine and not CustomerMap).  These partial classes have to be in the same assembly as its other half.

The modifications I'm talking about for existing entities are limited to adding ICollection<ProductLicense> Licenses to say a Customer entitie (which doesn't add a new field in SQL if I use a shared key on my entity).  This way I can have my ProductLicense table/entity and be able to use the collection (defined via addition partial class for Customer).

What do people think? Developers?

Bernie


To get full support of bidirectional relationships you would need to make modifications to the Nop.Core/Nop.Data libraries. You could include nopCommerce navigation properties on your own entities by writing your own ObjectContext class that knows where to look for additional mapping files. You would also need to change the dependency registrar so that it no longer uses the NopObjectContext.

One thing to think about though is that if your object model requires complex relationships with nopCommerce entities it may not be a case where plugins work, and should be treated as an enhancement to the core of the nopCommerce framework.
12 years ago
OK thanks, this is useful info for me.  

Since I believe that other developers may run into the same needs, I am thinking outloud about how nopCommerce may be extended to these kinds of database extentions more easily.  My thinking on this so far is there there could be 2 kinds of extendability: One is using plugins to extend functionallity and using private tables (as is now) and second is being able to extent the nopCommerce tables in way that least interferes with releases.  I've added a suggested work item http://nopcommerce.codeplex.com/workitem/10386 that makes that suggestion.  I'm by no means an expert the internals of nopCommerce but have begun to develop some ideas on this could be done (at least on the conceptual level).

Add a a new directory called Extentions (Nop.Core.Domain.Extentions) where all the external extentions go.  I would create a subdirectory to Extentions (e.g. mystuff or Nop.Core.Domain.Extentions.mystuff) where I would place all my domains and partial classes to existing domains where 'mystuff' is the name of the extention package (this is for concept #2 above)

Likewise I would create a new directory (Nop.Data.Mapping.Extentions) where all the new mappings go.  There would need to be a mechanism to allows one to add additional mappings to existing entities.  Perhaps adding a subclass to add the mappings but then need to make sure subclass is called vs original base class (e.g. CustomerMapping vs CustomerMappingMystuff)

Instead of creating a new db context and having to change the registar, wouldn't it be better to allow some extendability functionality to the NopObjectContext (so that it can find these mappings ie explicity look for mapping class at Nop.Data.Mapping.Extentions.xxxx)?

My goal here is to make minimize conflcts with upgrades and also to define a clear path to developers on how to make data modifications.

A plug-in would still be required to supply the functionallity that these new entities provide.  For simple extentions (functionality with simple data changes), one would just use plugins,  If you need to add new fields or add new tables that intertwine with existing tables, you would use this new extention methods to make the data changes and still use plugins to supply the functionallity changes.

Bernie
12 years ago
Just thinking out loud here, but if you were adding new tables and have the "Shared Primary Key Association' concept, maybe something like this would work.

In my plugin I have a dealer table that is new, but in it I have an 'AddressId' column, that contains the id that coresponds to the existing nop address table.

When I say, want to update my dealer, I just include the existing nop address services/models/etc in my plugin. Then I use my own plugin to first update my own dealer record, then I use the existing address service to update the address table.

If I wanted to extend the address table, perhaps the same idea would work? I would make a new "address_myfiends" table, which would share the same primary key as the existing address table, to which I would write my own custom services. Then when I wanted to update an address, I could use the existing address service to first update the main address record, then use my own service to update the records in my new table using the same id for both.

In this case my plugin would handle my "address_myfiends" table, and the existing nop services I include would handle the "address" table.

This way might be limiting in some regards, but if you could get it to work at least you wouldn't need to change the nop core files.
12 years ago
Thanks for the input.  One of the reasons for integrating the tables with the existing ones is that EF 4.1 (Entity Framekwork), will automatically create the foreign key relationships between you tables and the existing ones.  Although you can go create an address entry and have your entity point to it, there is no database 'knowledge' of this.  Generally in databases, you want to give the database as much knowledge of the relationships of entities so it can ensure data entegrity (this is why I'm going on with integrating it all together).

This is what've been able to do so far without touching a single line of nopCommerce base code:
1) I've add 5 new tables for licensing, three with direct relationships with existing tables (Affiliate, Product, and Customer)

2) I've done this by adding a new  'Extentions' directory to both the Nop.Core (Domains directory) and Nop.Data(Mapping directory).

3) Under both of those directories, I've created a 'Licensing' directory (this allows other extentions to co-exist as different directory names under the 'Extentions' directories.

4) I've added my new domain classes to the Nop.Core.Domain.Extentions.Licensing directory using that same name space (e.g. License).  I've create these in the same manner as other Domain classes in nopCommerce (ie same base classes, using's etc)

5) I've added partial class for existing entities (domain entries) that I want to modify.  Because the nopCommerce class all already 'partial' there is no problem of adding more to the class by creating your own partial version to it.  So for example, I need to have a Licenses collection in the existing Customer entity, so I created my own Customer.cs in the Nop.Core.Domains.Extentions.Licensing directory:


namespace Nop.Core.Domain.Customers
{
  /// <summary>
  /// Represents a customer
  /// </summary>
  public partial class Customer : BaseEntity
  {
    ICollection<License> _licenses;

    public virtual ICollection<License> Licenses
    {
      get { return _licenses ?? (_licenses = new List<License>()); }
      set { _licenses = value; }
    }
  }
}


The net effect of these 2 partial classes (both the nopCommerce verson and mine) is a merge of the two done at compile time (note these 2 partials have to be in the same assembly)

6) In the above examples (and it turns out to be true for my other partial classes), all I needed was to add a 'Navigation' property to the entity (which doesn't modify the sql database tables).

7) In the Nop.Data (Mapping.Extentions.Licensing directory), I added the "Mapping' classes for my new domain entities e.g.  LicenseMap.cs.  The entry that establishes the ForeignKey relations ship to Customer looks like this:


//  Customer foreign key
    this.HasOptional(L => L.Customer) // unsold license don't yet have a relationship with a customer so this is optional
         .WithMany(C => C.Licenses)  // this refers back to the Licenses property in my new partial Customer.cs class
         .HasForeignKey(L => L.CustomerId);  // this refers to the int field I have in my license.cs class that has the ID for the Customer


8) As long as the relationship of an existing entity is one to zero or one or many to a new entity (ie a customer can have many licenses), then I don't believe that you need to modify the mapping class for the existing entity.  With the partial class, you can even add a new field (but this will change the database structure).  The only issue (that I'm not running into because I don't need it) is that if you have a situation where an existing entity has a relationship which in it's self is a many.  Then you would need to modify the existing tables 'mapping' class.

One of my goals in making these changes is to make then in such a way as to minimize any conflicts with new versions of nopCommerce (obviously, the more tables I need to interact with the higher probability of potential conflict, but I do believe that if you stick to the core common ones, then there is a lower probability).  The one things that is missing for a complete solution is how to handle the situation where you need to add additional 'mappings' to an existing entity.  I think that this could be done with a small change in how NopObjectContext.OnModelCreating works.  What I propose is that if you have to extend the mapping of an existing entity map, that you create a subclass the original map class with some sort of naming convention (some sort of naming convention is required in order for my proposed changes to the OnModelCreating methods to work).  OnMethodCreating method in (NopObjectContext) current uses reflection to find all classes that have a direct generic superclass of 'EntityTypeConfiguration<>' in the assembly that contains the map classes (Nop.Data).  The change required would be to first find those classes that have 'EntityTypeConfiguration<>' as a superclass but indirectly (ie the exiting CustomerMap class derives from the EntityType.. class and then the CustomerAddMap?? class derives from the CustomerMap class).  So we examine and use those first and discard the classes between it and the 'EntityType...' superclass.
--------------------------
I believe that I can then add my services, controllers etc back in my plugin (haven't done that yet).

So in conclusion, I've proposed 2 ares for extention: a new way for database changes (new tables, relationships etc) and the existing 'plugin' method for adding new processing, business rules, views etc
-----------------------------------------------------------------------------------------------

Unit testing:
So far I've used the existing tests (because as a by product they create a database with all the new tables, but right now as they are setup, they use SqlCE database which doesn't show all the foreign key relations ships.

I will be adding unit tests for my database extentions by referencing the exsing Nop.Core.Test and Nop.Data.Tests to make use of their common test base classes in a new project ( maybe 2 - one for Domains and one for mappings) and then another for my plugin.

Obviously I believe that what I've proposed here is a cleaner way to extend nopCommerce (especially in the database area)

Bernie
11 years ago
Geralt wrote:
What's the best way to implement this in terms of plugins?  A separate pluging for database (and admin), web service, and customer mode operations, or can one plugin do it all?


I'm thinking that a single plugin should be able to handle the bulk of your work. Although I've only done one plugin so far, I was able to get  a single plugin to handle everything I was trying to do--it has its own database table, with admin crud and frontend search/display features, as well as tying into the existing nop address model for use by my own dealers information. Saying that however, I didn't need to display any of my information on other existing nop pages. The admin was it's own new section, and the frontend displays all had their own seperate pages.

If it helps you at all, I've included my plugin's structure below.

Project: Nop.Plugin.Misc.Dealers
Controllers
   DealerAdminController.cs
   DealerController.cs
   DealerLocatorController.cs
Data
   DealerObjectContext.cs
   DealerRecordMap.cs
Domain
   DealerRecord.cs
Models
   DealerAdminModel.cs
   DealerListModel.cs
   DealerLocatorModel.cs
   DealerModel.cs
Services
   DealerService.cs
   IDealerService.cs
Validators
   DealerValidator.cs
Views
   Dealer
      Index.cshtml
      List.cshtml
   DealerAdmin
      _CreateOrUpdate.cshtml
      Create.cshtml
      Edit.cshtml
      List.cshtml
    DealerLocator
      Index.cshtml
   web.config
DealerPlugin.cs
DependencyRegistrar.cs
Description.txt
HtmlExtensions.cs
RouteProvider.cs
web.config


Can you share the code simple i don't mean to copy . I am just trying to make a blog plugin that shows the social links and short info of the author and user profiles but unfortunately i am bit confused where i can began so i had created the Database using the plugin but i don't know how to  override the existing views of admin as well as front-end views.

or can help me out to add more fields to customer table in database using PLUGIN. I just want to add social links and short info thing to customer table.

Thanks
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.