prosource

Azure 기능의 DI

probook 2023. 4. 23. 10:34
반응형

Azure 기능의 DI

ASP에서 사용하는 클래스 라이브러리가 몇 개 있습니다.Azure SQL Database, Cosmos DB 등의 여러 데이터베이스에 대한 CRUD 작업 등 모든 백엔드 작업을 처리하는 NET Web API 앱

Visual Studio 2017에서 만들고 있는 새로운 Azure Functions에서 휠을 재발명하여 사용할 수 있는 것은 아닙니다.모든 저장소 메서드는 인터페이스를 사용합니다.그렇다면 새로운 Azure 기능에 의존성 주입을 어떻게 구현해야 할까요?

DI에 대한 지원은 보이지 않지만 조금 혼란스럽습니다.Azure Functions는 WebJobs와 같은 SDK를 기반으로 하고 있는 것 같습니다.Microsoft는 작년에 WebJobs에서 DI를 지원하기 시작했다고 생각합니다.Ninject를 사용하여 구현했기 때문에 확실히 알고 있습니다.

새로운 Azure Functions 프로젝트에서 기존 라이브러리를 사용할 수 있는 방법이 있습니까?

서비스 로케이터(안티) 패턴에 덧붙여, 이러한 2개의 테크닉이 표시됩니다.Azure Functions 팀에도 코멘트를 부탁했습니다.

https://blog.wille-zone.de/post/azure-functions-dependency-injection/

https://blog.wille-zone.de/post/azure-functions-proper-dependency-injection/

Azure Functions의 GitHub 페이지에 이 건에 관한 오픈 기능 요청이 있습니다.

그러나 제가 접근하려는 방법은 일종의 '랩퍼' 진입점을 사용하는 것입니다. 서비스 로케이터를 사용하여 이 문제를 해결하고 거기서부터 기능을 시작합니다.

이것은 약간 이와 같습니다(간소화).

var builder = new ContainerBuilder();
//register my types

var container = builder.Build();

using(var scope = container.BeginLifetimeScope())
{
  var functionLogic = scope.Resolve<IMyFunctionLogic>();

  functionLogic.Execute();
}

이것은 물론 좀 진부하지만, 내가 아는 바로는 그것이 최선이다.

willi-zone 블로그에서 이 토픽에 대해 많이 언급하고 있습니다만, Azure 기능과 함께 DI를 사용하기 위해 그 루트를 사용할 필요는 없습니다.

버전 2 를 사용하고 있는 경우는, Azure 기능을 정적으로 할 수 있습니다.그런 다음 종속성을 주입하는 공용 생성자를 추가할 수 있습니다.다음 단계에서는 IWebJobsStartup 클래스를 추가합니다.스타트업 클래스에서는 다른 클래스와 마찬가지로 서비스를 등록할 수 있습니다.넷코어 프로젝트

여기에서는, 다음의 어프로치를 채용하고 있는 퍼블릭 리포트가 있습니다.https://github.com/jedi91/MovieSearch/tree/master/MovieSearch

다음은 스타트업 클래스에 대한 직접 링크입니다.https://github.com/jedi91/MovieSearch/blob/master/MovieSearch/Startup.cs

다음은 https://github.com/jedi91/MovieSearch/blob/master/MovieSearch/Functions/Search.cs의 기능입니다.

이 접근방식이 도움이 되길 바랍니다.Azure 함수를 정적인 상태로 유지하고 싶다면 willi-zone 접근법이 효과적일 것입니다만, 저는 이 접근법이 매우 마음에 들어 서드파티 라이브러리가 필요 없습니다.

한 가지 주의할 점은 디렉토리입니다.Build.target 파일.이 파일은 호스트 파일에 확장자를 복사하여 함수를 Azure에 배포한 후 DI가 작동하도록 합니다.함수를 로컬로 실행할 때는 이 파일이 필요하지 않습니다.

Azure Functions Dependency Injection은 MSBuild 2019에서 발표되었습니다.다음은 그 방법의 예입니다.

[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>();
        }
    }
}

위와 같이 방금 Build 2019에서 발표되었습니다.ASP와 거의 동일하게 셋업할 수 있게 되었습니다.넷코어 앱

Microsoft 문서

내가 쓴 짧은 블로그

