且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用.NET Core 3.1的Azure Webjob

更新时间:2023-02-15 12:56:05

更新0515:

解决方法:

1.请同时添加appsettings.jsonappsettings.dev.json(请记住右键单击这些文件->选择属性->将复制到输出目录"设置为如果更新则复制")文件到您的项目中. 2个json文件应具有相同的结构(键),但值可以不同.如下所示:

我的appsettings.json文件:

{
  "AzureWebJobsStorage": "xxxxx",
  "EventHubConnectionString": "xxxx",
  "EventHubName": "yyeventhub1",
  "ConsumerGroupName": "cg1"
}

我的appsettings.dev.json文件:

   {
      "AzureWebJobsStorage": "xxxxx",
      "EventHubConnectionString": "xxxx",
      "EventHubName": "yyeventhub2",
      "ConsumerGroupName": "hub2cg"
    }

2.在 CustomNameResolver 类中,使用以下代码:

public class CustomNameResolver : INameResolver
{
    public string Resolve(string name)
    {
        var config = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile("appsettings.dev.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();

        return config[name];
    }
}

注释,应该在appsettings.dev.json之前添加appsettings.json,因此appsettings.dev.json中的设置具有较高的优先级,它将覆盖appsettings.json中的相同键. >

3.然后运行该项目并将事件发送到appsettings.dev.json中定义的 yyeventhub2 ,然后可以发现已触发Webjob.


原始答案:

在函数中使用%%时有一些限制.

根据我的测试,这里有一些注意事项:

1.一些最新的nuget软件包不能与eventhub触发器一起使用,您应该使用以下版本的nuget软件包:

<ItemGroup>
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="3.0.0" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.EventHubs" Version="4.1.1" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.10" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.4" />
    <PackageReference Include="System.Configuration.ConfigurationManager" Version="4.7.0" />
  </ItemGroup>

2.无法在Functions.cs中以%EventHubConnectionString%格式使用eventhub命名空间连接字符串.

3.请勿使用appsettings.json中的嵌套设置.

4.请勿在appsettings.json中指定EventProcessorHostNameStorageContainerName. webjobs SDK会自动为您设置它们.

5.使用%%格式时,应使用将复制到输出目录"设置为如果更新则复制"):

{     
    "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=xx;AccountKey=xx;BlobEndpoint=https://xxx.blob.core.windows.net/;TableEndpoint=https://xxx.table.core.windows.net/;QueueEndpoint=https://xxx.queue.core.windows.net/;FileEndpoint=https://xxx.file.core.windows.net/",
    "EventHubConnectionString": "Endpoint=sb://xxx.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=xxx",
    "EventHubName": "xxx",
    "ConsumerGroupName": "xxx"  
}

我的NameResolver类(创建一个名为CustomNameResolver.cs的自定义类):

public class CustomNameResolver : INameResolver
{
    public string Resolve(string name)
    {
        var config = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        //.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();

        return config[name];
    }
}

Program.cs:

class Program
{     

    static void Main(string[] args)
    {
        var builder = new HostBuilder();
        var resolver = new CustomNameResolver();

        builder.ConfigureWebJobs(b =>
        {
            b.AddAzureStorageCoreServices();
            b.AddAzureStorage();
            b.AddEventHubs();
        });
        builder.ConfigureLogging((context, b) =>
        {
            b.AddConsole();
        });          

        builder.ConfigureServices(s => s.AddSingleton<INameResolver>(resolver));

        var host = builder.Build();
        using (host)
        {
            host.Run();
        }
    }
}

Functions.cs:

public class Functions
{
    public static void ProcessEvent([EventHubTrigger("%EventHubName%", Connection = "EventHubConnectionString",ConsumerGroup = "%ConsumerGroupName%")] EventData eventData, ILogger logger)
    {
        logger.LogInformation("it is triggered!" + DateTime.Now.ToLongTimeString());
    }
}

测试结果:

I am writing a azure webjob using .net core 3.1 and I am getting the following runtime exception: InvalidOperationException: %EventHubName% does not resolve to a value.

where my trigger looks like: ProcessEvent([EventHubTrigger("%EventHubName%", ConsumerGroup = "%ConsumerGroupName%")] EventData eventData)

