从 Azure 逻辑应用的标准工作流中创建并运行 .NET Framework 代码

适用于:Azure 逻辑应用(标准)

对于必须在标准逻辑应用工作流中创作和运行 .NET Framework 代码的集成解决方案,可以使用 Visual Studio Code 和 Azure 逻辑应用(标准版)扩展。 此扩展提供以下功能和优势:

  • 通过创建具有灵活性和控制能力的函数来编写自己的代码,以解决最具挑战性的集成问题。
  • 在 Visual Studio Code 中本地调试代码。 在同一调试会话中逐步执行代码和工作流。
  • 同步部署代码与工作流。 不需要其他服务计划。
  • 支持 BizTalk Server 迁移方案,以便你可以将自定义 .NET Framework 投资从本地平稳迁移到云端。

借助自行编写代码的功能,你可以完成如下方案:

  • 自定义业务逻辑实现
  • 自定义分析功能,可从入站消息中提取信息
  • 数据验证和简单转换
  • 为到另一个系统(例如 API)的出站消息进行消息塑形
  • 计算

此功能不适用于以下方案:

  • 需要超过 10 分钟才能运行的进程
  • 大型消息和数据转换
  • 复杂的批处理和解批处理方案
  • 实现流式处理的 BizTalk Server 管道组件

有关 Azure 逻辑应用中的限制的详细信息,请参阅限制和配置 - Azure 逻辑应用

先决条件

限制

自定义函数创作目前在 Microsoft Azure 门户中不可用。 但是,将函数从 Visual Studio Code 部署到 Azure 后,请按照 Microsoft Azure 门户的从工作流调用代码中的步骤操作。 可以使用名为“调用此逻辑应用中的本地函数”的内置操作从部署的自定义函数中进行选择并运行代码。 工作流中的后续操作可以引用这些函数的输出,就像任何其他工作流一样。 可以查看内置操作的运行历史记录、输入和输出。

创建代码项目

适用于 Visual Studio Code 的最新 Azure 逻辑应用(标准版)扩展包含一个代码项目模板,该模板为编写、调试和部署你自己的代码和工作流提供了简化体验。 此项目模板创建了一个工作区文件和两个示例项目:一个项目用于编写代码,另一个项目用于创建工作流。

注意

不能对代码和工作流使用相同的项目文件夹。

  1. 打开 Visual Studio Code。 在活动栏中选择 Azure 图标。 (键盘:Shift+Alt+A)

  2. 在打开的 Azure 窗口中,在“工作区”部分工具栏的“Azure 逻辑应用”菜单中选择“新建逻辑应用工作区”。

    Screenshot shows Visual Studio Code, Azure window, Workspace section toolbar, and selected option for Create new logic app workspace.

  3. 在“选择文件夹”框中,浏览到并选择为项目创建的本地文件夹。

  4. 当出现“创建新的逻辑应用工作区”提示框时,请提供工作区的名称:

    Screenshot shows Visual Studio Code with prompt to enter workspace name.

    此示例继续使用 MyLogicAppWorkspace

  5. 当出现“选择逻辑应用工作区的项目模板”提示框时,请选择“具有自定义代码项目的逻辑应用”。

    Screenshot shows Visual Studio Code with prompt to select project template for logic app workspace.

  6. 按照后续提示提供以下示例值:

    示例值
    函数项目的函数名称 WeatherForecast
    函数项目的命名空间名称 Contoso.Enterprise
    工作流模板:
    - 有状态工作流
    - 无状态工作流
    有状态工作流
    工作流名称 MyWorkflow
  7. 选择“在当前窗口中打开”。

    完成此步骤后,Visual Studio Code 会创建工作区,其中默认包括函数项目和逻辑应用项目,例如:

    Screenshot shows Visual Studio Code with created workspace.

    节点 说明
    <workspace-name> 包含你的函数项目和逻辑应用工作流项目。
    函数 包含你的函数项目的工件。 例如,<function-name>.cs 文件是可在其中创作代码的代码文件。
    逻辑应用 包含你的逻辑应用项目的工件,包括空白工作流。

