programing

Azure WebJobs SDK를 사용한 종속성 주입?

i4 2023. 5. 6. 14:04
반응형

Azure WebJobs SDK를 사용한 종속성 주입?

문제는 Azure WebJobs SDK가 작업 진입점으로 공개 정적 메서드만 지원하므로 생성자/속성 주입을 구현할 방법이 없다는 것입니다.

공식 WebJobs SDK 문서/자료에서 이 항목에 대한 내용을 찾을 수 없습니다.제가 발견한 유일한 해결책은 여기 게시물에 설명된 서비스 로케이터(안티) 패턴을 기반으로 합니다.

Azure WebJobs SDK 기반 프로젝트에 "적절한" 의존성 주입을 사용하는 좋은 방법이 있습니까?

이제 Azure WebJobs SDK가 인스턴스 메서드를 지원합니다.이를 사용자 정의 IJob Activator와 결합하면 DI를 사용할 수 있습니다.

먼저 즐겨찾는 DI 컨테이너를 사용하여 작업 유형을 확인할 수 있는 사용자 정의 IJobActivator를 생성합니다.

public class MyActivator : IJobActivator
{
    private readonly IUnityContainer _container;

    public MyActivator(IUnityContainer container)
    {
        _container = container;
    }

    public T CreateInstance<T>()
    {
        return _container.Resolve<T>();
    }
}

사용자 지정 JobHostConfiguration을 사용하여 이 클래스를 등록해야 합니다.

var config = new JobHostConfiguration
{
    JobActivator = new MyActivator(myContainer)
};
var host = new JobHost(config);

그런 다음 작업에 대한 인스턴스 메서드와 함께 간단한 클래스를 사용할 수 있습니다(여기서는 Unity의 생성자 주입 기능을 사용합니다).

public class MyFunctions
{
    private readonly ISomeDependency _dependency;

    public MyFunctions(ISomeDependency dependency)
    {
        _dependency = dependency;
    }

    public Task DoStuffAsync([QueueTrigger("queue")] string message)
    {
        Console.WriteLine("Injected dependency: {0}", _dependency);

        return Task.FromResult(true);
    }
}

이것이 제가 새로운 SDK를 사용하여 범위 지정을 처리한 방법입니다. Alexander Molenkamp가 설명한 IJobactivator를 사용하여.

public class ScopedMessagingProvider : MessagingProvider
{
    private readonly ServiceBusConfiguration _config;
    private readonly Container _container;

    public ScopedMessagingProvider(ServiceBusConfiguration config, Container container)
        : base(config)
    {
        _config = config;
        _container = container;
    }

    public override MessageProcessor CreateMessageProcessor(string entityPath)
    {
        return new CustomMessageProcessor(_config.MessageOptions, _container);
    }

    private class CustomMessageProcessor : MessageProcessor
    {
        private readonly Container _container;

        public CustomMessageProcessor(OnMessageOptions messageOptions, Container container)
            : base(messageOptions)
        {
            _container = container;
        }

        public override Task<bool> BeginProcessingMessageAsync(BrokeredMessage message, CancellationToken cancellationToken)
        {
            _container.BeginExecutionContextScope();
            return base.BeginProcessingMessageAsync(message, cancellationToken);

        }

        public override Task CompleteProcessingMessageAsync(BrokeredMessage message, FunctionResult result, CancellationToken cancellationToken)
        {
            var scope = _container.GetCurrentExecutionContextScope();
            if (scope != null)
            {
                scope.Dispose();
            }

            return base.CompleteProcessingMessageAsync(message, result, cancellationToken);
        }
    }
}

다음과 같은 작업호스트 구성에서 사용자 지정 메시징 공급자를 사용할 수 있습니다.

var serviceBusConfig = new ServiceBusConfiguration
{ 
    ConnectionString = config.ServiceBusConnectionString
};
serviceBusConfig.MessagingProvider = new ScopedMessagingProvider(serviceBusConfig, container);
jobHostConfig.UseServiceBus(serviceBusConfig);

범위 지정을 처리하는 방법에 대해 직접 질문한 후...저는 방금 이 솔루션에 도달했습니다.저는 이것이 이상적이라고 생각하지 않지만 현재로서는 다른 해결책을 찾을 수 없었습니다.

이 예에서는 ServiceBusTrigger를 다루고 있습니다.

Simple을 사용하고 있기 때문에인젝터, IJob Activator 인터페이스 구현은 다음과 같습니다.

public class SimpleInjectorJobActivator : IJobActivator
{
    private readonly Container _container;

    public SimpleInjectorJobActivator(Container container)
    {
        _container = container;
    }

    public T CreateInstance<T>()
    {
        return (T)_container.GetInstance(typeof(T));
    }
}

저는 트리거된 웹 작업을 처리하고 있습니다.

그래서 저는 두 가지 의존성을 가지고 있습니다.

  • 싱글톤:

    public interface ISingletonDependency { }
    
    public class SingletonDependency : ISingletonDependency { }
    
  • 그리고 내 기능이 작동할 때만 살아야 하는 또 다른 것은:

    public class ScopedDependency : IScopedDependency, IDisposable
    {
        public void Dispose()
        {
             //Dispose what need to be disposed...
        }
    }
    

따라서 웹 작업과 독립적으로 실행되는 프로세스가 필요합니다.프로세스를 클래스로 캡슐화했습니다.

