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();
This is the registration code posted in the OP:
None of these statements registers
ILog
. Register the service, or perhaps implement a registration source that handles it.