2

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?

Peter
  • 793
  • 3
  • 8
  • 31
  • This issue is the `DbContextOption` which is part of the constructor needed to initialize the context. – Nkosi Jul 20 '19 at 11:50
  • Where/How do you configure the connection string for the dbcontext? – Nkosi Jul 20 '19 at 11:52
  • I did not configure connection string as by convention NopCommerce will do that (so I think) – Peter Jul 20 '19 at 11:59
  • No it wont. Not the way you registered the context with the container. Where would it do that by convention? – Nkosi Jul 20 '19 at 12:00

1 Answers1

1

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

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Thanks for your feedback. Where will the above be implemented, inside the DependencyRegistrar class? There seems to be some methods e.g optionsBuilder.UseSqlServer() does not exist, optionsBuilder.Build() also does not exist. – Peter Jul 20 '19 at 16:49
  • @Peter yes within the registrar. – Nkosi Jul 20 '19 at 16:50
  • @Peter `UseSqlServer` is an extension method. You are most likely missing a using statement. I fixed the `Build` method issue. That was a typo. – Nkosi Jul 20 '19 at 17:02
  • I guess you should take a look at the snippet again. optionsBuilder.UseSqlServer() does not exist. It's not a case of missing any reference. – Peter Jul 20 '19 at 18:59
  • @Peter I am telling you that it is https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.sqlserverdbcontextoptionsextensions.usesqlserver?view=efcore-2.1 – Nkosi Jul 20 '19 at 19:00
  • @Peter even their sorce code uses it under the hood https://github.com/nopSolutions/nopCommerce/blob/develop/src/Presentation/Nop.Web.Framework/Infrastructure/Extensions/DbContextOptionsBuilderExtensions.cs#L29 – Nkosi Jul 20 '19 at 19:02