编写代码

  1. 在工作区中,展开“Functions”节点(如果尚未展开)。

  2. 打开 <function-name>.cs 文件,此示例中名为 WeatherForecast.cs

    默认情况下,此文件包含具有以下代码元素的示例代码,它具有前面提供的示例值(如果适用):

    • 命名空间名称
    • 类名
    • 函数名称
    • 函数参数
    • 返回类型
    • 复杂类型

    以下示例展示了完整的示例代码:

    //------------------------------------------------------------
    // Copyright (c) Microsoft Corporation. All rights reserved.
    //------------------------------------------------------------
    
    namespace Contoso.Enterprise
    {
        using System;
        using System.Collections.Generic;
        using System.Threading.Tasks;
        using Microsoft.Azure.Functions.Extensions.Workflows;
        using Microsoft.Azure.WebJobs;
        using Microsoft.Extensions.Logging;
    
        /// <summary>
        /// Represents the WeatherForecast flow invoked function.
        /// </summary>
        public class WeatherForecast
        {
    
            private readonly ILogger<WeatherForecast> logger;
    
            public WeatherForecast(ILoggerFactory loggerFactory)
            {
                logger = loggerFactory.CreateLogger<WeatherForecast>();
            }
    
            /// <summary>
            /// Executes the logic app workflow.
            /// </summary>
            /// <param name="zipCode">The zip code.</param>
            /// <param name="temperatureScale">The temperature scale (e.g., Celsius or Fahrenheit).</param>
            [FunctionName("WeatherForecast")]
            public Task<Weather> Run([WorkflowActionTrigger] int zipCode, string temperatureScale)
            {
    
                this.logger.LogInformation("Starting WeatherForecast with Zip Code: " + zipCode + " and Scale: " + temperatureScale);
    
                // Generate random temperature within a range based on the temperature scale
                Random rnd = new Random();
                var currentTemp = temperatureScale == "Celsius" ? rnd.Next(1, 30) : rnd.Next(40, 90);
                var lowTemp = currentTemp - 10;
                var highTemp = currentTemp + 10;
    
                // Create a Weather object with the temperature information
                var weather = new Weather()
                {
                    ZipCode = zipCode,
                    CurrentWeather = $"The current weather is {currentTemp} {temperatureScale}",
                    DayLow = $"The low for the day is {lowTemp} {temperatureScale}",
                    DayHigh = $"The high for the day is {highTemp} {temperatureScale}"
                };
    
                return Task.FromResult(weather);
            }
    
            /// <summary>
            /// Represents the weather information for WeatherForecast.
            /// </summary>
            public class Weather
            {
                /// <summary>
                /// Gets or sets the zip code.
                /// </summary>
                public int ZipCode { get; set; }
    
                /// <summary>
                /// Gets or sets the current weather.
                /// </summary>
                public string CurrentWeather { get; set; }
    
                /// <summary>
                /// Gets or sets the low temperature for the day.
                /// </summary>
                public string DayLow { get; set; }
    
                /// <summary>
                /// Gets or sets the high temperature for the day.
                /// </summary>
                public string DayHigh { get; set; }
            }
        }
    }
    

    函数定义包括可用于入门的默认 Run 方法。 此示例 Run 方法演示了自定义函数特性提供的一些功能,例如传递不同的输入和输出,包括复杂的 .NET 类型。

    <function-name>.cs 文件还包括 ILogger 接口,该接口支持将事件记录到 Application Insights 资源。 可以将跟踪信息发送到 Application Insights,并将该信息与工作流中的跟踪信息一起存储,例如:

    private readonly ILogger<WeatherForecast> logger;
    
    public WeatherForecast(ILoggerFactory loggerFactory)
    {
        logger = loggerFactory.CreateLogger<WeatherForecast>();
    }
    
    [FunctionName("WeatherForecast")]
    public Task<Weather> Run([WorkflowActionTrigger] int zipCode, string temperatureScale)
    {
    
        this.logger.LogInformation("Starting WeatherForecast with Zip Code: " + zipCode + " and Scale: " + temperatureScale);
    
        <...>
    
    }
    
  3. 将示例函数代码替换为你自己的代码,并为你自己的方案编辑默认 Run 方法。 或者,可以复制该函数(包括 [FunctionName("<*function-name*>")] 声明),然后使用唯一名称重命名该函数。 然后,可以编辑重命名后的函数以满足你的需求。

此示例会继续执行示例代码,不进行任何更改。

编译和生成代码

