Autofac Unable to resolve service for type 'log4net.ILog'

133 Views Asked by At

I have ASP.NET MVC app which was working fine but once I upgraded Autofac and did the change for Log4Net middleware example it keeps failling if I try to Resolve ILog.

We use FluentMigrator and I am getting

Unable to resolve service for type 'log4net.ILog' while attempting to activate 'Migrations.Maintenance.BeforeAll.FlushRedisOnVersionUpdate'.

it seems that the middleware cannot resolve log4net.ILog which is injected inside the constructor.

internal class OrderedMaintenanceLoader : IMaintenanceLoader
{
    #region Private fields

    private readonly IMaintenanceLoader _inner;
    private readonly CurrentReleaseMigrationStage _releaseMigrationStage;

    #endregion
    #region Ctors

    public OrderedMaintenanceLoader(IMaintenanceLoader inner, CurrentReleaseMigrationStage releaseMigrationStage)
    {
        _inner = inner ?? throw new ArgumentNullException(nameof(inner));
        _releaseMigrationStage = releaseMigrationStage ?? throw new ArgumentNullException(nameof(releaseMigrationStage));
    }

    #endregion
    #region IMaintenanceLoader Members

    public IList<IMigrationInfo> LoadMaintenance(MigrationStage stage)
    {
        return _inner.LoadMaintenance(stage)
            .Where(info => _releaseMigrationStage.CanExecuteMaintenanceMigration(info.Migration))
            .OrderBy(
                info =>
                {
                    OrderedMaintenanceAttribute orderedMaintenanceAttribute = info.Migration.GetType().GetCustomAttribute<OrderedMaintenanceAttribute>();

                    return orderedMaintenanceAttribute?.Order ?? int.MaxValue;
                })
            .ToList();
    }

    #endregion
}

Here is part of the registration (There are a lot of registrations happening):

var loggerMiddlewareModule = new MiddlewareModule(new Log4NetMiddleware());
builder.RegisterModule(loggerMiddlewareModule);

builder.RegisterType<LoggerAdapterProvider>().As<ILoggerProvider>().InstancePerLifetimeScope();
builder.RegisterType<LoggerFactory>().As<ILoggerFactory>().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Logger<>)).As(typeof(ILogger<>)).InstancePerLifetimeScope();

// registers AutofacServiceProvider and AutofacServiceScopeFactory
builder.Populate(Enumerable.Empty<ServiceDescriptor>());

builder.RegisterType<AssemblyFileLoadEngine>().As<IAssemblyLoadEngine>().InstancePerLifetimeScope();
builder.RegisterType<MaintenanceLoader>();
builder.Register(context => new OrderedMaintenanceLoader(context.Resolve<IMaintenanceLoader>(), context.Resolve<CurrentReleaseMigrationStage>()))
        .As<IMaintenanceLoader>()
        .InstancePerLifetimeScope();
builder
    .RegisterType<AssemblySource>()
    .Keyed<IAssemblySource>(AssemblySourceKey)
    .InstancePerLifetimeScope();

builder
    .Register<IAssemblySource>(
        ctx =>
            new MaintenanceAssemblySource(ctx.ResolveKeyed<IAssemblySource>(AssemblySourceKey)))
    .As<IAssemblySource>()
    .InstancePerLifetimeScope();

And here is the migration class which fails in the constructor:

[OrderedMaintenance(MigrationStage.BeforeAll, 5)]
public class FlushRedisOnVersionUpdate : ConditionalMigration
{
    #region Private fields

    private readonly IConfigurationStateMonitor _configurationStateMonitor;
    private readonly ILog _logger;
    #endregion
    #region Ctors

    public FlushRedisOnVersionUpdate(
        IConfigurationStateMonitor configurationStateMonitor, 
        ILog logger) : base(migrationProcessor)
    {
        _configurationStateMonitor = configurationStateMonitor;
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    #endregion
    #region Overrides

    public override void Up()
    {
        _configurationStateMonitor.FlushCache();
    }

    #endregion
}

And this is the class which runs the migration:

public class MigrationService : IMigrationService
{
    #region Private fields

