Правильный способ регистрации сторонней платформы DI Framework (Lamar/Autofac) в функциях Azure V2

Функции Azure V2 теперь поддерживают внедрение зависимостей.net

Для этого вам нужно сделать следующий код:

[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]

namespace MyNamespace
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddHttpClient();
            builder.Services.AddSingleton((s) => {
                return new CosmosClient(Environment.GetEnvironmentVariable("COSMOSDB_CONNECTIONSTRING"));
            });
            builder.Services.AddSingleton<ILoggerProvider, MyLoggerProvider>();
        }
    }
}

Я хочу изменить контейнер по умолчанию с.net на структуру DI "Lamar".

В их документации есть пример для WebHost:

var builder = new WebHostBuilder();
builder
    // Replaces the built in DI container
    // with Lamar
    .UseLamar()

    // Normal ASP.Net Core bootstrapping
    .UseUrls("http://localhost:5002")
    .UseKestrel()
    .UseStartup<Startup>();

builder.Start();

Но я не могу изменить IFunctionsHostBuilder для использования расширения "UseLamar()". Поскольку это расширяет IWebHostBuilder. Единственным способом, которым я смог перехватить инициализацию лазурных функций, был либо FunctionsStartup, который настраивает IFunctionsHostBuilder, либо IWebJobsStartup, который настраивает IWebJobsBuilder, но я не нахожу расширений для таких сборок на Lamar.

Я попытался проверить существующее расширение, чтобы создать аналогичный код, но не работает, потому что, вероятно, мне нужно создать больше вещей:

[assembly: FunctionsStartup(typeof(FunctionAppPrototype.Startup))]
namespace FunctionAppPrototype
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            var container = new Container(x =>
            {
                x.AddTransient<IMyService, MyService>();
            });

            builder.Services.AddSingleton<IServiceProviderFactory<IServiceCollection>, LamarServiceProviderFactory>();
            builder.Services.AddSingleton<IServiceProviderFactory<ServiceRegistry>, LamarServiceProviderFactory>();
        }
    }
}

2 ответа

Решение

После некоторого исследования я смог найти решение с помощью Autofac. Я не смог сделать это с Lamar, у него не было расширения ни для IFunctionsHostBuilder, ни для IWebJobsBuilder.

Исходный код: расширения привязки для внедрения зависимостей в функции Azure v2

Nuget: https://www.nuget.org/packages/Willezone.Azure.WebJobs.Extensions.DependencyInjection

Во-первых, вам нужно перехватить запуск приложения-функции, выполнив следующий код:

[assembly: WebJobsStartup(typeof(AutoFacFunctionAppPrototype.WebJobsStartup))]

namespace AutoFacFunctionAppPrototype
{
    public class WebJobsStartup : IWebJobsStartup
    {
        public void Configure(IWebJobsBuilder builder) =>
            builder.AddDependencyInjection<AutoFacServiceProviderBuilder>();
    }
}

Затем создайте контейнер и зарегистрируйте зависимости:

namespace AutoFacFunctionAppPrototype.Builders
{
    public class AutoFacServiceProviderBuilder : IServiceProviderBuilder
    {
        private readonly IConfiguration configuration;

        public AutoFacServiceProviderBuilder(IConfiguration configuration) 
            => this.configuration = configuration;

        public IServiceProvider Build()
        {
            var services = new ServiceCollection();
            services.AddTransient<ITransientService, TransientService>();
            services.AddScoped<IScopedService, ScopedService>();

            var builder = new ContainerBuilder();

            builder.RegisterType<SingletonService>().As<ISingletonService>().SingleInstance();

            builder.Populate(services); // Populate is needed to have support for scopes.
            return new AutofacServiceProvider(builder.Build());
        }
    }
}

Затем вы можете использовать их в функции с помощью атрибута [Inject]:

namespace AutoFacFunctionAppPrototype.Functions
{
    public static class CounterFunction
    {
        [FunctionName("Counter")]
        public static IActionResult Run(
            [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
            [Inject]ITransientService transientService,
            [Inject]IScopedService scopedService,
            [Inject]ISingletonService singletonService,
             ILogger logger)
        {
            logger.LogInformation("C# HTTP trigger function processed a request.");

            string result = String.Join(Environment.NewLine, new[] {
                $"Transient: {transientService.GetCounter()}",
                $"Scoped: {scopedService.GetCounter()}",
                $"Singleton: {singletonService.GetCounter()}",
            });
            return new OkObjectResult(result);
        }
    }
}

Используя этот подход, я мог только вводить параметры, но не мог вводить конструктор или свойство, хотя я был нестатическим классом.

Примечание. Если в будущем Autofac будет поддерживать расширение для IFunctionsHostBuilder, вероятно, будет лучше использовать этот подход вместо IWebJobsStartup.

Я создал новый способ использования Autofac DI в проекте Azure Functions v3 без использования статических функций или атрибутов вставки. Списание услуг вызывается с использованием соответствующей области.

GitHub: https://github.com/junalmeida/autofac-azurefunctions
NuGet: https://www.nuget.org/packages/Autofac.Extensions.DependencyInjection.AzureFunctions

Не стесняйтесь вносить свой вклад вместе со мной!

пример

public class Function1 
{
    private readonly IService1 _service1;

    public Function1(IService1 service1)
    {
        _service1 = service1;
    }

    [FunctionName(nameof(Function1))]
    public async Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")]string myQueueItem, ILogger log)
    {
        ...
    }
Другие вопросы по тегам