编写完代码后,进行编译以确保不存在生成错误。 你的函数项目会自动包含生成任务,这些任务会编译代码,然后将其添加到逻辑应用项目中的 lib\custom 文件夹中,其中工作流会查找要运行的自定义函数。 这些任务将程序集置于 lib\custom\net472 文件夹中。

  1. 在 Visual Studio Code 的“终端”菜单中,选择“新建终端”。

  2. 在显示的工作目录列表中,选择“Functions”作为新终端的当前工作目录。

    Screenshot shows Visual Studio Code, prompt for current working directory, and selected Functions directory.

    Visual Studio Code 会打开终端窗口,其中包含一个命令提示符。

  3. 终端窗口的命令提示符处,输入 dotnet restore

    Visual Studio Code 会分析你的项目并确定它们是否是最新的。

    Screenshot shows Visual Studio Code, Terminal window, and completed dotnet restore command.

  4. 命令提示符重新出现后,输入 dotnet build。 或者,在“终端”菜单中,选择“运行任务”。 从任务列表中,选择“生成 (Functions)”。

    如果你的生成成功,则终端窗口将报告生成成功

  5. 确认你的逻辑应用项目中存在以下项:

    • 在工作区中,展开以下文件夹:LogicApp>lib\custom>net472。 确认名为 net472 的子文件夹包含运行代码所需的多个程序集 (DLL) 文件,包括名为 <function-name>.dll 的文件。

    • 在工作区中,展开以下文件夹:LogicApp>lib\custom><函数名称>。 确认名为 <function-name> 的子文件夹包含 function.json 文件,该文件包含有关你编写的函数代码的元数据。 工作流设计器使用此文件来确定调用你的代码时所需的输入和输出。

    以下示例显示了逻辑应用项目中示例生成的程序集和其他文件:

    Screenshot shows Visual Studio Code and logic app workspace with function project and logic app project, now with the generated assemblies and other required files.

从工作流调用你的代码

确认你的代码已编译且逻辑应用项目包含代码运行所需的文件后,打开逻辑应用项目中包含的默认工作流。

  1. 在工作区的“LogicApp”下,展开 <workflow-name> 节点,打开 workflow.json 的快捷菜单,然后选择“打开设计器”。

    在打开的工作流设计器上,你的逻辑应用项目包含的默认工作流会随以下触发器和操作一起显示:

  2. 选择名为“在此逻辑应用中调用本地函数”的操作。

    该操作的信息窗格会在右侧打开。

    Screenshot shows Visual Studio Code, workflow designer, and default workflow with trigger and actions.

  3. 查看并确认 Function Name 参数值已设为要运行的函数。 查看或更改函数使用的任何其他参数值。

调试代码和工作流

  1. 重复以下步骤以启动 Azurite 存储模拟器次:为以下每个 Azure 存储服务启动一次:

    • Azure Blob 服务
    • Azure 队列服务
    • Azure 表服务
    1. 在 Visual Studio Code 的“视图”菜单中选择“命令面板”。

    2. 在出现的提示中,找到并选择“Azurite:启动 Blob 服务”。

    3. 在显示的工作目录列表中,选择 LogicApp

    4. Azurite:启动队列服务Azurite:启动表服务重复这些步骤。

    当屏幕底部的 Visual Studio Code 任务栏显示三个存储服务正在运行时,你就成功了,例如:

    Screenshot shows Visual Studio Code taskbar with Azure Blob Service, Azure Queue Service, and Azure Table Service running.

  2. 在 Visual Studio Code 活动栏上,选择“运行并调试”。 (键盘:Ctrl+Shift+D)

    Screenshot shows Visual Studio Code Activity Bar with Run and Debug selected.

  3. 从“运行并调试”列表中,选择“附加到逻辑应用 (LogicApp)”(如果尚未选择),然后选择“运行”(绿色箭头)。

    Screenshot shows Run and Debug list with Attach to logic app selected and Play button selected.

    终端”窗口随即打开,并显示已开始的调试过程。 然后,将显示“调试控制台”窗口,并显示调试状态。 在 Visual Studio Code 底部,任务栏变为橙色,表示 .NET 调试程序已加载。

  4. 在“运行并调试”列表中,选择“附加到 .NET Functions (Functions)”,然后选择“运行”(绿色箭头)。

    Screenshot shows Run and Debug list with Attach to NET Functions selected and Play button selected.

  5. 若要设置任何断点,在你的函数定义 (<function-name>.cs) 或工作流定义 (workflow.json) 中,找到需要断点的行号,然后选择左侧的列,例如:

    Screenshot shows Visual Studio Code and the open function code file with a breakpoint set for a line in code.

  6. 若要在工作流中手动运行请求触发器,请打开工作流的“概述”页。

    1. 在逻辑应用项目中,打开 workflow.json 文件的快捷菜单,然后选择“概述”。

      在工作流的“概述”页上,当你想要手动启动工作流时,可以使用“运行触发器”按钮。 在“工作流属性”下,“回叫 URL”值是你的工作流中请求触发器创建的可调用终结点的 URL。 可以向此 URL 发送请求,以从其他应用(包括其他逻辑应用工作流)触发工作流。

      Screenshot shows Visual Studio Code and workflow's Overview page opened.

  7. 在“概述”页工具栏中,选择“运行触发器”。

    工作流开始运行后,调试器会激活你的第一个断点。

  8. 在“运行”菜单或调试程序工具栏中,选择“调试操作”。

    工作流运行完成后,“概述”页会显示已完成的运行以及有关该运行的基本详情。

  9. 要查看有关工作流运行的详细信息,请选择已完成的运行。 或者,在“持续时间”列旁边的列表中,选择“显示运行”。

    Screenshot shows Visual Studio Code and finished workflow run.