실제로 마이크로소프트가 바로 사용할 수 있는 훨씬 더 좋고 간단한 방법이 있습니다.찾기가 좀 어렵긴 한데.스타트업 클래스를 만들고 필요한 모든 서비스를 여기에 추가하면 일반 웹 앱이나 웹 아피스와 같이 컨스트럭터 주입을 사용할 수 있습니다.

이것만 하면 돼요.

처음에 스타트업 클래스를 만들 때는 Razor 웹 앱과 일관성을 유지하기 위해 Startup.cs이라고 불렀습니다.이것은 Azure Functions용이지만, 여전히 Microsoft 방식입니다.

using System;
using com.paypal;
using dk.commentor.bl.command;
using dk.commentor.logger;
using dk.commentor.sl;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using org.openerp;

[assembly:Microsoft.Azure.WebJobs.Hosting.WebJobsStartup(typeof(dk.commentor.starterproject.api.Startup))]
namespace dk.commentor.starterproject.api
{

    public class Startup : IWebJobsStartup
    {

        public void Configure(IWebJobsBuilder builder)
        {
            builder.Services.AddSingleton<ILogger, CommentorLogger>();
            builder.Services.AddSingleton<IPaymentService, PayPalService>();
            builder.Services.AddSingleton<IOrderService, OpenERPService>();
            builder.Services.AddSingleton<ProcessOrderCommand>();
            Console.WriteLine("Host started!");
        }
    }
}

다음으로 함수의 메서드 호출을 스태틱에서 비 스태틱으로 변경하고 클래스에 컨스트럭터를 추가합니다(이것도 비 스태틱입니다).이 컨스트럭터에서는 필요한 서비스를 컨스트럭터 파라미터로 추가합니다.

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using dk.commentor.bl.command;

namespace dk.commentor.starterproject.api
{
    public class ProcessOrder
    {
        private ProcessOrderCommand processOrderCommand;

        public ProcessOrder(ProcessOrderCommand processOrderCommand) {
            this.processOrderCommand = processOrderCommand;
        }

        [FunctionName("ProcessOrder")]
        public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log)
        {
            log.LogInformation("C# HTTP trigger ProcessOrder called!");
            log.LogInformation(System.Environment.StackTrace);

            string jsonRequestData = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic requestData = JsonConvert.DeserializeObject(jsonRequestData);

            if(requestData?.orderId != null)
                return (ActionResult)new OkObjectResult($"Processing order with id {requestData.orderId}");
            else
                return new BadRequestObjectResult("Please pass an orderId in the request body");
        }
    }
}

도움이 됐으면 좋겠다.

여기에 제 2센트를 더하고 싶습니다.호스트가 ILogger를 주입할 때 사용하는 기술을 사용했습니다.스타트업 프로젝트를 보시면 IBinding Provider를 구현하는 Generic Binding Provider를 만들었습니다.그런 다음 주입을 원하는 유형별로 다음과 같이 등록합니다.

builder.Services.AddTransient<IWelcomeService, WelcomeService>();
builder.Services.AddSingleton<IBindingProvider, GenericBindingProvider<IWelcomeService>>();

단점은 함수에 삽입할 유형을 두 번 등록해야 한다는 것입니다.

샘플 코드:

Azure 함수 V2 의존성 주입 샘플

Simple을 사용하고 있습니다.Azure Functions(Azure 기능)에서 인젝터가 완벽하게 미세합니다.등록이 있는 클래스(IoCConfig라고 부릅니다)를 만들고 각 인스턴스가 기존 인스턴스를 사용하도록 함수 클래스에 해당 클래스의 정적 인스턴스를 만듭니다.

public interface IIoCConfig
{
    T GetInstance<T>() where T : class;
}

public class IoCConfig : IIoCConfig
{
    internal Container Container;

    public IoCConfig(ExecutionContext executionContext, ILogger logger)
    {
        var configurationRoot = new ConfigurationBuilder()
            .SetBasePath(executionContext.FunctionAppDirectory)
            .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();

        Container = new Container();
        Configure(configurationRoot, logger);
    }

    public IoCConfig(IConfigurationRoot configurationRoot, ILogger logger)
    {
        Container = new Container();
        Configure(configurationRoot, logger);
    }

    private void Configure(IConfigurationRoot configurationRoot, ILogger logger)
    {
        Container.RegisterInstance(typeof(IConfigurationRoot), configurationRoot);
        Container.Register<ISomeType, SomeType>();
    }

    public T GetInstance<T>() where T : class
    {
        return Container.GetInstance<T>();
    }
}

그 후 루트:

   public static class SomeFunction
{
    public static IIoCConfig IoCConfig;

    [FunctionName("SomeFunction")]
    public static async Task Run(
        [ServiceBusTrigger("some-topic", "%SUBSCRIPTION_NAME%", Connection = "AZURE_SERVICEBUS_CONNECTIONSTRING")]
        SomeEvent msg,
        ILogger log,
        ExecutionContext executionContext)
    {
        Ensure.That(msg).IsNotNull();

        if (IoCConfig == null)
        {
            IoCConfig = new IoCConfig(executionContext, log);
        }

        var someType = IoCConfig.GetInstance<ISomeType>();
        await someType.Handle(msg);
    }
}

Azure Functions (Azure Functions)오토팩은 매우 사용하기 쉽다.

구성 파일을 추가합니다.

public class DIConfig
{
    public DIConfig(string functionName)
    {
        DependencyInjection.Initialize(builder =>
        {
            builder.RegisterType<Sample>().As<ISample>();
            ...
        }, functionName);
    }
}

의존 관계 추가Injection Config Atribute를 다음에 injection합니다.

[DependencyInjectionConfig(typeof(DIConfig))]
public class MyFunction
{
    [FunctionName("MyFunction")]
    public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]HttpRequestMessage request, 
                                          TraceWriter log, 
                                          [Inject]ISample sample)
    {

https://github.com/introtocomputerscience/azure-function-autofac-dependency-injection

저는 이것이 더 나은 해결책이라고 생각합니다.

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

프로젝트에 NuGet을 설치한 후 Startup.cs을 만들고 다음 내용을 추가합니다.

[assembly: FunctionsStartup(typeof(Startup))]

public class Startup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder
            .UseAppSettings() // this is optional, this will bind IConfiguration in the container.
            .UseAutofacServiceProviderFactory(ConfigureContainer);
    }

    private void ConfigureContainer(ContainerBuilder builder)
    {
         // do DI registration against Autofac like normal! (builder is just the normal ContainerBuilder from Autofac)
    }
    ...

그런 다음 함수 코드에서 DI를 통해 일반 생성자 주입을 수행할 수 있습니다.

public class Function1 : Disposable
{
    public Function1(IService1 service1, ILogger logger)
    {
        // logger and service1 injected via autofac like normal
        // ...
    }

    [FunctionName(nameof(Function1))]
    public async Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")]string myQueueItem)
    {
        //...

종속성 주입 지원은 Azure Functions 2.x부터 시작됩니다. 즉, 이제 종속성 주입은 Azure 함수를 활용할 수 있습니다.NET 코어 의존성 주입 기능.

종속성 주입을 사용하려면 먼저 다음 NuGet 패키지를 설치해야 합니다.

  • 마이크로소프트(MS.애저, 기능.내선번호
  • 마이크로소프트(MS.NET.Sdk기능들

의존성 주입을 사용하면 DBContext, Http 클라이언트 사용률(Httpclienfactory), Ilogger factory, 캐시 지원 등이 쉬워집니다.

먼저 아래와 같이 스타트업 클래스를 업데이트합니다.

namespace DemoApp
{
    public class Startup: FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddScoped<IHelloWorld, HelloWorld>();

            // Registering Serilog provider
            var logger = new LoggerConfiguration()
                .WriteTo.Console()
                .CreateLogger();
            builder.Services.AddLogging(lb => lb.AddSerilog(logger));
            //Reading configuration section can be added here etc.
        }
    }
}

둘째, 함수 클래스 및 메서드레벨에서의 Static 키워드 삭제

public class DemoFunction
{
    private readonly IHelloWorld _helloWorld;
    public DemoFunction(IHelloWorld helloWorld)
    {
        _helloWorld = helloWorld;
    }

    [FunctionName("HttpDemoFunction")]
    public async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
        ILogger log)
    {
        log.LogInformation("C# HTTP trigger function processed a request.");
    }

예를 들어 위의 내용을 살펴보면IHello World는 를 사용하여 주입됩니다.NET 코어 DI

**주의:**의존성 주입용 Azure 함수 v3의 최신 버전을 사용하여 몇 가지 절차를 수행할 수 있도록 하는 것은 위와 같이 수동입니다.

github의 샘플 코드는 여기에서 찾을 수 있습니다.

언급URL : https://stackoverflow.com/questions/45912224/di-in-azure-functions

반응형