web-dev-qa-db-ger.com

DI in Azure-Funktionen

Ich habe einige Klassenbibliotheken, die ich in meiner ASP.NET-Web-API-App verwende, die alle meine Backend-Sachen handhaben, z. CRUD-Vorgänge für mehrere Datenbanken wie Azure SQL-Datenbank, Cosmos DB usw.

Ich möchte das Rad nicht neu erfinden und kann es in einer neuen Azure-Funktion verwenden, die ich in Visual Studio 2017 erstelle. Alle meine Repository-Methoden verwenden eine Schnittstelle. Wie kann ich also die Abhängigkeitsinjektion in meine neue Azure-Funktion implementieren?

Ich sehe keine Unterstützung für DI, bin aber etwas verwirrt. Anscheinend basieren Azure-Funktionen auf demselben SDK wie WebJobs, und ich glaube, Microsoft hatte letztes Jahr angefangen, DI in WebJobs zu unterstützen.

Gibt es einen Ausweg, um meine vorhandenen Bibliotheken in meinem neuen Azure Functions-Projekt verwenden zu können?

27
Sam

Ich sehe diese beiden Techniken zusätzlich zum Service Locator (Anti) -Muster. Ich habe das Azure Functions-Team ebenfalls nach ihren Kommentaren gefragt.

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

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

22
Sam

Zu diesem Thema gibt es eine Open Feature-Anforderung auf den GitHub-Seiten für Azure-Funktionen .

Die Art und Weise, wie ich mich dem annehme, ist eine Art 'Wrapper'-Einstiegspunkt. Lösen Sie dies mit dem Service Locator und starten Sie die Funktion von dort.

Das sieht ein bisschen so aus (vereinfacht)

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

var container = builder.Build();

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

  functionLogic.Execute();
}

Dies ist natürlich ein bisschen hackig, aber es ist das Beste, was es gibt, bis es im Moment (meines Wissens) ist.

8
Jan_V

Die Abhängigkeitsinjektion für Azure-Funktionen wurde auf der MSBuild 2019 angekündigt. Hier ein Beispiel zur Vorgehensweise:

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

Wie oben bereits erwähnt, wurde es gerade bei Build 2019 angekündigt. Es kann jetzt fast genau so eingerichtet werden, wie Sie es in einer ASP .Net Core App tun würden.

Microsoft-Dokumentation

Kurzer Blog, den ich geschrieben habe

2

Tatsächlich gibt es eine viel schönere und einfachere Möglichkeit, die von Microsoft standardmäßig bereitgestellt wird. Es ist allerdings etwas schwer zu finden. Sie erstellen einfach eine Startklasse und fügen hier alle erforderlichen Services hinzu. Anschließend können Sie die Konstruktorinjektion wie in normalen Web-Apps und Web-APIs verwenden.

Das ist alles was Sie tun müssen.

Zuerst erstelle ich meine Startup-Klasse. Ich rufe meine Startup.cs auf, um mit Razor-Webanwendungen konsistent zu sein. Dies gilt zwar für Azure-Funktionen, aber es ist immer noch die Art von 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!");
        }
    }
}

Als nächstes ändere ich den Methodenaufruf in der Funktion von statisch in nicht statisch und füge der Klasse einen Konstruktor hinzu (der jetzt auch nicht statisch ist). In diesem Konstruktor füge ich einfach die Dienste hinzu, die ich als Konstruktorparameter benötige.

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

Hoffe das hilft.

1
Michael

AzureFunctions.Autofac ist sehr einfach zu bedienen.

Fügen Sie einfach eine Konfigurationsdatei hinzu:

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

Fügen Sie das DependencyInjectionConfig-Attribut hinzu und fügen Sie dann Folgendes ein:

[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

0
Nate

Ich möchte meine 2 Cent dazu hinzufügen. Ich habe die Technik verwendet, die von Host verwendet wird, um ILogger zu injizieren. Wenn Sie das Startup-Projekt betrachten, habe ich GenericBindingProvider erstellt, der IBindingProvider implementiert. Für jeden Typ, den ich injizieren möchte, registriere ich ihn wie folgt:

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

Der Nachteil ist, dass Sie den Typ registrieren müssen, den Sie zweimal in die Funktion einfügen möchten.

Beispielcode:

Azure Functions V2-Abhängigkeitspritze

0
aczarkowski

Ich habe gesehen, dass der Blog der Willie-Zone in diesem Thema viel erwähnt wurde, aber Sie müssen diesen Weg nicht gehen, um DI mit Azure-Funktionen zu verwenden.

Wenn Sie Version2 verwenden, können Sie Ihre Azure-Funktionen nicht statisch machen. Dann können Sie einen öffentlichen Konstruktor zum Einfügen Ihrer Abhängigkeiten hinzufügen. Der nächste Schritt ist das Hinzufügen einer IWebJobsStartup-Klasse. In Ihrer Startup-Klasse können Sie Ihre Dienste wie für jedes andere .Net Core-Projekt registrieren. 

Ich habe ein öffentliches Repo, das diesen Ansatz hier verwendet: https://github.com/jedi91/MovieSearch/tree/master/MovieSearch

Hier ist ein direkter Link zur Startup-Klasse: https://github.com/jedi91/MovieSearch/blob/master/MovieSearch/Startup.cs

Und hier ist die Funktion: https://github.com/jedi91/MovieSearch/blob/master/MovieSearch/Functions/Search.cs

Hoffe, dieser Ansatz hilft. Wenn Sie möchten, dass Ihre Azure-Funktionen statisch bleiben, sollte der Willie-Zone-Ansatz funktionieren. Ich mag diesen Ansatz jedoch sehr, und es sind keine Bibliotheken von Drittanbietern erforderlich. 

Zu beachten ist die Datei Directory.Build.target. Diese Datei kopiert Ihre Erweiterungen in die Host-Datei, sodass DI funktioniert, sobald die Funktion in Azure bereitgestellt wird. Für das lokale Ausführen der Funktion ist diese Datei nicht erforderlich. 

0