后端实现指南(预览版)
此 Microsoft Fabric 工作负载开发人员示例存储库是构建需要与各种服务集成以及与 Lakehouse 平台集成的应用程序的起点。 本指南可帮助你设置环境,并配置必要的组件以开始使用。 本文概述了关键组件,以及它们在体系结构中的角色。
前端
在前端管理用户体验 (UX) 和行为。 它通过 iFrame 与 Fabric 前端门户进行通信,实现与用户的无缝交互。 有关详细信息,请参阅 Fabric 工作负载开发工具包前端指南。
后端
后端可存储数据和元数据。 它利用 CRUD 操作创建工作负载 (WL) 项以及元数据,并执行作业来填充存储中的数据。 前端与后端之间的通信通过公共 API 建立。
Azure 中继和 DevGateway
Azure 中继支持在本地开发环境和 Fabric 后端之间进行通信,同时在开发模式下运行。
在开发人员模式下,工作负载在开发人员的计算机上运行。
DevGateway.exe
实用工具有两个角色:
- 处理 Azure 中继通道的工作负载端,并在特定容量背景下使用 Fabric 管理工作负载本地实例的注册,使得工作负载可在分配给该容量的所有工作区中访问。 该实用工具在停止时处理注销。
- 通过通道将工作负载 API 调用从 Fabric(连同 Azure 中继)传输到工作负载。
工作负载控制 API 调用直接从工作负载到 Fabric,不需要 Azure 中继通道。
Lakehouse 集成
工作负载开发工具包体系结构与 Lakehouse 无缝集成,允许保存、读取和提取数据之类的操作。 通过 Azure 中继和 Fabric SDK 促进交互,从而确保安全且经过身份验证的通信。 有关详细信息,请参阅使用客户数据
身份验证和安全性
Microsoft Entra ID 用于安全身份验证,从而确保体系结构中的所有交互都获得授权并且的安全的。
此开发工具包概述对体系结构进行简要介绍。 有关项目配置、身份验证指南和入门指南的详细信息,请参阅以下身份验证文档:
前端通过 iFrame 与 Fabric 前端门户建立通信。 门户反过来又通过调用公开的公共 API 来与 Fabric 后端交互。
对于后端开发工具箱和 Fabric 后端之间的交互,Azure 中继充当管道。 此外,后端开发工具箱与 Lakehouse 无缝集成。 使用后端开发工具箱中安装的 Azure 中继和 Fabric 软件开发工具包 (SDK) 促进通信。
通过 Microsoft Entra 来确保对这些组件中的所有通信进行身份验证。 Microsoft Entra 提供一个安全且经过身份验证的环境,用于前端、后端、Azure 中继、Fabric SDK 和湖屋之间的交互。
先决条件
- .NET 7.0 SDK
- Visual Studio 2022
确保 NuGet 包管理器集成到你的 Visual Studio 安装中。 此工具对于我们项目所必需的外部库和包的简化管理是必需的。
NuGet 包管理
<NuspecFile>Packages\manifest\ManifestPackageDebug.nuspec</NuspecFile>
和<NuspecFile>Packages\manifest\ManifestPackageRelease.nuspec</NuspecFile>
:这些属性可指定用于为“调试”模式和“发布”模式创建 NuGet 包的 NuSpec 文件的路径。 NuSpec 文件包含有关包的元数据,例如其 ID、版本、依赖项以及其他相关信息。<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
:当设置为 true 时,此属性指示构建过程在每个构建中自动生成 NuGet 包。 此属性可用于确保包始终与项目中的最新更改保持同步。<IsPackable>true</IsPackable>
:当设置为 true 时,此属性指示项目可打包,这意味着它可以打包到 NuGet 包中。 它是用于在构建过程中生成 NuGet 包的项目的基本属性。
生成完成后,针对“调试”模式生成的 NuGet 包位于 src\bin\Debug
目录中。
在云模式下工作时,可以将 Visual Studio 生成配置切换到“发布”并生成包。 生成的包位于 src\bin\Release
目录中。 有关详细信息,请参阅在云模式下工作指南
依赖项
后端示例样板取决于以下 Azure SDK 包:
- Azure.Core
- Azure.Identity
- Azure.Storage.Files.DataLake
- Microsoft 标识包
要配置 NuGet 包管理器,请在构建过程之前在“包源”部分中指定路径。
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<BuildDependsOn>PreBuild</BuildDependsOn>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IsPackable>true</IsPackable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<NuspecFile>Packages\manifest\ManifestPackageRelease.nuspec</NuspecFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<NuspecFile>Packages\manifest\ManifestPackageDebug.nuspec</NuspecFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Core" Version="1.38.0" />
<PackageReference Include="Azure.Identity" Version="1.11.0" />
<PackageReference Include="Azure.Storage.Files.DataLake" Version="12.14.0" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.60.3" />
<PackageReference Include="Microsoft.IdentityModel.Protocols" Version="6.30.1" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="6.30.1" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.30.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\ServiceDependencies\" />
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="powershell.exe -ExecutionPolicy Bypass -File ValidationScripts\RemoveErrorFile.ps1 -outputDirectory ValidationScripts\" />
<Exec Command="powershell.exe -ExecutionPolicy Bypass -File ValidationScripts\ManifestValidator.ps1 -inputDirectory .\Packages\manifest\ -inputXml WorkloadManifest.xml -inputXsd WorkloadDefinition.xsd -outputDirectory ValidationScripts\" />
<Exec Command="powershell.exe -ExecutionPolicy Bypass -File ValidationScripts\ItemManifestValidator.ps1 -inputDirectory .\Packages\manifest\ -inputXsd ItemDefinition.xsd -outputDirectory ValidationScripts\" />
<Exec Command="powershell.exe -ExecutionPolicy Bypass -File ValidationScripts\ValidateNoDefaults.ps1 -outputDirectory ValidationScripts\" />
<Error Condition="Exists('ValidationScripts\ValidationErrors.txt')" Text="Validation errors with either manifests or default values" File="ValidationScripts\ValidationErrors.txt" />
</Target>
</Project>
入门
若要在本地计算机上设置工作负载示例项目,请按照以下步骤进行操作:
克隆存储库:
git clone https://github.com/microsoft/Microsoft-Fabric-workload-development-sample.git
在 Visual Studio 2022 中打开解决方案。
按照身份验证指南中的说明设置应用注册。 确保前端和后端项目均有指南中所述的必要设置。 Microsoft Entra 用于安全身份验证,从而确保体系结构中的所有交互都获得授权并且的安全的。
更新 OneLake DFS 基 URL:根据 Fabric 环境,可以更新 src\Constants 文件夹中的
OneLakeDFSBaseURL
。 默认值为onelake.dfs.fabric.microsoft.com
,但可以更新此项以反映所处的环境。 有关 DFS 路径的详细信息,请参阅 OneLake 文档。设置工作负载配置
- 将 workload-dev-mode.json 从 src/Config 复制到
C:\
- 在 workload-dev-mode.json 文件中,更新以下字段以匹配配置:
- CapacityGuid:容量 ID。 可以在 Fabric 门户中的管理员门户的容量设置下找到。
- ManifestPackageFilePath:清单包的位置。 生成解决方案时,它将在 src\bin\Debug 中保存清单包。 有关清单包的更多详细信息,请参阅后续步骤。
- WorkloadEndpointURL:工作负载端点 URL。
- 在 Packages/manifest/WorkloadManifest.xml 文件中,更新以下字段以匹配配置:
- <AppId>:工作负载 Microsoft Entra 应用程序的客户端 ID(应用程序 ID)。
- <RedirectUri>:重定向 URI。 可在“身份验证”部分下创建的应用注册中找到。
- <ResourceId>:传入 Microsoft Entra 令牌的受众。 这些信息可在“公开 API”部分下创建的应用注册中找到。
- 在 src/appsettings.json 文件中,更新以下字段以匹配配置:
- PublisherTenantId:工作负载发布者租户的 ID。
- ClientId:工作负载 Microsoft Entra 应用程序的客户端 ID (应用程序 ID)。
- ClientSecret:工作负载 Microsoft Entra 应用程序的密钥。
- 受众:传入 Microsoft Entra 令牌的受众。 这些信息可在“公开 API”部分下创建的应用注册中找到。 这也称为应用程序 ID URI。
- 将 workload-dev-mode.json 从 src/Config 复制到
生成清单包。 要生成清单包文件,请生成 Fabric_Extension_BE_Boilerplate。 这会运行一个三步骤流程以生成清单包文件:
- 在 *Packages\manifest* 中的
ManifestValidator.ps1
上触发WorkloadManifest.xml
,并在 *Packages\manifest* 中的所有项 XML(例如ItemManifestValidator.ps1
)上触发Item1.xml
。 如果验证失败,则会生成错误文件。 可以在 *ValidationScripts* 中查看验证脚本。 - 如果存在错误文件,则会生成失败并显示
Validation errors with either manifests or default values
。 在 Visual Studio 中双击错误以查看错误文件。 - 验证成功后,将
WorkloadManifest.xml
和Item1.xml
文件打包到 ManifestPackage.1.0.0.nupkg 中。 生成的包位于 src\bin\Debug 中。
将 ManifestPackage.1.0.0.nupkg 文件复制到 workload-dev-mode.json 配置文件中定义的路径。
- 在 *Packages\manifest* 中的
Program.cs 用作应用程序的入口点和启动脚本。 在此文件中,你可以配置各种服务、初始化应用程序并启动 Web 主机。
构建以确保项目可以访问编译和执行所需的依赖项。
从 Microsoft 下载中心下载 DevGateway
运行位于 DevGateway 文件夹中的 Microsoft.Fabric.Workload.DevGateway.exe 应用程序。 使用对 workload-dev-mode.json (CapacityGuid) 中定义的容量具有容量管理员权限的用户登录。 在工作负载初始化后,会出现身份验证提示。
身份验证后,外部工作负载通过 Azure 中继与 Fabric 后端建立通信。 此过程涉及中继注册和通信管理,由指定的代理节点协助进行。 此外,将上传并发布包含工作负载清单的包。
在此阶段,Fabric 已了解工作负载,包括其分配的容量。
可以在控制台中查看监测潜在错误的情况。
如果未出现任何错误,则表示已建立连接,注册已成功执行,并且工作负载清单已系统上传。
将 Visual Studio 中的启动项目更改为“样板”项目,然后再选择“运行”。
使用样板
代码生成
我们使用工作负载样板 C# ASP.NET Core 示例来说明如何使用 REST API 生成工作负载。 首先基于工作负载 API Swagger 规范生成服务器存根和协定类。 可以使用多个 Swagger 代码生成工具中的任何一种生成它们。 我们的样板示例使用 NSwag。 此示例包含 GenerateServerStub.cmd 命令行脚本,该脚本封装有 NSwag 代码生成器。 该脚本采用单个参数,它是 NSwag 安装目录的完整路径。 它还需要查找旁边的 Swagger 定义文件 (swagger.json) 和配置文件 (nswag.json)。
执行此脚本可生成 C# 文件 WorkloadAPI_Generated.cs。 此文件的内容可以逻辑划分成三个部分,如下所示。
ASP.NET Core 存根控制器
ItemLifecycleController
和 JobsController
类是 ASP.NET Core 控制器的精简实现,适用于工作负载 API 的两个子集:项生命周期管理和作业。 这些类插入 ASP.NET Core HTTP 管道,并充当 Swagger 规范中定义的 API 方法的入口点。 这些类将调用转发到工作负载提供的“real”实现。
这是 CreateItem 方法的示例:
/// <summary>
/// Called by Microsoft Fabric for creating a new item.
/// </summary>
/// <remarks>
/// Upon item creation Fabric performs some basic validations, creates the item with 'provisioning' state and calls this API to notify the workload. The workload is expected to perform required validations, store the item metadata, allocate required resources, and update the Fabric item metadata cache with item relations and ETag. To learn more see [Microsoft Fabric item update flow](https://updateflow).
/// <br/>
/// <br/>This API should accept [SubjectAndApp authentication](https://subjectandappauthentication).
/// <br/>
/// <br/>##Permissions
/// <br/>Permissions are checked by Microsoft Fabric.
/// </remarks>
/// <param name="workspaceId">The workspace ID.</param>
/// <param name="itemType">The item type.</param>
/// <param name="itemId">The item ID.</param>
/// <param name="createItemRequest">The item creation request.</param>
/// <returns>Successfully created.</returns>
[Microsoft.AspNetCore.Mvc.HttpPost, Microsoft.AspNetCore.Mvc.Route("workspaces/{workspaceId}/items/{itemType}/{itemId}")]
public System.Threading.Tasks.Task CreateItem(System.Guid workspaceId, string itemType, System.Guid itemId, [Microsoft.AspNetCore.Mvc.FromBody] CreateItemRequest createItemRequest)
{
return _implementation.CreateItemAsync(workspaceId, itemType, itemId, createItemRequest);
}
用于工作负载实现的接口
IItemLifecycleController 和 IJobsController 是以前提及的“real”实现的接口。 它们定义控制器实现的相同方法。
协定类的定义
API 使用的 C# 协定类。
实现
生成代码后,下一步是实现 IItemLifecycleController 和 IJobsController 接口。 在样板示例中,ItemLifecycleControllerImpl 和 JobsControllerImpl 实现这些接口。
例如,此代码是 CreateItem API 的实现:
/// <inheritdoc/>
public async Task CreateItemAsync(Guid workspaceId, string itemType, Guid itemId, CreateItemRequest createItemRequest)
{
var authorizationContext = await _authenticationService.AuthenticateControlPlaneCall(_httpContextAccessor.HttpContext);
var item = _itemFactory.CreateItem(itemType, authorizationContext);
await item.Create(workspaceId, itemId, createItemRequest);
}
处理项有效负载
多个 API 方法接受各种类型的“有效负载”作为请求正文的一部分,或将其作为响应中的一部分返回。 例如,CreateItemRequest 具有 creationPayload 属性。
"CreateItemRequest": {
"description": "Create item request content.",
"type": "object",
"additionalProperties": false,
"required": [ "displayName" ],
"properties": {
"displayName": {
"description": "The item display name.",
"type": "string",
"readOnly": false
},
"description": {
"description": "The item description.",
"type": "string",
"readOnly": false
},
"creationPayload": {
"description": "Creation payload specific to the workload and item type, passed by the item editor or as Fabric Automation API parameter.",
"$ref": "#/definitions/CreateItemPayload",
"readOnly": false
}
}
}
这些“有效负载”属性的类型在 Swagger 规范中定义。 每种有效负载都有专用的类型。 这些类型不定义任何具体属性,并允许包含任何属性。 例如,这是 CreateItemPayload 类型:
"CreateItemPayload": {
"description": "Creation payload specific to the workload and item type.",
"type": "object",
"additionalProperties": true
}
生成的 C# 协定类定义为分部类,并具有包含属性的字典。
/// <summary>
/// Creation payload specific to the workload and item type.
/// </summary>
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class CreateItemPayload
{
private System.Collections.Generic.IDictionary<string, object> _additionalProperties;
[Newtonsoft.Json.JsonExtensionData]
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, object>()); }
set { _additionalProperties = value; }
}
}
代码可以使用此字典读取和返回属性。 但是,更好的方法是使用相应的类型和名称来定义特定属性。 由于生成的类上的“分部”声明,可以轻松实现此目的。
例如,CreateItemPayload.cs 文件包含 CreateItemPayload 类的补充定义,其中增加了 Item1Metadata 属性。
namespace Fabric_Extension_BE_Boilerplate.Contracts.FabricAPI.Workload
{
/// <summary>
/// Extend the generated class by adding item-type-specific fields.
/// In this sample every type will have a dedicated property. Alternatively, polymorphic serialization could be used.
/// </summary>
public partial class CreateItemPayload
{
[Newtonsoft.Json.JsonProperty("item1Metadata", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public Item1Metadata Item1Metadata { get; init; }
}
}
但是,如果工作负载支持多个项类型,CreateItemPayload 类需要能够处理不同类型的创建有效负载,每个项类型一个。 可通过两种方式来执行此操作。 样板示例使用的更简单方法是定义多个可选属性,每个属性都表示不同项类型的创建有效负载。 然后,根据正在创建的项类型,每个请求只有一个属性集。 或者,可以实现多态序列化,但在示例中未演示,因为它不能提供任何显著优势。
例如,为了支持两种项类型,此定义需要按如下方式进行扩展:
namespace Fabric_Extension_BE_Boilerplate.Contracts.FabricAPI.Workload
{
public partial class CreateItemPayload
{
[Newtonsoft.Json.JsonProperty("item1Metadata", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public Item1Metadata Item1Metadata { get; init; }
[Newtonsoft.Json.JsonProperty("item2Metadata", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public Item2Metadata Item2Metadata { get; init; }
}
}
注意
发送到工作负载的“有效负载”由客户端生成。 它可以是项编辑器 iFrame 或 Fabric 自动化 REST API。 客户端负责发送正确的有效负载并匹配项类型。 工作负载负责验证。 Fabric 将此有效负载视为不透明对象,仅将其从客户端传输到工作负载。 同样,对于工作负载返回给客户端的有效负载,工作负载和客户端负责正确处理有效负载。
例如,此代码演示样板示例 Item1 实现如何处理:
protected override void SetDefinition(CreateItemPayload payload)
{
if (payload == null)
{
Logger.LogInformation("No payload is provided for {0}, objectId={1}", ItemType, ItemObjectId);
_metadata = Item1Metadata.Default.Clone();
return;
}
if (payload.Item1Metadata == null)
{
throw new InvalidItemPayloadException(ItemType, ItemObjectId);
}
if (payload.Item1Metadata.Lakehouse == null)
{
throw new InvalidItemPayloadException(ItemType, ItemObjectId)
.WithDetail(ErrorCodes.ItemPayload.MissingLakehouseReference, "Missing Lakehouse reference");
}
_metadata = payload.Item1Metadata.Clone();
}
故障排除和调试
已知问题和解决方案
缺少客户端密码
错误:
Microsoft.Identity.Client.MsalServiceException:配置问题正在阻止身份验证。 检查来自服务器的错误消息以了解详细信息。 你可以在应用程序注册门户中修改配置。 有关详细信息,请参阅https://aka.ms/msal-net-invalid-client
。
原始例外:AADSTS7000215:提供的客户端密码无效。 对于添加到应用“app_guid”的机密,请确保请求中发送的机密是客户端密码值,而不是客户端密码 ID。
解决方法:请确保你在 appsettings.json 中具有正确的客户端密码。
由于缺少管理员同意,项目创建过程中出错
错误:
Microsoft.Identity.Client.MsalUiRequiredException:AADSTS65001:用户或管理员不同意使用 ID 为 .... 的应用程序……针对此用户和资源发送交互式授权请求。
解决方法:在项编辑器中,导航到底部,然后选择“导航到身份验证页”。 在“范围”下写入 .default 并选择“获取访问令牌”。 在弹出对话框中批准同意。
由于容量选择,项创建失败
错误:PriorityPlacement:没有可用的核心服务用于优先级放置,只有“name”、“guid”、“workload-name”。
解决方法:你可能正在使用仅有权访问试用版容量的用户。 请确保使用你有权访问的容量。
文件创建失败,显示 404 (NotFound) 错误
错误:
为 filePath 创建新文件失败:'workspace-id'/'lakehouse-id'/Files/data.json。 错误:响应状态代码未指示成功:404 (NotFound)。
解决方法:请确保使用适合环境的 OneLake DFS URL。 例如,如果你使用 PPE 环境,请将 Constants.cs 中的 EnvironmentConstants.OneLakeDFSBaseUrl 更改为相应的 URL。
调试
对各种操作进行故障排除时,可在代码中设置断点以分析和调试行为。 要进行有效调试,请遵照以下步骤操作:
- 在开发环境中打开代码。
- 导航到相关的操作处理程序函数(例如,用于 CRUD 操作的 OnCreateFabricItemAsync 或用于 Execute 操作的控制器中的终结点)。
- 在需要检查代码的特定行上设置断点。
- 在调试模式下运行应用程序。
- 从要调试的前端 (FE) 触发操作。
调试器在指定的断点处暂停执行,使你能够检查变量、逐步检查代码并识别问题。
工作区
如果要将后端连接到示例工作负载,请注意,项必须属于与容量关联的工作区。 默认情况下,“我的工作区”工作区不会与容量关联。 否则,可能会收到此错误:
切换到命名工作区并保留默认的“我的工作区”:
在正确的工作区中,加载示例工作负载并继续执行测试:
参与
我们欢迎大家对这个项目做出的贡献。 如果发现任何问题或想要添加新功能,请遵照以下步骤操作:
- 创建存储库分支。
- 为你的功能或缺陷修复创建新分支。
- 作出更改并提交。
- 将你的更改推送到分支存储库。
- 创建一个拉取请求,其中包含更改的明确说明。