The following is a list of common questions asked by developers working with nopCommerce. It also describes some of the architectural choices the nopCommerce team made.
What are the requirements?
nopCommerce technology and system requirements can be found here.
How can developers contribute changes to the nopCommerce project?
nopCommerce manages a Codeplex Mercurial repository that users can view here. With a public repository users can see upcoming changes and previous design decisions. Please find more information about Codeplex Mercurial support for forks here or here. Developers can easily upload their plugin or language pack to our extensions section and share it with other users. To upload an extension visit the my account page in a web browser, select the tab labeled "Your contributions and extensions", and click the "Upload a new extension" button.
How do I report a bug?
nopCommerce uses Codeplex as the official issue tracker, and if a bug is found it can be reported to the development team by creating a new task in Codeplex. Developers and users can also post about newly discovered bugs in our Bug Reports forum. There is a good chance your bug has already been reported and because of this, it is important to verify the bug has not already been reported. Duplicated bug reports are distracting and reduce the time available for new development and bug fixes.
The nopCommerce Data Access Layer
The Nop.Data project contains a set of classes and functions for reading from and writing to a database or other data store. The Nop.Data library helps separate data-access logic from your business objects. NopCommerce uses the Entity Framework (EF) Code-First approach. Code-First allows a developer to define entities in the source code (all core entities are defined in the Nop.Core project), and then use EF to generate the database from the C# classes. That's why it's called Code-First. You can then query your objects using LINQ, which translates to SQL behind the scenes and is executed against the database. NopCommerce uses a fluent code API to fully customize the persistence mapping. You can find more about Code-First here or here.
Inversion of Control and Dependency Injection
Inversion of Control and Dependency Injection are two related ways to break apart dependencies in your applications. Inversion of Control (IoC) means that objects do not create other objects on which they rely to do their work. Instead, they get the objects that they need from an outside source. Dependency Injection (DI) means that this is done without the object intervention, usually by a framework component that passes constructor parameters and sets properties. Martin Fowler has written a great description of Dependency Injection or Inversion of Control. I'm not going to duplicate his work, and you can find his article here. nopCommerce uses Autofac library as its IoC container. Once a service and an appropriate interface, which the service implements, are written you should register them in any class implementing the IDependencyRegistrar interface (Nop.Core.Infrastructure.DependencyManagement namespace). For example, all core nopCommerce services are registered in the DependencyRegistrar class located in the Nop.Web.Framework library.
public class DependencyRegistrar : IDependencyRegistrar
{
public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
{
builder.Register(c => c.Resolve<HttpContextBase>().Request)
.As<HttpRequestBase>()
.InstancePerHttpRequest();
...
}
}
You can create as many dependency registrar classes as you need. Each class implementing IDependencyRegistrar interface has an Order property. It allows you to replace existing dependencies. To override nopCommerce dependencies, set the Order property to something greater than 0. nopCommerce orders dependency classes and runs them in ascending order. The higher the number the later your objects will be registered.
How do I register new routes?
ASP.NET routing is responsible for mapping incoming browser requests to particular MVC controller actions. You can find more information about routing here. NopCommerce has an IRouteProvider interface which is used for route registration during application startup. All core routes are registered in the RouteProvider class located in the Nop.Web project.
public partial class RouteProvider : IRouteProvider
{
public void RegisterRoutes(RouteCollection routes)
{
//home page
routes.MapLocalizedRoute("HomePage",
"",
new { controller = "Home", action = "Index"},
new[] { "Nop.Web.Controllers" });
You can create as many RouteProvider classes as you need. For example, if your plugin has some custom routes which you want to register, then create a new class implementing the IRouteProvider interface and register the routes specific to your new plugin.
Data Validation
Data validation is the process of ensuring that a program operates on clean, correct and useful data. Most .NET developers use Data Annotation Validators. But nopCommerce uses Fluent Validation. It's a small validation library for .NET that uses a fluent interface and lambda expressions for building validation rules for your business objects. You have to complete two steps in order to add a validation to some models in nopCommerce:
1. Create a class derived from AbstractValidator class and put all required logic there. See the image below to get an idea:
public class AddressValidator : AbstractValidator<AddressModel>
{
public AddressValidator(ILocalizationService localizationService)
{
RuleFor(x => x.FirstName)
.NotEmpty()
.WithMessage(localizationService.GetResource("Address.Fields.FirstName.Required"));
}
}
2. Annotate your model class with the ValidatorAttribute. Refer to the example below for guidance.
[Validator(typeof(AddressValidator))]
public class AddressModel : BaseNopEntityModel
{
ASP.NET will execute the appropriate validator when a view model is posted to a controller.
Scheduled Tasks
With Scheduled tasks, you can schedule a task to run at certain periods in the background. For example, nopCommerce sends queued emails periodically. Tasks are being run on a separate thread that comes from the ASP.NET thread pool. The basic steps to create a new task are:
- Define a class which implements ITask interface. It has only one method that takes no arguments; Execute. As you guessed this method is invoked when the task should be run.
- To schedule a task the developer should insert a new ScheduleTask record into the appropriate database table. You can use IScheduleTaskService for inserting such a record.
Exposing and Handling Events
Events are notifications broadcasted to interested parties. Events are triggered on data changes like inserts, updates and deletes. nopCommerce allows developers to "listen" for events they might be interested in. There are two ways a developer will work with events. A developer will either want to publish events for listeners to consume, or subscribe to events other developers will have programmatically published.
- To publish an event a developer will need to obtain an instance of IEventPublisher and call the Publish method with the appropriate event data.
- To listen for an event the developer will want to create a new implementation of the generic IConsumer interface. Once a new consumer implementation has been created nopCommerce uses reflection to find and register the implementation for event handling.
Settings API
Like any other website platforms nopCommerce has settings such as "Store name" or "One page checkout enabled". There are two ways to manage settings in nopCommerce.
You can use GetSettingByKey and SetSetting methods of ISettingService implementation for loading and saving individual settings. The preferred approach for handling settings in nopCommerce is to create a new implementation of the ISettings interface. Each setting will be represented by a C# property and developers should rely on setting classes to be injected via the constructor when they are required. Below is an example settings class.
public class MediaSettings : ISettings
{
public int AvatarPictureSize { get; set; }
public int ProductThumbPictureSize { get; set; }
public int ProductDetailsPictureSize { get; set; }
public int ProductThumbPictureSizeOnProductDetailsPage { get; set; }
public int ProductVariantPictureSize { get; set; }
public int CategoryThumbPictureSize { get; set; }
public int ManufacturerThumbPictureSize { get; set; }
public int CartThumbPictureSize { get; set; }
public bool DefaultPictureZoomEnabled { get; set; }
public int MaximumImageSize { get; set; }
}