简体   繁体   English

使用 Azure WebJobs SDK 进行依赖注入?

[英]Dependency injection using Azure WebJobs SDK?

The problem is that the Azure WebJobs SDK supports only public static methods as job entry-points which means there is no way of implementing constructor/property injection.问题是 Azure WebJobs SDK 仅支持公共静态方法作为作业入口点,这意味着无法实现构造函数/属性注入。

I am unable to find anything about this topic in official WebJobs SDK documentation/resources.我无法在官方 WebJobs SDK 文档/资源中找到有关此主题的任何信息。 The only solution that I came across is based on service locator (anti) pattern described on this post here .我遇到的唯一的解决方案是基于对这个职位描述的服务定位器(反)模式在这里

Is there a good way to use "proper" dependency injection for projects based on Azure WebJobs SDK?有没有一种好方法可以为基于 Azure WebJobs SDK 的项目使用“适当的”依赖项注入?

Azure WebJobs SDK now supports instance methods. Azure WebJobs SDK现在支持实例方法。 Combining this with a custom IJobActivator allows you to use DI. 将其与自定义IJobActivator相结合,您可以使用DI。

First, create the custom IJobActivator that can resolve a job type using your favourite DI container: 首先,创建可以使用您喜欢的DI容器解析作业类型的自定义IJobActivator:

public class MyActivator : IJobActivator
{
    private readonly IUnityContainer _container;

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

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

You need to register this class using a custom JobHostConfiguration: 您需要使用自定义JobHostConfiguration注册此类:

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

Then, you can use a simple class with instance methods for your jobs (here I'm using Unity's constructor injection feature): 然后,您可以使用一个带有实例方法的简单类来完成您的工作(这里我使用的是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);
    }
}

This is how I handled scoping using the new SDK. 这就是我使用新SDK处理范围的方法。 Using the IJobactivator as described by Alexander Molenkamp. 使用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);
        }
    }
}

You can the use your custom MessagingProvider in your JobHostConfiguration like 您可以在JobHostConfiguration中使用自定义MessagingProvider

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

After asking my own question about how to handle scoping ... I've just came up to this solution: I don't think this is ideal but I couldn't find any other solution for the moment. 在询问了我自己关于如何处理范围的问题之后...我刚刚达成了这个解决方案:我认为这不是理想的,但我暂时找不到任何其他解决方案。

In my example I am dealing with ServiceBusTrigger. 在我的例子中,我正在处理ServiceBusTrigger。

As I am using SimpleInjector , the implementation of the IJobActivator interface looks like that: 当我使用SimpleInjector时 ,IJobActivator接口的实现看起来像这样:

public class SimpleInjectorJobActivator : IJobActivator
{
    private readonly Container _container;

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

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

Here, I am dealing with Triggered webjobs. 在这里,我正在处理Triggered webjobs。

So I have two dependencies: 所以我有两个依赖项:

  • A singleton: 单身人士:

     public interface ISingletonDependency { } public class SingletonDependency : ISingletonDependency { } 
  • And another that need to live only the time my function is triggered: 而另一个只需要触发我的函数触发的时间:

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

So in order to have a process that run independently from the webjob. 因此,为了拥有一个独立于webjob运行的进程。 I've encapsulated my process into a class : 我已将我的流程封装到一个类中:

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)
    {
        ...
    }
}

So now when the webjob starts, I need to register my dependencies depending their scopes: 所以现在当webjob启动时,我需要根据其范围注册我的依赖项:

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

And this is the triggered job: 这是触发的工作:

  • Only have one dependency : the IoC container. 只有一个依赖:IoC容器。 Because this class is part of my composition root, it should be ok. 因为这个类是我的组合根的一部分,所以应该没问题。
  • It handle the scope into the triggered function. 它将范围处理为触发功能。

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