I have registered the configuration in program.cs

I have added appSettings.environment.json file which contains something like:

"EventHubConfig": { "EventHubConnectionString": "..", "EventHubName": "..", "EventProcessorHostName": "..", "ConsumerGroupName": "..", "StorageConnectionString": "..", "StorageContainerName": ".." },

Can anyone suggest what I maybe missing?

Update 0515:

The workaround:

1.Please add both appsettings.json and appsettings.dev.json(Remember right click these files -> select properties -> set "Copy to Output Directory" as "copy if newer") files into your project. The 2 json files should have the same structure(the keys), but the value can be different. Like below:

My appsettings.json file:

{
  "AzureWebJobsStorage": "xxxxx",
  "EventHubConnectionString": "xxxx",
  "EventHubName": "yyeventhub1",
  "ConsumerGroupName": "cg1"
}

My appsettings.dev.json file:

   {
      "AzureWebJobsStorage": "xxxxx",
      "EventHubConnectionString": "xxxx",
      "EventHubName": "yyeventhub2",
      "ConsumerGroupName": "hub2cg"
    }

2.In CustomNameResolver class, use the code below:

public class CustomNameResolver : INameResolver
{
    public string Resolve(string name)
    {
        var config = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile("appsettings.dev.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();

        return config[name];
    }
}

note, appsettings.json should be added before appsettings.dev.json, so the settings in appsettings.dev.json has high priority which will overwrite the same keys in appsettings.json.

3.Then run the project and send events to yyeventhub2 which is defined in appsettings.dev.json, then you can find the webjob is triggered.


Original answer:

There're some limitations when use %% in the function.

As per my test, here are some notes:

1.Some latest nuget packages cannot work with eventhub trigger, you should use the below version of nuget packages:

<ItemGroup>
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="3.0.0" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.EventHubs" Version="4.1.1" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.10" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.4" />
    <PackageReference Include="System.Configuration.ConfigurationManager" Version="4.7.0" />
  </ItemGroup>

2.The eventhub namespace connection string cannot be used in this format %EventHubConnectionString% in the Functions.cs.

3.Don't use the nested settings in appsettings.json.

4.Don't specify EventProcessorHostName and StorageContainerName in appsettings.json. The webjobs SDK automatically set them for you.

5.When use %% format, you should use Custom binding expressions to resolve the name.

The following are my code and appsettings.json:

appsettings.json(please also right click this file -> select Properties -> set "Copy to Output Directory" as "Copy if newer"):

{     
    "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=xx;AccountKey=xx;BlobEndpoint=https://xxx.blob.core.windows.net/;TableEndpoint=https://xxx.table.core.windows.net/;QueueEndpoint=https://xxx.queue.core.windows.net/;FileEndpoint=https://xxx.file.core.windows.net/",
    "EventHubConnectionString": "Endpoint=sb://xxx.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=xxx",
    "EventHubName": "xxx",
    "ConsumerGroupName": "xxx"  
}

My NameResolver class(create a custom class named CustomNameResolver.cs):

public class CustomNameResolver : INameResolver
{
    public string Resolve(string name)
    {
        var config = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        //.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();

        return config[name];
    }
}

Program.cs:

class Program
{     

    static void Main(string[] args)
    {
        var builder = new HostBuilder();
        var resolver = new CustomNameResolver();

        builder.ConfigureWebJobs(b =>
        {
            b.AddAzureStorageCoreServices();
            b.AddAzureStorage();
            b.AddEventHubs();
        });
        builder.ConfigureLogging((context, b) =>
        {
            b.AddConsole();
        });          

        builder.ConfigureServices(s => s.AddSingleton<INameResolver>(resolver));

        var host = builder.Build();
        using (host)
        {
            host.Run();
        }
    }
}

Functions.cs:

public class Functions
{
    public static void ProcessEvent([EventHubTrigger("%EventHubName%", Connection = "EventHubConnectionString",ConsumerGroup = "%ConsumerGroupName%")] EventData eventData, ILogger logger)
    {
        logger.LogInformation("it is triggered!" + DateTime.Now.ToLongTimeString());
    }
}

Test result: