Search code examples
c#dependency-injectionautofacnopcommerce-4.2

NopCommerce 4.20 Plugin development error with Dependency Injection


I have a NopCommerce Plugin development with the dBContext name BookAppointmentDBContext and Dependency Registrar DependencyRegistrar see my snippet below.

public class DependencyRegistrar : IDependencyRegistrar
{
    private const string CONTEXT_NAME ="nop_object_context_bookappointment";
    public  void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)
    {
        builder.RegisterType<BookAppointmentService>().As<IBookAppointmentService>().InstancePerLifetimeScope();
        //data context
        builder.RegisterPluginDataContext<BookAppointmentDBContext>(CONTEXT_NAME);
        //override required repository with our custom context
        builder.RegisterType<EfRepository<CarInspectionModel>>()
            .As<IRepository<CarInspectionModel>>()
            .WithParameter(ResolvedParameter.ForNamed<IDbContext>(CONTEXT_NAME))
            .InstancePerLifetimeScope();
    }

    public int Order => 1;
}

and BookAppointmentDBContext class below

public class BookAppointmentDBContext : DbContext, IDbContext
{
    #region Ctor
    public BookAppointmentDBContext(DbContextOptions<BookAppointmentDBContext> options) : base(options)
    {

    }
  /*the other implementation of IDbContext as found in http://docs.nopcommerce.com/display/en/Plugin+with+data+access*/
}

Also, I have a BasePluglin class with

public class BookAppointmentPlugin : BasePlugin
{
    private IWebHelper _webHelper;
    private readonly BookAppointmentDBContext _context;

    public BookAppointmentPlugin(IWebHelper webHelper, BookAppointmentDBContext context)
    {
        _webHelper = webHelper;
        _context = context;
    }

    public override void Install()
    {
        _context.Install();
        base.Install();
    }
    public override void Uninstall()
    {
        _context.Uninstall();
        base.Uninstall();
    }
}

I keep having this error: ComponentNotRegisteredException: The requested service 'Microsoft.EntityFrameworkCore.DbContextOption 1[[Nop.Plugin.Misc.BookAppointment.Models.BookAppointmentDBContext, Nop.Plugin.Misc.BookAppointment, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.

I have BookAppointmentDBContext registered but the error state otherwise. Any idea what I did wrongly?


Solution

  • This issue is the lack of a registered DbContextOption which is part of the constructor needed to initialize the target db context.

    Internally this is what RegisterPluginDataContext does.

    /// <summary>
    /// Represents extensions of Autofac ContainerBuilder
    /// </summary>
    public static class ContainerBuilderExtensions
    {
        /// <summary>
        /// Register data context for a plugin
        /// </summary>
        /// <typeparam name="TContext">DB Context type</typeparam>
        /// <param name="builder">Builder</param>
        /// <param name="contextName">Context name</param>
        public static void RegisterPluginDataContext<TContext>(this ContainerBuilder builder, string contextName) where TContext : DbContext, IDbContext
        {
            //register named context
            builder.Register(context => (IDbContext)Activator.CreateInstance(typeof(TContext), new[] { context.Resolve<DbContextOptions<TContext>>() }))
                .Named<IDbContext>(contextName).InstancePerLifetimeScope();
        }
    }
    

    Source

    Note it is trying to resolve DbContextOptions<TContext> when activating the context.

    You would need to build the db context options and provide it to the container so that it can be injected into the context when being resolved.

    private const string CONTEXT_NAME ="nop_object_context_bookappointment";
    public  void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config) {
    
        //...code removed for brevity
    
        var optionsBuilder = new DbContextOptionsBuilder<BookAppointmentDBContext>();
        optionsBuilder.UseSqlServer(connectionStringHere);
        DbContextOptions<BookAppointmentDBContext> options = optionsBuilder.Options;
        builder.RegisterInstance<DbContextOptions<BookAppointmentDBContext>>(options); 
    
        //data context
        builder.RegisterPluginDataContext<BookAppointmentDBContext>(CONTEXT_NAME);
    
        //...code removed for brevity
    }
    

    Reference Configuring a DbContext