I've used a couple patterns that rely on the concept of child containers/scopes (depending on the terminology of your IoC container of choice). 我使用了一些依赖于子容器/范围概念的模式(取决于您选择的IoC容器的术语)。 Not sure which ones support it, but I can tell you that StructureMap 2.6.x and AutoFac do. 不确定哪些支持它,但我可以告诉你StructureMap 2.6.x和AutoFac。

The idea is to spin up a child scope for each message coming in, inject any context that's unique to that request, resolve the top-level object from the child scope, and then run your process. 我们的想法是为每个进入的消息启动子范围,注入该请求唯一的上下文,从子范围解析顶级对象,然后运行您的进程。

Here's some generalized code showing it with AutoFac. 这是一些使用AutoFac显示它的通用代码。 It does do a direct resolve from the container, similar to the anti-pattern you're attempting to avoid, but it's been isolated to one place. 它确实从容器中直接解决,类似于您试图避免的反模式,但它被隔离到一个地方。

In this case, it's using a ServiceBusTrigger to fire the job, but could be anything - a job host could potentially have a list of these for the different queues/processes. 在这种情况下,它使用ServiceBusTrigger来激活作业,但可以是任何东西 - 作业主机可能有一个列表,用于不同的队列/进程。

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

This method is called by all instances of the above methods. 上述方法的所有实例都调用此方法。 It wraps creation of the child scope in a using block to make sure things are cleaned up. 它将子范围的创建包装在一个使用块中,以确保清理事物。 Then, any objects that would vary per request and contain context used by other dependencies (user/client information, etc) would be created and injected into the child container (in this example, the IRequestContext). 然后,将创建每个请求不同并包含其他依赖项(用户/客户端信息等)使用的上下文的任何对象,并将其注入子容器(在此示例中为IRequestContext)。 Finally, the component doing the work would be resolved from the child container. 最后,执行工作的组件将从子容器中解析。

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)
    {

    }
}

All answers to the question are outdated now.该问题的所有答案现在都已过时。 Using the latest packages you can easily get constructor injection right out of the box.使用最新的包,您可以轻松地立即获得构造函数注入。 Two steps are only required:只需要两步:

  1. Create the event handler function as an instance method in a non-static class.在非静态类中创建事件处理函数作为实例方法。 Let's call the class QueueFunctions .让我们将类QueueFunctions

  2. Add your class to the list of services.将您的课程添加到服务列表中。

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

Now, you'll be able to inject dependencies through the constructor.现在,您将能够通过构造函数注入依赖项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用NoAutomaticTrigger时Azure WebJobs SDK中的依赖项注入? - Dependency Injection in Azure WebJobs SDK when using NoAutomaticTrigger? Azure WebJobs 通过 Unity 容器的依赖注入使用 Logger 面临错误 - Azure WebJobs facing error using Logger via dependency injection with Unity container 通过依赖注入将配置传递给webjobs - Passing configuration to webjobs via dependency injection Microsoft.Azure.WebJobs 的传递依赖问题 - Transitive dependency problem with Microsoft.Azure.WebJobs 将JSON对象与webjobs SDK和Azure队列一起使用 - use a JSON object with a webjobs SDK and Azure queue Azure WebJobs SDK TimerTrigger 函数未运行 - Azure WebJobs SDK TimerTrigger Functions not running NServiceBus IUniformSession 不适用于 Azure WebJobs SDK - NServiceBus IUniformSession does not work with Azure WebJobs SDK Azure WebJobs SDK和模拟器 - 触发器无法正常工作 - Azure WebJobs SDK and Emulator - Triggers not working 我在使用 Net Core 3.1 的控制台应用程序中使用带有 Azure SDK WebJobs 的用户机密时遇到问题 - I am having trouble in using User Secrets with Azure SDK WebJobs in a console application using Net Core 3.1 Azure 函数 - 无法从 Azure WebJobs 调用 SDK - Azure Functions - can't be invoked from Azure WebJobs SDK
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM