JasperFx / lamar

Fast Inversion of Control Tool and Successor to StructureMap
https://jasperfx.github.io/lamar
MIT License
563 stars 118 forks source link

Need help for adding Lamar support to Azure Functions #261

Open valdisiljuconoks opened 3 years ago

valdisiljuconoks commented 3 years ago

Hi,

First, thanks for the library. We used heavily StructureMap back in days and now we are migrating away to .NET Core and want to keep the same knowledge and habits that we had with StructureMap.

However, we struggle with adding support for Lamar in Azure Functions.

Background

Case with functions is that there is some weird sequence of the services registration process in functions. One of the extension hook we have given is function's startup class (which is very similar to ASP.NET Startup.cs).

However - some of the services (and it's especially around ILoggerFactory and ILogger & ILogger<T> dependencies) are registered after external startup class is called.

Attempt to replace job activator in functions

We have replaced something called IJobActivator & IJobActivatorEx interfaces:

public static IFunctionsHostBuilder UseLamar(this IFunctionsHostBuilder hostBuilder, Action<ServiceRegistry> configurationAction = null)
{
    var registry = new ServiceRegistry();
    registry.AddRange(hostBuilder.Services);
    configurationAction?.Invoke(registry);

    var container = new Container(registry);
    var scoped = new ScopedJobActivator(container);

    // Replacing Azure Functions ServiceProvider
    hostBuilder.Services.Replace(ServiceDescriptor.Singleton(typeof(IJobActivator), scoped));
    hostBuilder.Services.Replace(ServiceDescriptor.Singleton(typeof(IJobActivatorEx), scoped));

    return hostBuilder;
}

Job activator is more or less straight forward:

public T CreateInstance<T>()
{
     var scope = _container.CreateScope();

    return scope.ServiceProvider.GetService<T>();
}

But as dependencies around logging infrastructure in Azure Function host got registered after external startup, Lamar container does not have knowledge about these ones and fails to return required services when calling the function. This happens when you have ILogger<T> dependency in your function and/or any other services you might need while executing function

Exception:

Cannot build registered instance hfpsFeedEndpointFunction of 'Kolumbus.RealtimeHub.Functions.Core.Hfps.HfpsFeedEndpointFunction':
Dependency new HostFileLoggerProvider(): Cannot fill the dependencies of any of the public constructors
Available constructors:new HostFileLoggerProvider(IOptions<Microsoft.Azure.WebJobs.Script.ScriptJobHostOptions> options, IFileLoggingStatusManager fileLoggingStatusManager, IFileWriterFactory fileWriterFactory)
* IFileLoggingStatusManager is not registered within this container and cannot be auto discovered by any missing family policy
Dependency new FunctionFileLoggerProvider(): Cannot fill the dependencies of any of the public constructors
Available constructors:new FunctionFileLoggerProvider(IOptions<Microsoft.Azure.WebJobs.Script.ScriptJobHostOptions> scriptOptions, IFileLoggingStatusManager fileLoggingStatusManager, IPrimaryHostStateProvider primaryHostStateProvider, IFileWriterFactory fileWriterFactory)
* IFileLoggingStatusManager is not registered within this container and cannot be auto discovered by any missing family policy
* IPrimaryHostStateProvider is not registered within this container and cannot be auto discovered by any missing family policy

I know that it's not very polite to cross reference any other DI here - but my intent is not to switch to anything other than Lamar, I really would like to get it working, but I need your help. There is a working example of using Autofac container in Azure functions - https://github.com/junalmeida/autofac-azurefunctions

And also there is a special note about ILogger issues - https://github.com/junalmeida/autofac-azurefunctions/blob/master/Autofac.Extensions.DependencyInjection.AzureFunctions/ScopedJobActivator.cs#L33

What I don't understand - is how to translate this to Lamar infrastructure. Tried various approaches - like resolving logger and logger factory from passed in functionInstance.InstanceServices (which is the final list of service collection) and then inject that logger and factory into scoped container and then resolve requested function. But that failed also.

I would really much appreciate your help here!

Many thanks!

tjrobinson commented 3 years ago

Related: https://github.com/JasperFx/lamar/issues/183

gursharan001 commented 3 years ago

Hi there,

I did try the approach suggested in the linked issue #183. As per this link other IoC containers are not supported at the moment - https://github.com/Azure/azure-functions-host/issues/4410

Posting this here to save someone some time trying to get this working.