public interface IBrokeredMessageProcessor
{
    Task ProcessAsync(BrokeredMessage incommingMessage, CancellationToken token);
}

public class BrokeredMessageProcessor : IBrokeredMessageProcessor
{
    private readonly ISingletonDependency _singletonDependency;
    private readonly IScopedDependency _scopedDependency;

    public BrokeredMessageProcessor(ISingletonDependency singletonDependency, IScopedDependency scopedDependency)
    {
        _singletonDependency = singletonDependency;
        _scopedDependency = scopedDependency;
    }

    public async Task ProcessAsync(BrokeredMessage incommingMessage, CancellationToken token)
    {
        ...
    }
}

이제 웹 작업이 시작되면 범위에 따라 종속성을 등록해야 합니다.

class Program
{
    private static void Main()
    {
        var container = new Container();
        container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
        container.RegisterSingleton<ISingletonDependency, SingletonDependency>();
        container.Register<IScopedDependency, ScopedDependency>(Lifestyle.Scoped);
        container.Register<IBrokeredMessageProcessor, BrokeredMessageProcessor>(Lifestyle.Scoped);
        container.Verify();

        var config = new JobHostConfiguration
        {
            JobActivator = new SimpleInjectorJobActivator(container)
        };

        var servicebusConfig = new ServiceBusConfiguration
        {
            ConnectionString = CloudConfigurationManager.GetSetting("MyServiceBusConnectionString")
        };

        config.UseServiceBus(servicebusConfig);
        var host = new JobHost(config);
        host.RunAndBlock();
    }
}

그리고 이것이 트리거된 작업입니다.

  • 종속성은 IoC 컨테이너 하나뿐입니다.이 수업은 제 작문 루트의 일부이기 때문에 괜찮을 것입니다.
  • 스코프를 트리거된 기능으로 처리합니다.

    public class TriggeredJob
    {
        private readonly Container _container;
    
        public TriggeredJob(Container container)
        {
            _container = container;
        }
    
        public async Task TriggeredFunction([ServiceBusTrigger("queueName")] BrokeredMessage message, CancellationToken token)
        {
            using (var scope = _container.BeginExecutionContextScope())
            {
                var processor = _container.GetInstance<IBrokeredMessageProcessor>();
                await processor.ProcessAsync(message, token);
            }
        }
    }
    

그 질문에 대한 모든 대답은 이제 구식입니다.최신 패키지를 사용하면 즉시 생성자 주입을 쉽게 받을 수 있습니다.두 단계만 필요합니다.

  1. 정적이 아닌 클래스의 인스턴스 메서드로 이벤트 핸들러 함수를 만듭니다.수업들읍다시라고 .QueueFunctions.

  2. 클래스를 서비스 목록에 추가합니다.

     builder.ConfigureServices(services =>
     {
         // Add 
         // dependencies
         // here
    
         services.AddScoped<QueueFunctions>();
     });
    

이제 생성자를 통해 종속성을 주입할 수 있습니다.

선택한 IoC 컨테이너의 용어에 따라 하위 컨테이너/범위의 개념에 의존하는 몇 가지 패턴을 사용했습니다.어떤 것이 그것을 지원하는지 확실하지 않지만 StructureMap 2.6.x와 AutoFac이 그것을 지원한다고 말할 수 있습니다.

이 아이디어는 들어오는 각 메시지에 대해 하위 범위를 스핀업하고, 해당 요청에 고유한 컨텍스트를 삽입하고, 하위 범위에서 최상위 개체를 해결한 다음 프로세스를 실행하는 것입니다.

여기 오토팩으로 보여주는 일반화된 코드가 있습니다.사용자가 피하려는 안티패턴과 유사하게 컨테이너에서 직접 확인할 수 있지만 한 곳으로 격리되어 있습니다.

이 경우에는 ServiceBusTrigger를 사용하여 작업을 시작하지만 작업 호스트에 여러 대기열/프로세스에 대한 목록이 있을 수 있습니다.

public static void ServiceBusRequestHandler([ServiceBusTrigger("queuename")] ServiceBusRequest request)
{
   ProcessMessage(request);
}

위 메소드의 모든 인스턴스에서 이 메소드를 호출합니다.하위 범위 만들기를 사용 블록으로 마무리하여 정리할 수 있도록 합니다.그런 다음 요청마다 다르고 다른 종속성(사용자/클라이언트 정보 등)에서 사용되는 컨텍스트를 포함하는 개체가 생성되어 하위 컨테이너(이 예에서는 IRquestContext)에 주입됩니다.마지막으로 작업을 수행하는 구성 요소가 하위 컨테이너에서 해결됩니다.

private static void ProcessMessage<T>(T request) where T : IServiceBusRequest
{
    try
    {
        using (var childScope = _container.BeginLifetimeScope())
        {
            // create and inject things that hold the "context" of the message - user ids, etc

            var builder = new ContainerBuilder();
            builder.Register(c => new ServiceRequestContext(request.UserId)).As<IRequestContext>().InstancePerLifetimeScope();
            builder.Update(childScope.ComponentRegistry);

            // resolve the component doing the work from the child container explicitly, so all of its dependencies follow

            var thing = childScope.Resolve<ThingThatDoesStuff>();
            thing.Do(request);
        }
    }
    catch (Exception ex)
    {

    }
}

언급URL : https://stackoverflow.com/questions/30328775/dependency-injection-using-azure-webjobs-sdk

반응형