部署你的代码

可以通过部署逻辑应用项目相同的方式部署自定义函数。 无论是从 Visual Studio Code 部署还是使用 CI/CD DevOps 过程进行部署,请确保在部署之前生成代码,并且所有依赖程序集都存在于逻辑应用项目的 lib/custom/net472 文件夹中。 有关详细信息,请参阅将标准工作流从 Visual Studio Code 部署到 Azure

排查问题

操作信息窗格错误

在工作流设计器中,当你选择名为“调用此逻辑应用中的本地函数”的内置操作时,该操作的信息窗格会显示以下消息:

Failed to retrieve dynamic inputs. Error details:

在此方案中,检查你的逻辑应用项目以确定 LogicApp\lib\custom 文件夹是否为空。 如果为空,请在“终端”菜单中选择“运行任务>生成 Functions”。

当前没有具有指定名称的进程在运行

如果在运行工作流时收到此错误消息,则很可能是因为你将调试程序进程附加到了 .NET Functions,而不是你的逻辑应用。

要解决此问题,请在“运行并调试”列表中,选择“附加到逻辑应用 (LogicApp)”,然后选择“运行”(绿色三角形)。

包未正确导入

如果“输出”窗口显示了类似于以下消息的错误,请确保你已安装 .NET 6.0。 如果已安装此版本,请尝试卸载,然后重新安装。

C:\Users\yourUserName\.nuget\packages\microsoft.net.sdk.functions\4.2.0\build\Microsoft.NET.Sdk.Functions.targets(83,5): warning : The ExtensionsMetadataGenerator package was not imported correctly. Are you missing 'C:\Users\yourUserName\.nuget\packages\microsoft.azure.webjobs.script.extensionsmetadatagenerator\4.0.1\build\Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator.targets' or 'C:\Users\yourUserName\.nuget\packages\microsoft.azure.webjobs.script.extensionsmetadatagenerator\4.0.1\build\Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator.props'? [C:\Desktop\...\custom-code-project\MyLogicAppWorkspace\Function\WeatherForecast.csproj] WeatherForecast -> C:\Desktop\...\custom-code-project\MyLogicAppWorkspace\Function\\bin\Debug\net472\WeatherForecast.dll C:\Users\yourUserName\.nuget\packages\microsoft.net.sdk.functions\4.2.0\build\Microsoft.NET.Sdk.Functions.Build.targets(32,5): error : It was not possible to find any compatible framework version [C:\Desktop\...\custom-code-project\MyLogicAppWorkspace\Function\WeatherForecast.csproj] C:\Users\yourUserName\.nuget\packages\microsoft.net.sdk.functions\4.2.0\build\Microsoft.NET.Sdk.Functions.Build.targets(32,5): error : The specified framework 'Microsoft.NETCore.App', version '6.0.0' was not found. [C:\Desktop\...\custom-code-project\MyLogicAppWorkspace\Function\WeatherForecast.csproj] C:\Users\yourUserName\.nuget\packages\microsoft.net.sdk.functions\4.2.0\build\Microsoft.NET.Sdk.Functions.Build.targets(32,5): error : - Check application dependencies and target a framework version installed at: [C:\Desktop\...\custom-code-project\MyLogicAppWorkspace\Function\WeatherForecast.csproj]

生成失败

如果你的函数不包含变量,并且生成了代码,则“输出”窗口可能会显示以下错误消息:

C:\Users\yourUserName\...\custom-code-project\Function\func.cs (24,64): error CS1031: Type expected [C:\Users\yourUserName\...\custom-code-project\Function\func.csproj]
C:\Users\yourUserName\...\custom-code-project\Function\func.cs (24,64): error CS1001: Identifier expected [C:\Users\yourUserName\...\custom-code-project\Function\func.csproj]

Build FAILED.

C:\Users\yourUserName\...\custom-code-project\Function\func.cs (24,64): error CS1031: Type expected [C:\Users\yourUserName\...\custom-code-project\Function\func.csproj]
C:\Users\yourUserName\...\custom-code-project\Function\func.cs (24,64): error CS1001: Identifier expected [C:\Users\yourUserName\...\custom-code-project\Function\func.csproj]

0 Warning(s)
2 Error(s)

要解决此问题,请在代码的 Run 方法中追加以下参数:

string parameter1 = null

以下示例显示了 Run 方法签名的显示方式:

public static Task<Weather> Run([WorkflowActionTrigger] int zipCode, string temperatureScale, string parameter1 = null)

后续步骤

使用 Visual Studio Code 创建标准工作流