Skip to content

Services & Dependency Injection

Learn how to create and use services in your nopCommerce plugins using dependency injection.

Overview

nopCommerce uses the built-in ASP.NET Core dependency injection (DI) container. This allows you to:

  • Register your own services
  • Inject existing nopCommerce services
  • Override default implementations
  • Manage service lifetimes

Registering Services

Using INopStartup

Create a startup class implementing INopStartup:

csharp
// Infrastructure/PluginStartup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Nop.Core.Infrastructure;

namespace Nop.Plugin.Widgets.MyPlugin.Infrastructure;

public class PluginStartup : INopStartup
{
    public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
    {
        // Register your services
        services.AddScoped<IMyService, MyService>();
        services.AddScoped<IDataProcessor, DataProcessor>();
        
        // Register with factory method
        services.AddScoped<IComplexService>(sp => 
            new ComplexService(
                sp.GetRequiredService<ILogger<ComplexService>>(),
                sp.GetRequiredService<ISettingService>()
            ));
    }

    public void Configure(IApplicationBuilder application)
    {
        // Configure middleware if needed
    }

    // Order of startup (lower = earlier)
    public int Order => 3000;
}

Startup Order

  • Core nopCommerce services: 0-100
  • Database services: 200-500
  • Custom plugins: 1000+

Use higher numbers to ensure core services are available.

Service Lifetimes

LifetimeBehaviorUse Case
AddScopedOne instance per HTTP requestMost services (recommended)
AddTransientNew instance each timeLightweight, stateless services
AddSingletonSingle instance for app lifetimeCaches, configuration
csharp
// Scoped - most common, one per request
services.AddScoped<IOrderProcessor, OrderProcessor>();

// Transient - new instance each injection
services.AddTransient<IRandomGenerator, RandomGenerator>();

// Singleton - single instance (careful with state!)
services.AddSingleton<IExternalApiClient, ExternalApiClient>();

Creating a Custom Service

Step 1: Define the Interface

csharp
// Services/IMyService.cs
namespace Nop.Plugin.Widgets.MyPlugin.Services;

public interface IMyService
{
    Task<IList<MyDto>> GetAllAsync();
    Task<MyDto> GetByIdAsync(int id);
    Task CreateAsync(MyDto dto);
    Task UpdateAsync(MyDto dto);
    Task DeleteAsync(int id);
}

Step 2: Implement the Service

csharp
// Services/MyService.cs
using Nop.Core.Caching;
using Nop.Data;

namespace Nop.Plugin.Widgets.MyPlugin.Services;

public class MyService : IMyService
{
    #region Fields

    private readonly IRepository<MyEntity> _repository;
    private readonly IStaticCacheManager _cacheManager;
    private readonly ILogger<MyService> _logger;

    #endregion

    #region Ctor

    public MyService(
        IRepository<MyEntity> repository,
        IStaticCacheManager cacheManager,
        ILogger<MyService> logger)
    {
        _repository = repository;
        _cacheManager = cacheManager;
        _logger = logger;
    }

    #endregion

    #region Methods

    public async Task<IList<MyDto>> GetAllAsync()
    {
        var cacheKey = new CacheKey("MyPlugin.All");
        
        return await _cacheManager.GetAsync(cacheKey, async () =>
        {
            var entities = await _repository.GetAllAsync(query => query);
            return entities.Select(MapToDto).ToList();
        });
    }

    public async Task<MyDto> GetByIdAsync(int id)
    {
        var entity = await _repository.GetByIdAsync(id);
        return entity != null ? MapToDto(entity) : null;
    }

    public async Task CreateAsync(MyDto dto)
    {
        var entity = MapToEntity(dto);
        await _repository.InsertAsync(entity);
        await _cacheManager.RemoveByPrefixAsync("MyPlugin");
    }

    public async Task UpdateAsync(MyDto dto)
    {
        var entity = await _repository.GetByIdAsync(dto.Id);
        if (entity == null) return;

        entity.Name = dto.Name;
        entity.UpdatedOnUtc = DateTime.UtcNow;

        await _repository.UpdateAsync(entity);
        await _cacheManager.RemoveByPrefixAsync("MyPlugin");
    }

    public async Task DeleteAsync(int id)
    {
        var entity = await _repository.GetByIdAsync(id);
        if (entity != null)
        {
            await _repository.DeleteAsync(entity);
            await _cacheManager.RemoveByPrefixAsync("MyPlugin");
        }
    }

    #endregion

    #region Utilities

    private MyDto MapToDto(MyEntity entity) => new()
    {
        Id = entity.Id,
        Name = entity.Name
    };

    private MyEntity MapToEntity(MyDto dto) => new()
    {
        Name = dto.Name,
        CreatedOnUtc = DateTime.UtcNow
    };

    #endregion
}

Injecting Services

In Controllers

csharp
public class MyPluginController : BasePluginController
{
    #region Fields

    private readonly IMyService _myService;
    private readonly ISettingService _settingService;
    private readonly ILocalizationService _localizationService;

    #endregion

    #region Ctor

    public MyPluginController(
        IMyService myService,
        ISettingService settingService,
        ILocalizationService localizationService)
    {
        _myService = myService;
        _settingService = settingService;
        _localizationService = localizationService;
    }

    #endregion

    #region Methods

    public async Task<IActionResult> Index()
    {
        var items = await _myService.GetAllAsync();
        return View(items);
    }

    #endregion
}

In View Components

csharp
public class MyWidgetViewComponent : NopViewComponent
{
    #region Fields

    private readonly IMyService _myService;
    private readonly IStoreContext _storeContext;

    #endregion

    #region Ctor

    public MyWidgetViewComponent(
        IMyService myService,
        IStoreContext storeContext)
    {
        _myService = myService;
        _storeContext = storeContext;
    }

    #endregion

    #region Methods

    public async Task<IViewComponentResult> InvokeAsync()
    {
        var store = await _storeContext.GetCurrentStoreAsync();
        var items = await _myService.GetAllAsync();
        
        return View(new WidgetModel { Items = items, StoreName = store.Name });
    }

    #endregion
}

Common nopCommerce Services

Core Services

ServicePurpose
IStoreContextCurrent store information
IWorkContextCurrent customer/language/currency
IWebHelperHTTP utilities, URLs
ILoggerLogging

Data Services

ServicePurpose
IRepository<T>Generic data access
IProductServiceProduct operations
ICustomerServiceCustomer operations
IOrderServiceOrder operations

Configuration Services

ServicePurpose
ISettingServicePlugin/store settings
ILocalizationServiceTranslations
INotificationServiceAdmin notifications

Example: Using Multiple Services

csharp
public class OrderProcessor
{
    #region Fields

    private readonly IOrderService _orderService;
    private readonly ICustomerService _customerService;
    private readonly INotificationService _notificationService;
    private readonly ILogger<OrderProcessor> _logger;

    #endregion

    #region Ctor

    public OrderProcessor(
        IOrderService orderService,
        ICustomerService customerService,
        INotificationService notificationService,
        ILogger<OrderProcessor> logger)
    {
        _orderService = orderService;
        _customerService = customerService;
        _notificationService = notificationService;
        _logger = logger;
    }

    #endregion

    #region Methods

    public async Task ProcessOrderAsync(int orderId)
    {
        try
        {
            var order = await _orderService.GetOrderByIdAsync(orderId);
            var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId);
            
            // Process the order...
            
            _notificationService.SuccessNotification($"Order {order.Id} processed successfully");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to process order {OrderId}", orderId);
            throw;
        }
    }

    #endregion
}

Overriding Default Services

Replace a nopCommerce service with your own implementation:

csharp
public class PluginStartup : INopStartup
{
    public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
    {
        // Override the default tax service
        services.AddScoped<ITaxService, MyCustomTaxService>();
    }

    public int Order => 10000; // Run after default registrations
}

Best Practices

  1. Use interfaces - Always inject interfaces, not concrete classes
  2. Prefer AddScoped - Most services should be scoped to request
  3. Avoid circular dependencies - Restructure if services depend on each other
  4. Use constructor injection - Avoid service locator pattern
  5. Cache expensive operations - Use IStaticCacheManager

Next Steps

Released under the nopCommerce Public License.