实现 Microsoft Fabric 后端
此 Microsoft Fabric 工作负载开发示例存储库是构建需要与各种服务集成的应用程序以及与湖屋体系结构集成的起点。 本文可帮助你设置环境,并配置必要的组件以开始使用。 本文概述了关键组件,以及它们在体系结构中的角色。
前端
在前端管理用户体验 (UX) 和行为。 它通过 iFrame 与 Fabric 前端门户进行通信,以促进无缝交互。
有关详细信息,请参阅 Microsoft Fabric 工作负载开发工具包前端。
后端
后端可存储数据和元数据。 它使用创建、读取、更新和删除 (CRUD) 操作来创建工作负载项和元数据,且它执行作业来填充存储中的数据。 前端与后端之间的通信通过公共 API 建立。
Azure 中继和 DevGateway
Azure 中继支持在本地开发环境和 Fabric 后端之间以开发人员模式进行通信。 在开发人员模式下,工作负载在开发人员的计算机上运行。
DevGateway 实用工具有两个角色:
- 它处理 Azure 中继通道的工作负载端,并在特定容量的背景下使用 Fabric 管理工作负载本地实例的注册。 DevGateway 使得可在分配到该容量的所有工作区中访问该工作负载。 该实用工具在通道停止时处理注销。
- 它与 Azure 中继协作,通过通道将工作负载 API 调用从 Fabric 传输到工作负载。
直接从工作负载向 Fabric 进行工作负载控制 API 调用。 该调用不需要 Azure 中继通道。
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 用于安全身份验证,帮助确保体系结构中的所有交互都获得授权并且的安全的。
更新 Microsoft OneLake DFS 基 URL。 根据 Fabric 环境,你可能能够更新 src\Constants 文件夹中
OneLakeDFSBaseURL
的值。 默认值为onelake.dfs.fabric.microsoft.com
,但可更新 URL 以反映你的环境。 有关 DFS 路径的详细信息,请参阅 OneLake 文档。设置工作负载配置。
- 将 workload-dev-mode.json 从 src/Config 复制到 C:。
- 在 workload-dev-mode.json 文件中,更新以下字段以匹配配置:
- WorkspaceGuid:你的工作区 ID。 在 Fabric 中选择一个工作区时,可在浏览器 URL 中找到此值。 例如,
https://app.powerbi.com/groups/<WorkspaceID>/
。 - ManifestPackageFilePath:清单包的位置。 生成解决方案时,它在 src\bin\Debug 中保存清单包。 本文后面提供了有关清单包的详细信息。
- WorkloadEndpointURL:工作负载终结点 URL。
- WorkspaceGuid:你的工作区 ID。 在 Fabric 中选择一个工作区时,可在浏览器 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”。
生成清单包。
要生成清单包文件,请生成 Fabric_Extension_BE_Boilerplate。 该生成是一个用于生成清单包文件的三步骤流程。 它运行以下步骤:
- 对 Packages\manifest/ 中的 WorkloadManifest.xml 触发 ManifestValidator.ps1,并对 Packages\manifest/ 中的所有项 XML(例如 Item1.xml)触发 ItemManifestValidator.ps1。 如果验证失败,则生成错误文件。 可在 ValidationScripts/ 中查看验证脚本。
- 如果存在错误文件,则生成失败,并出现错误“清单或默认值验证错误”。 若要在 Visual Studio 中查看错误文件,请在验证结果中双击该错误。
- 成功验证后,将 WorkloadManifest.xml 和 Item1.xml 文件打包到 ManifestPackage.1.0.0.nupkg 中。 生成的包位于 src\bin\Debug 中。
将 ManifestPackage.1.0.0.nupkg 文件复制到 workload-dev-mode.json 配置文件中定义的路径。
Program.cs 用作应用程序的入口点和启动脚本。 在此文件中,你可以配置各种服务、初始化应用程序并启动 Web 主机。
构建以确保项目可以访问编译和执行所需的依赖项。
从 Microsoft 下载中心下载 DevGateway
运行 Microsoft.Fabric.Workload.DevGateway.exe 应用程序,并使用对 workload-dev-mode.json 的
WorkspaceGuid
字段中指定的工作区具有工作区管理员权限的用户登录。身份验证后,外部工作负载通过 Azure 中继与 Fabric 后端建立通信。 此过程涉及中继注册和通信管理,由指定的代理节点协助进行。 上传并发布包含工作负载清单的包。
在此阶段,Fabric 检测工作负载并整合其分配的容量。
可监视控制台中的潜在错误。
如果未显示任何错误,则已建立连接,注册已成功执行,并且工作负载清单已系统地上传。
在 Visual Studio 中,将启动项目更改为样本项目,然后选择“运行”。
使用样本示例项目
代码生成
我们使用工作负载样本 C# ASP.NET Core 示例来演示如何使用 REST API 生成工作负载。 该示例首先基于工作负载 API Swagger 规范生成服务器存根和协定类。 可使用多个 Swagger 代码生成工具中的任何一个来生成代码。 该样本示例使用 NSwag。 此示例包含 GenerateServerStub.cmd 命令行脚本,该脚本封装有 NSwag 代码生成器。 该脚本采用单个参数,它是 NSwag 安装目录的完整路径。 它还会检查文件夹中的 Swagger 定义文件 (swagger.json) 和配置文件 (nswag.json)。
执行此脚本可生成一个名为 WorkloadAPI_Generated.cs 的 C# 文件。 此文件的内容可逻辑划分成三个部分,如后续各节所述。
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”实现的接口。 它们定义控制器实现的相同方法。
协定类的定义
C# 协定类是 API 使用的类。
实现
生成代码后的第二步是实现 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# 协定类定义为 partial
。 它们有一个定义了属性的字典。
下面是一个示例:
/// <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; }
}
}
代码可使用此字典读取和返回属性。 但是,更好的方法是使用相应的类型和名称来定义特定属性。 可对生成的类使用 partial
声明,以有效地定义属性。
例如,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 为 <example 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
操作的控制器中的终结点)。 - 在需要检查代码的特定行上设置断点。
- 在调试模式下运行应用程序。
- 从要调试的前端触发操作。
调试器在指定的断点处暂停执行,使你可以检查变量、逐步检查代码并识别问题。
工作区
如果要将后端连接到示例工作负载项目,项必须属于与容量关联的工作区。 默认情况下,“我的工作区”工作区不与容量关联。 否则,你可能会收到以下屏幕截图中显示的错误:
切换到命名工作区。 保留默认的工作区名称“我的工作区”。
在正确的工作区中,加载示例工作负载并继续执行测试:
参与
我们欢迎大家对这个项目做出的贡献。 如果发现任何问题或想要添加新功能,请遵照以下步骤操作:
- 创建存储库分支。
- 为你的功能或缺陷修复创建新分支。
- 进行更改,然后提交这些更改。
- 将你的更改推送到分支存储库。
- 创建一个拉取请求,其中包含更改的明确说明。