    private readonly ILog _logger;
    private readonly DatabaseOptions _databaseOptions;
    private readonly MigrationManagerOptions _migrationOptions;
    private readonly ILifetimeScope _scope;

    #endregion
    #region Ctors

    public MigrationService(
        DatabaseOptions databaseOptions,
        MigrationManagerOptions migrationOptions,
        ILog logger,
        ILifetimeScope scope)
    {
        _databaseOptions = databaseOptions;
        _migrationOptions = migrationOptions;
        _scope = scope;
        _logger = logger;
    }

    #endregion
    #region Private methods

    private ILifetimeScope BeginLifetimeScope(PackageDefinition package, Release release, ReleaseMigrationStage releaseMigrationStage)
    {
        return _scope.BeginLifetimeScope(
            builder =>
            {
                builder
                    .RegisterModule<MicrosoftExtensionsOptionsModule>();

                builder
                    .Configure<ProcessorOptions>(
                        options =>
                        {
                            options.ConnectionString = _databaseOptions.ConnectionString;
                            options.Timeout = TimeSpan.FromMinutes(_migrationOptions.DefaultMigrationCommandTimeout);
                        });
                builder.RegisterInstance(new CurrentReleaseMigrationStage(releaseMigrationStage));

                builder.RegisterInstance(package);
                builder.RegisterInstance(new CurrentRelease(release));
            });
    }

    #endregion
    #region IMigrationService Members

    public bool Migrate(PackageDefinition package, Release release, ReleaseMigrationStage releaseMigrationStage)
    {
        using (ILifetimeScope scope = BeginLifetimeScope(package, release, releaseMigrationStage))
        {
            bool hasAnyToApply = scope.Resolve<TaskExecutor>().HasMigrationsToApply();

            scope.Resolve<IMigrationRunner>().MigrateUp();

            return hasAnyToApply;
        }
    }

    #endregion
}

MigrationService can actually get ILog ... could be the issue because we create a life time scope?

Update:

I have created a unit test without calling MigrationService and still failing (so issues is not in the creation of lifetime scope):

Here is the exception:

System.Reflection.TargetInvocationException
  HResult=0x80131604
  Message=Exception has been thrown by the target of an invocation.
  Source=mscorlib
  StackTrace:
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) in f:\dd\ndp\clr\src\BCL\system\reflection\methodinfo.cs:line 761
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) in f:\dd\ndp\clr\src\BCL\system\reflection\methodinfo.cs:line 739
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) in f:\dd\ndp\clr\src\BCL\system\reflection\methodbase.cs:line 211
   at NUnit.Framework.Internal.Reflect.InvokeMethod(MethodInfo method, Object fixture, Object[] args) in C:\Src\NUnit\nunit\src\NUnitFramework\framework\Internal\Reflect.cs:line 222

  This exception was originally thrown at this call stack:
    Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(System.IServiceProvider) in ActivatorUtilities.cs
    Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(System.IServiceProvider, System.Type, object[]) in ActivatorUtilities.cs
    FluentMigrator.Runner.MaintenanceLoader..ctor.AnonymousMethod__4(<>f__AnonymousType3<System.Type, FluentMigrator.MigrationStage?>)
    System.Linq.Enumerable.WhereSelectEnumerableIterator<TSource, TResult>.MoveNext() in Enumerable.cs
    System.Linq.Lookup<TKey, TElement>.Create<TSource>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource, TKey>, System.Func<TSource, TElement>, System.Collections.Generic.IEqualityComparer<TKey>) in Lookup.cs
    System.Linq.GroupedEnumerable<TSource, TKey, TElement>.GetEnumerator() in GroupedEnumerable.cs
    System.Linq.Enumerable.ToDictionary<TSource, TKey, TElement>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource, TKey>, System.Func<TSource, TElement>, System.Collections.Generic.IEqualityComparer<TKey>) in Enumerable.cs
    FluentMigrator.Runner.MaintenanceLoader.MaintenanceLoader(FluentMigrator.Runner.Initialization.IAssemblySource, Microsoft.Extensions.Options.IOptions<FluentMigrator.Runner.Initialization.RunnerOptions>, FluentMigrator.Runner.IMigrationRunnerConventions, System.IServiceProvider) in MaintenanceLoader.cs
    Autofac.Core.Activators.Reflection.BoundConstructor.Instantiate() in BoundConstructor.cs

