Closed LockTar closed 1 year ago
So I did found a working solution for now. Maybe you have a better solution but this works for me for now...
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
{
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
// Get autofac container so we resolve dependencies from there
var autoFacContainer = sp.GetRequiredService<IContainer>();
// This does work
var iLifetimeScope = autoFacContainer.Resolve<ILifetimeScope>();
// Create a new logger that is connected to the console with a minimum loglevel from host.json based on the namespace
var loggerFactory = LoggerFactory.Create(builder =>
{
var logLevel = configuration.GetValue<LogLevel>("AzureFunctionsJobHost:logging:logLevel:" + typeof(Startup).Namespace, LogLevel.Information);
builder.AddConsole();
builder.SetMinimumLevel(logLevel);
});
var logger = loggerFactory.CreateLogger<EventBusServiceBus>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
string subscriptionName = configuration["SubscriptionClientName"];
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
eventBusSubcriptionsManager, iLifetimeScope, subscriptionName);
});
return services;
}
So the important part is this:
// Create a new logger that is connected to the console with a minimum loglevel from host.json based on the namespace
var loggerFactory = LoggerFactory.Create(builder =>
{
var logLevel = configuration.GetValue<LogLevel>("AzureFunctionsJobHost:logging:logLevel:" + typeof(Startup).Namespace, LogLevel.Information);
builder.AddConsole();
builder.SetMinimumLevel(logLevel);
});
var logger = loggerFactory.CreateLogger<EventBusServiceBus>();
I think you don't need to create a separate LoggerFactory
as this is supposed to be registered already.
What I would try in your case, if you really need an instance of ILogger<T>
is something like:
builder.RegisterGeneric(typeof(Logger<>))
.As(typeof(ILogger<>))
.SingleInstance();
You can also use the plain ILogger
that will work out-of-the-box.
As for using the documentation or the classes on this package, it is up to what you consider best for your project. The latest version of this package contains the classes suggested in the documentation.
If this autofac registration does not work, you can also do something like:
builder.Register(ctx => {
var factory = ctx.Resolve<ILoggerFactory>();
// create the logger here, (inspect ctx for T being requested)
return logger;
})
.As(typeof(ILogger<>))
.SingleInstance();
I will give it a try and will let you know. Thanks!
I tried all kind of setups but I'm still getting issues with that logger. I don't understand why I just can't get an instance of that logger in de startup.cs
. Now I got something working with the logger locally, in Azure I get the same errors again.
I think I'm gonna leave Functions. Every time there is something wrong with it. Isolated or in process, doesn't matter. I need to use durable functions so I'm stuck with in process. Durable isolated aren't stable (yet).
ok wait. So, for Isolated is a whole different thing, and you don't need this package. This package is meant for non-isolated projects.
I use this extensively on dozens of durable functions with no problem. I'm not sure I got your problem this time. You said you can't get an instance in Startup.cs
, however you only need an instance in function classes, no?
And yet, you could use only ILogger
instead of ILogger<T>
. I don't think there is a much significant difference unless you have anything specific to your project and you may correct me there.
ok wait. So, for Isolated is a whole different thing, and you don't need this package. This package is meant for non-isolated projects.
Sorry for the confusing. My project is not an isolated project but an in process version. What I meant was that er always something with Azure functions. It changes to much and logging is always an issue from v1 until v4 isolated mode. I need in process right now because durable functions aren't stable yet for Isolated.
You said you can't get an instance in
Startup.cs
, however you only need an instance in function classes, no?
I will explain a bit more on what I'm trying to do. I have several domains in a project. Some are Web API projects and some are Functions (Isolated) and some are Functions (in process) because durable functions are in example not stable in isolated. My DI is autofac. My Azure function in process needs to start and:
So the first bullet works fine but I get problems with the second. We use this project for reference and you will see a Startup.cs
Web API sample here for reference.
The problem is line 338 and 339 (in my functions verison).
I need there the iLifetimeScope
and the ILogger<[EventBusServiceBus>
. All other resolving works.
This singleton services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
is resolved on startup because of method ConfigureEventBus
. See that method here.
It resolves the registered EventBusServiceBus
as IEventBus
and tries to subscribe on events. Those events are Mediatr handlers that are running as "background processes". But it doesn't matter what I try, it crashes on the lines iLifetimeScope
and the ILogger<[EventBusServiceBus>
.
I thought I had a solution but then in one of my handlers it crashed again on another ILogger
I think it's because of the LoggerModule
. It crashes there on sequence contains not matching elements. To be honest, I don't fully understand that part. I understand the functional part but not really the technical line here of registering with a named parameter like this lifetimeScope.Resolve<ILoggerFactory>( new NamedParameter(LoggerModule.LoggerFactoryParam, loggerFactory) );
Because I tried to resolve with name without luck.
And yet, you could use only
ILogger
instead ofILogger<T>
.
Could you explain a bit more on how to get in example that ILogger then? Because I spend way to much hours on this already and I don't see it anymore. Thanks!
The problem is line 338 and 339 (in my functions verison).
I don't understand why do you need to mix two dep injection libraries. Here I see .AddSingleton
, which is the native DI, and inside it I see you requiring ILifetimeScope
which is Autofac. Why don't you have a regular Autofac ContainerBuilder
doing that?
Could you explain a bit more on how to get in example that ILogger then?
This is supposed to be like
public class MyFunction {
public MyFunction(ILogger logger) { // ILogger as opposed to ILogger<EventBusServiceBus>
// ...
}
or
scope.Resolve<ILogger>();
Unless you have a specific reason to need ILogger<T>
BTW the ILogger
is registered per instance. So, you need a function trigger scope to Resolve it. You can't resolve ILogger inside a singleton scope.
I don't understand why do you need to mix two dep injection libraries. Here I see
.AddSingleton
, which is the native DI, and inside it I see you requiringILifetimeScope
which is Autofac. Why don't you have a regular AutofacContainerBuilder
doing that?
I don't understand either why Microsoft is doing that in the sample app so I rewrote it to the code as below. That is the autofac functions docs with my setup.
[assembly: FunctionsStartup(typeof(Sample.Startup))]
namespace Sample;
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var configuration = builder.GetContext().Configuration;
// https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection#use-injected-dependencies
builder.Services.AddHttpClient();
// Setup AutoFac and return container so we can use it later to resolve components here
var container = SetupAutoFac(builder, configuration);
// Configure EventBus and subscribe on integration events
ConfigureEventBus(container);
}
private void ConfigureEventBus(IContainer container)
{
var eventBus = container.Resolve<BuildingBlocks.EventBus.Abstractions.IEventBus>();
eventBus.Subscribe<MyIntegrationEvent, IIntegrationEventHandler<MyIntegrationEvent>>();
}
/// <summary>
/// Setup AutoFac and return container so you can use it later to resolve components.
/// </summary>
/// <param name="builder">The IFunctionsHostBuilder</param>
/// <param name="configuration">Configuration like appsettings, local.settings.json, user settings etc.</param>
/// <returns>Return the AutoFac container so you can use it later to resolve components.</returns>
private static IContainer SetupAutoFac(IFunctionsHostBuilder builder, IConfiguration configuration)
{
IContainer container = GetContainer(builder.Services, configuration);
builder.Services.AddSingleton(container);
// Important: Use AddScoped so our Autofac lifetime scope gets disposed
// when the function finishes executing
builder.Services.AddScoped<LifetimeScopeWrapper>();
builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IJobActivator), typeof(AutofacJobActivator)));
builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IJobActivatorEx), typeof(AutofacJobActivator)));
return container;
}
/// <summary>
/// Register everything in the AutoFac container.
/// </summary>
/// <param name="serviceCollection"></param>
/// <param name="configuration">Configuration like appsettings, local.settings.json, user settings etc.</param>
/// <returns>Return the AutoFac container so you can use it later to resolve components.</returns>
private static IContainer GetContainer(IServiceCollection serviceCollection, IConfiguration configuration)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.Populate(serviceCollection);
containerBuilder.RegisterModule<LoggerModule>();
containerBuilder.RegisterModule(new MediatorModule());
string connectionString = "Todo";
containerBuilder.RegisterModule(new ApplicationModule(connectionString));
// This is a convenient way to register all your function classes at once
////containerBuilder.RegisterAssemblyTypes(typeof(Startup).Assembly)
//// .InNamespaceOf<Function1>();
containerBuilder.RegisterType<HttpContextAccessor>()
.As<IHttpContextAccessor>()
.SingleInstance();
// PersistASB connection
containerBuilder.Register<IServiceBusPersisterConnection>(sp =>
{
var serviceBusConnectionString = configuration["EventBusConnection"];
var subscriptionClientName = configuration["SubscriptionClientName"];
return new DefaultServiceBusPersisterConnection(serviceBusConnectionString);
}).SingleInstance();
// Things to test https://github.com/junalmeida/autofac-azurefunctions/issues/29#issuecomment-1413991014
////containerBuilder.RegisterGeneric(typeof(Logger<>))
//// .As(typeof(ILogger<>))
//// .SingleInstance();
////containerBuilder.Register(ctx => {
//// var factory = ctx.Resolve<ILoggerFactory>();
//// // create the logger here, (inspect ctx for T being requested)
//// return logger;
////})
//// .As(typeof(ILogger<>))
//// .SingleInstance();
// Setup EventBus
containerBuilder.Register<EventBusServiceBus>(sp =>
{
var serviceBusPersisterConnection = sp.Resolve<IServiceBusPersisterConnection>();
var iLifetimeScope = sp.Resolve<ILifetimeScope>();
// WORKAROUND THAT I THOUGHT WORKED
// Create a new logger that is connected to the console with a minimum loglevel from host.json based on the namespace
var loggerFactory = LoggerFactory.Create(builder =>
{
var logLevel = configuration.GetValue<LogLevel>("AzureFunctionsJobHost:logging:logLevel:" + typeof(Startup).Namespace, LogLevel.Information);
builder.AddConsole();
builder.SetMinimumLevel(logLevel);
});
var logger = loggerFactory.CreateLogger<EventBusServiceBus>();
var eventBusSubcriptionsManager = sp.Resolve<IEventBusSubscriptionsManager>();
string subscriptionName = configuration["SubscriptionClientName"];
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
eventBusSubcriptionsManager, iLifetimeScope, subscriptionName);
})
.As<IEventBus>()
.SingleInstance();
// Setup EventBus subscriptions
containerBuilder.RegisterType<InMemoryEventBusSubscriptionsManager>().As<IEventBusSubscriptionsManager>().SingleInstance();
return containerBuilder.Build();
}
}
So containerBuilder.Register<EventBusServiceBus>(sp =>
crashes directly when starting the application (not even calling a function yet) because it tries to resolve ILogger<T>
from the EventBusServiceBus
class. That EventBusServiceBus
class is resolved because of the ConfigureEventBus
method and there it tries (again before a function call) to resolve var eventBus = container.Resolve<BuildingBlocks.EventBus.Abstractions.IEventBus>();
.
I thought I tried ILogger as well but not sure anymore. Tried resolve ILoggerFactory
, LoggerFactory
etc...
Again. I try to register some general (background) handlers in the project that are not related to Function (triggers). That handler eventBus.Subscribe<MyIntegrationEvent, IIntegrationEventHandler<MyIntegrationEvent>>();
needs to start a new durable orchestrator.
I believe we are getting out of scope here, as this code is not related to any sample I provide here in this package. Not sure how I can help you if you don't need this package?
Well I do need the package because I also need DI with autofac inside my functions. This works great. I was just hoping that you would see an error that I made because of your experience with this setup (autofac and functions). But I understand if you want me to close the issue because it's a bit specific. Let me know or just close it.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Hi,
First a small question Is it better to use the classes in the documentation of Autofac or are these classes already in the nuget package and you can skip the first part and go directly to configuring the startup class?
Then my real question I hope you have an idea on how to solve this because I'm already busy with this for hours. Duplicate of StackOverflow post
I'm trying to get instance of
ILogger<T>
when resolving a dependency in theStartup
class of an Azure Function v4 in process version.Only this doesn't work and will result in an exception, null or a not working logger.
I followed the following
AutoFac
documentation for Azure Functions.I register some types and try to resolve a type in the
Startup
class. The problem is theILogger<T>
. In this case theLogger<EventBusServiceBus>
.In the sample I try to resolve
IEventBus
in theConfigureEventBus
method. Because of this,services.AddSingleton<IEventBus, EventBusServiceBus>(sp => ....);
is trying to be resolved in theAddEventBus
method.I don't understand why it can't be resolved because I see some registered types in the Autofac container.
See the comments in the code.
Thanks!
Simplified Startup from the docs:
Some extension methods: