我有一些类库,我在ASP.NET Web API应用程序中使用它来处理我的所有后端内容,例如对多个数据库(如Azure SQL数据库,Cosmos DB等)的CRUD操作
我不想重新发明轮子,并且能够在我在Visual Studio 2017中创建的新Azure功能中使用它们。我的所有存储库方法都使用接口。那么,我将如何在我的新Azure功能中实现依赖注入?
我没有看到对DI的任何支持,但我有点困惑。似乎Azure Functions基于与WebJobs相同的SDK,我认为去年微软已开始在WebJobs中支持DI - 我当然知道,因为我使用Ninject实现了它。
是否可以解决这个问题,以便在新的Azure Functions项目中使用现有的库?
答案 0 :(得分:23)
除了服务定位器(反)模式之外,我还看到了这两种技术。我也向Azure Functions团队询问了他们的意见。
https://blog.wille-zone.de/post/azure-functions-dependency-injection/
https://blog.wille-zone.de/post/azure-functions-proper-dependency-injection/
答案 1 :(得分:8)
关于此事有一个open feature request on the GitHub pages for Azure Functions。
然而,我接近这个的方式是使用某种“包装”。输入点,使用服务定位器解决此问题,并从那里启动该功能。
这看起来有点像(简化)
var builder = new ContainerBuilder();
//register my types
var container = builder.Build();
using(var scope = container.BeginLifetimeScope())
{
var functionLogic = scope.Resolve<IMyFunctionLogic>();
functionLogic.Execute();
}
这当然有点笨拙,但它是最好的,直到现在(据我所知)。
答案 2 :(得分:5)
关于该主题,我已经看到willie-zone博客提到了很多,但是您无需走这条路就可以将DI与Azure函数一起使用。
如果使用的是Version2,则可以使Azure函数变为非静态。然后,您可以添加一个公共构造函数以注入依赖项。下一步是添加IWebJobsStartup类。在启动课程中,您将可以像注册其他任何.Net Core项目一样注册服务。
我有一个使用此方法的公共仓库: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 Functions保持静态,则可以使用willie-zone方法,但是我真的很喜欢这种方法,它不需要任何第三方库。
要注意的一件事是Directory.Build.target文件。此文件会将扩展名复制到主机文件中,以便将功能部署到Azure后DI即可工作。在本地运行该功能不需要此文件。
答案 3 :(得分:1)
我想加2美分。我使用了主机注入ILogger所使用的技术。如果您查看Startup项目,我创建了实现IBindingProvider的GenericBindingProvider。然后,对于每种要注入的类型,我将其注册如下:
builder.Services.AddTransient<IWelcomeService, WelcomeService>();
builder.Services.AddSingleton<IBindingProvider, GenericBindingProvider<IWelcomeService>>();
缺点是您需要两次注册要注入到函数中的类型。
示例代码:
答案 4 :(得分:1)
Azure函数依赖注入是在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>();
}
}
}
答案 5 :(得分:1)
实际上,Microsoft提供了一种更好,更简单的方法。虽然有点难找。您只需创建一个启动类并在此处添加所有必需的服务,然后就可以像在常规Web应用程序和Web API中一样使用构造函数注入。
这就是您要做的一切。
首先,我创建我的启动类,我将其命名为Startup.cs以与Razor Web应用程序保持一致,尽管这是针对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");
}
}
}
希望这会有所帮助。
答案 6 :(得分:0)
AzureFunctions.Autofac非常易于使用。
只需添加一个配置文件:
public class DIConfig
{
public DIConfig(string functionName)
{
DependencyInjection.Initialize(builder =>
{
builder.RegisterType<Sample>().As<ISample>();
...
}, functionName);
}
}
添加DependencyInjectionConfig属性,然后注入:
[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
答案 7 :(得分:0)
答案 8 :(得分:0)
我一直在Azure Functions中使用SimpleInjector很好。只需创建一个具有注册的类(我们称其为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>();
}
}
然后以root身份登录
: 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);
}
}
答案 9 :(得分:0)
我认为这是一个更好的解决方案:
https://github.com/junalmeida/autofac-azurefunctions https://www.nuget.org/packages/Autofac.Extensions.DependencyInjection.AzureFunctions
在您的项目中安装NuGet,然后创建一个Startup.cs并将其放入其中:
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
然后在函数代码中,您可以通过DI进行普通的构造函数注入:
[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)
}
...
答案 10 :(得分:0)
对依赖关系注入的支持始于Azure Functions 2.x ,这意味着Azure功能中的依赖关系注入现在可以利用.NET Core依赖关系注入功能。
在使用依赖项注入之前,必须安装以下NuGet软件包:
具有依赖项注入功能可简化诸如DBContext,Http客户端使用(Httpclienfactory),Iloggerfactory,缓存支持等操作。
首先,如下所示更新Startup类
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.
}
}
}
第二,删除Function类和方法级别的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.");
}
如果我们查看上述内容,例如IHelloWorld是使用.NET Core DI注入的
**注意:**尽管具有用于依赖注入的最新版本的Azure函数v3的最新版本以启用了几个步骤,但手动操作如上所示
可以在github上找到示例代码here