Inner Exception 1:
DependencyResolutionException: An exception was thrown while activating λ:System.Object -> FluentMigrator.Runner.MaintenanceLoader.

Inner Exception 2:
DependencyResolutionException: An exception was thrown while invoking the constructor 'Void .ctor(FluentMigrator.Runner.Initialization.IAssemblySource, Microsoft.Extensions.Options.IOptions`1[FluentMigrator.Runner.Initialization.RunnerOptions], FluentMigrator.Runner.IMigrationRunnerConventions, System.IServiceProvider)' on type 'MaintenanceLoader'.

Inner Exception 3:
InvalidOperationException: Unable to resolve service for type 'log4net.ILog' while attempting to activate 'Migrations.Maintenance.BeforeAll.FlushRedisOnVersionUpdate'.

Here is the test case:

[TestCase(4, typeof(FlushRedisOnVersionUpdate), Description = "FlushRedisOnVersionUpdate should run after RemoveBuilderEntityPropertiesMigration")]
public void BeforeAllMigrationsShouldRunInSpecifiedOrder(int order, Type expectedMigrationType)
{
    using (IContainer container = BuildContainer())
    {
        IMaintenanceLoader maintenanceLoader = container.Resolve<IMaintenanceLoader>();
        IList<IMigrationInfo> beforeAllMigrations = maintenanceLoader.LoadMaintenance(MigrationStage.BeforeAll);

        Assert.IsNotNull(beforeAllMigrations);
        Assert.Less(order, beforeAllMigrations.Count);
        Assert.IsInstanceOf(expectedMigrationType, beforeAllMigrations[order].Migration);
    }
}

It fails on IMaintenanceLoader maintenanceLoader = container.Resolve<IMaintenanceLoader>();

I also changed the registration to use decorator registration instead but same result.

builder.RegisterType<MaintenanceLoader>()
        .Keyed<IMaintenanceLoader>(DecorateWithEx)
        .UsingConstructor(typeof(IAssemblySource),
                            typeof(IOptions<RunnerOptions>),
                            typeof(IMigrationRunnerConventions),
                            typeof(IServiceProvider));

builder
    .RegisterType<OrderedMaintenanceLoader>()
    .As<IOrderedMaintenanceLoader>();

builder
    .RegisterDecorator<IMaintenanceLoader>((ctx, maintenanceLoader) =>
        ctx.Resolve<IOrderedMaintenanceLoader>(TypedParameter.From(maintenanceLoader)), DecorateWithEx)
    .InstancePerLifetimeScope();
1

There are 1 best solutions below

1
On

This is the registration code posted in the OP:

builder.RegisterType<LoggerAdapterProvider>().As<ILoggerProvider>().InstancePerLifetimeScope();
builder.RegisterType<LoggerFactory>().As<ILoggerFactory>().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Logger<>)).As(typeof(ILogger<>)).InstancePerLifetimeScope();

// registers AutofacServiceProvider and AutofacServiceScopeFactory
builder.Populate(Enumerable.Empty<ServiceDescriptor>());

builder.RegisterType<AssemblyFileLoadEngine>().As<IAssemblyLoadEngine>().InstancePerLifetimeScope();
builder.RegisterType<MaintenanceLoader>();
builder.Register(context => new OrderedMaintenanceLoader(context.Resolve<IMaintenanceLoader>(), context.Resolve<CurrentReleaseMigrationStage>()))
       .As<IMaintenanceLoader>()
       .InstancePerLifetimeScope();

None of these statements registers ILog. Register the service, or perhaps implement a registration source that handles it.