DI best practices

Smartstore relies on Autofac for dependency injection but modules typically register services in a Startup class derived from StarterBase. Inside ConfigureServices you can access the ASP.NET Core IServiceCollection and the IApplicationContext:

internal class Startup : StarterBase
{
    public override void ConfigureServices(IServiceCollection services, IApplicationContext appContext)
    {
        services.Configure<MvcOptions>(o =>
        {
            o.Filters.AddEndpointFilter<StripeScriptIncludeFilter, PublicController>().WhenNonAjax();
        });

        if (appContext.IsInstalled)
        {
            services.AddScoped<StripeHelper>();
        }

        // override an existing service: last registration wins
        services.AddScoped<IPriceCalculationService, CustomPriceCalculationService>();
    }
}

The following tips help keeping components testable and scopes well defined.

ICommonServices

ICommonServices bundles frequently used helpers such as IWorkContext, StoreContext, and the SmartDbContext. In controllers or view components deriving from SmartController or SmartViewComponent, it is property-injected and available via the Services member without constructor parameters:

public class SampleController : SmartController
{
    public IActionResult Index()
    {
        var customer = Services.WorkContext.CurrentCustomer;
        return View();
    }
}

Inject it manually only in other classes that need many of its helpers to keep dependencies explicit.

ILifetimeScopeAccessor

Use ILifetimeScopeAccessor to obtain or create scopes outside the request pipeline, e.g. for background jobs or CLI tools. BeginContextAwareScope returns a scope that reuses the HTTP scope when available or creates a new one otherwise.

using var _ = _scopeAccessor.BeginContextAwareScope(out var scope);
var service = scope.Resolve<IMyService>();

Work

Work<T> lazily resolves services from the current scope. It is useful when a service is required only occasionally or within loops, avoiding unnecessary constructions.

public class Sample
{
    private readonly Work<IMyService> _work;
    public Sample(Work<IMyService> work) => _work = work;

    public void DoSomething() => _work.Value.Execute();
}

By following these guidelines, modules stay lightweight and lifetimes remain predictable.

Last updated

Was this helpful?