前言
Fabric用户数据函数其实是一个可以在Fabric上托管和运行的自定义API服务,与Azure Functions有点类似。
借助Fabric用户数据函数,可以轻松的根据自身需求来定制和创建API,并可以和Fabric或微软的其他服务集成,这种将整个生态打通所带来的体验是非常强大的。
目前Fabric用户数据函数还处于公共预览阶段,想要使用的话,首先需要有Fabric容量,并且需要在租户设置里打开对应选项,如下图所示:
如果没有Fabric容量的话,也可以申请Fabric试用容量,但需要注意容量的区域位置,有些位置目前还不支持用户数据函数功能,如下图所示:
具体的位置限制可以参考官方文档:Fabric 地区可用性 – Microsoft Fabric | Microsoft Learn
用户数据函数服务的创建
首先,在工作区中新建一个用户数据函数,如下图所示:
然后,输入用户数据函数的名称:
然后点击刚创建的用户数据函数,进入后,点击创建新函数的卡片,如下图所示:
然后系统会自动创建一个示例函数,如下图所示:
到这里为止,用户数据函数的服务就算是创建好了,剩下的就是根据需求,设计并编写各个函数了。
另外,因为目前用户数据函数功能还处在公共预览阶段,可能存在Bug,导致界面上看到的文本内容都是携带前缀的,虽然看起来怪怪的但并不影响使用,忽略即可。
API函数的编写
前面创建的用户数据函数只是一个服务,里面编写的函数才是真正可以使用API方式进行调用的函数,在一个用户数据函数服务里可以包含多个可以调用的函数。为了保持区分,下面出现的”用户数据函数“指代的是服务本身,而它里面可以用API进行调用的多个函数,则称之为API函数。
用户数据函数服务目前只支持Python语言,使用的是Python 3.11。在里面编写API函数时,需要遵守一些规则,否则无法被识别成可运行的API函数。规则具体如下:
-
需要导入
fabric.functions
库,提供采用 Python 创建API函数时所需的代码。 -
需要使用
fn.UserDataFunctions()
来提供执行上下文,需要在所有API函数前进行定义,一般添加在开头部分。 -
每个API函数都需要使用
@udf.function()
装饰器进行标识,没有该装饰器的函数都无法从外部进行调用,只能在包含装饰器的函数的内部进行调用。 -
每个API函数的参数以及函数的返回结果都需要声明数据类型。
下面是满足以上规则的API函数的示例:
import fabric.functions as fn
udf = fn.UserDataFunctions()
@udf.function()
def welcome(name: str) -> str:
return f"Hi {name}, Welcome to Fabric Functions!"
@udf.function()
def addnumber(a:float,b:float) -> float:
return a+b
在API函数编辑完后,需要点击上方菜单栏的发布选项进行发布,发布后所有可以使用API方式从外部进行调用的API函数都会显示在左边的函数浏览窗口中。如下图所示:
在发布前,新添加的API函数都是不可以调用的,只有发布后并出现在左侧的函数浏览窗口中的API函数才可以从外部进行调用。
另外,在上方的编辑菜单栏中有一个“重置代码”的按钮,如下图所示:
如果对当前编写的代码不满意或出错,想要重置回最近一次发布时的代码,那么就可以点击该按钮进行重置。
最后,如果想要删除某个API函数,那么只需要把该函数的代码删掉,然后重新发布即可。
数据源连接
如果需要在函数中操作外部数据源,比如连接到数据库并对表进行增删改查等,那么这种连接到数据源的操作只要是Python支持的方式都可以连接,Fabric并没有做限制。
比如下面就是一个连接到SQL Server数据库并进行查询的API函数示例:
# 连接mssql数据库并进行查询
import fabric.functions as fn
import pymssql
udf = fn.UserDataFunctions()
@udf.function()
def query_mssql(host:str, port:int, user:str, password:str, database:str, query:str) -> list:
"""
连接到MSSQL数据库并执行查询。
:param host: 数据库主机地址
:param port: 数据库主机端口
:param user: 数据库用户名
:param password: 数据库密码
:param database: 数据库名称
:param query: 要执行的SQL查询语句
:return: 查询结果列表
"""
try:
# 连接到数据库
conn = pymssql.connect(host=host, port=port, user=user, password=password, database=database)
cursor = conn.cursor()
# 执行查询
cursor.execute(query)
# 获取所有结果
results = cursor.fetchall()
# 关闭连接
cursor.close()
conn.close()
return results
except pymssql.Error as e:
return [f'Error connecting to MSSQL database: {e}']
但如果是Fabric上面的数据源,则用户数据函数服务提供了便捷的连接方式,目前支持的Fabric数据源有:
- Fabric SQL Database
- Fabric Warehouse
- Fabric Lakehouse
- Fabric mirrored databases
只需要点击上面菜单栏的“管理连接”按钮,然后添加数据连接,选择对应的Fabric数据源即可,如下图所示:
然后,需要记住刚创建的连接的别名,在API函数中进行连接时需要用到。
然后在API函数中连接Fabric数据源时,只需要给API函数再添加一个装饰器即可,比如下面所示代码的第一行:
@udf.connection(argName="sqlDB",alias="<alias for sql database>")
@udf.function()
def read_from_sql_db(sqlDB: fn.FabricSqlConnection)-> list:
# Replace with the query you want to run
query = "SELECT * FROM (VALUES ('John Smith', 31), ('Kayla Jones', 33)) AS Employee(EmpName, DepID);"
# Establish a connection to the SQL database
connection = sqlDB.connect()
cursor = connection.cursor()
query.capitalize()
# Execute the query
cursor.execute(query)
# Fetch all results
results = []
for row in cursor.fetchall():
results.append(row)
# Close the connection
cursor.close()
connection.close()
return results
其中,argName参数指的是在API函数中引用该Fabric数据源连接的变量名,alias参数则是前面创建的Fabric数据源连接的别名,在管理连接界面中可以看到。
Python第三方库管理
在编写API函数时,可以使用import
命令导入第三方库,但需要提前在用户数据函数服务里安装这些第三方库。
第三方库的安装非常简单,在上面菜单栏中点击“库管理”按钮,然后添加所需的库即可,并且可以指定版本,如下图所示:
其中,fabric_user_data_functions
库是默认安装的,且无法删除,这是API函数可以正常工作的前提。
另外,还可以安装自定义的专用库,在“Private Libraries”界面进行上传即可,如下图所示:
对于自定义的专用库,有一些限制,具体如下:
.whl
文件大小必须小于 30MB。- 该文件
.whl
必须与 OS 无关。 如果该文件特定于操作系统,例如numpy-2.2.2-cp311-cp311-linux_armv6l.whl
,它将无法上传。
API函数的调用
API函数可以在多个地方进行调用,具体如下:
1、用户数据函数服务
通过用户数据函数服务中的左侧函数浏览窗口,鼠标放在函数名称中,然后点击弹出的三角运行按钮即可,如下图所示:
上面这种方法一般是在测试函数作用时使用。
2、Fabric数据管道
在Fabric数据管道中,可以使用”函数“活动组件来调用API函数,如下图所示:
3、Fabric笔记本
在Fabric笔记本中,可以使用notebookutils.udf
工具来调用API函数,如下面代码所示:
# Python
# Get functions
myFunctions = notebookutils.udf.getFunctions('UDFItemName') # Get functions from UDF within the same workspace
myFunctions = notebookutils.udf.getFunctions('UDFItemName', 'workspaceId') # Get functions from UDF across different workspace
# Invoke the function
myFunctions.functionName('value1', 'value2')
myFunctions.functionName(parameter1='value1', parameter2='value2'...) # Another way to invoke the function
4、函数的API接口
首先,可以在API函数的属性中找到该函数所对应的API接口,如下图所示:
然后使用POST请求进行调用,如下面代码所示:
from azure.identity import InteractiveBrowserCredential
import requests
import json
# Acquire a token
# DO NOT USE IN PRODUCTION.
# Below code to acquire token is for development purpose only to test the UDF endpoint
# For production, always register an application in a Microsoft Entra ID tenant and use the appropriate client_id and scopes
# https://go.microsoft.com/fwlink/?linkid=2305210
app = InteractiveBrowserCredential()
scp = 'https://analysis.windows.net/powerbi/api/user_impersonation'
result = app.get_token(scp)
if not result.token:
print('Error:', "Could not get access token")
# Prepare headers
headers = {
'Authorization': f'Bearer {result.token}',
'Content-Type': 'application/json'
}
endpoint = 'https://api.fabric.microsoft.com/v1/workspaces/0e57f7f0-3dbe-462b-b711-dd8a9fcacbd5/userDataFunctions/701bd2bb-e73d-481c-91df-b2fb3a0d369c/functions/addnumber/invoke'
# UPDATE HERE: Update the request body based on the inputs to your function
request_body = {
"a": 1.0,
"b": 1.0
}
# Invoke fabric udf function from this app
response = requests.post(endpoint, json=request_body, headers=headers)
# Check the response status code
if response.status_code == 200:
print('Function invoked successfully!')
data=response.json()
print(json.dumps(data, indent=4))
else:
print('Failed to invoke function. Status code:', response.status_code)
print('Response:', response.text)
5、PowerBI跨事务任务流
可以在PowerBI报表中使用跨事务任务流来调用API函数,也就是添加按钮并设置操作为数据函数,如下图所示:
其中,可以使用度量值或按钮切片器、列表切片器、文本切片器来作为参数的输入,并且输入的值不能为空,且数据类型需要与API函数中定义的参数类型一致,否则无法识别添加。当所有参数都有了输入值后,按钮就可以被激活,此时点击该按钮就可以调用API函数。
如果参数的输入是使用的按钮切片器、列表切片器或文本切片器,那么需要注意切片器对报表中的其他图表的影响,在有必要的情况下可以使用编辑交互功能来屏蔽这些切片器的筛选。
另外,如果想让其他用户也能够点击按钮来调用API函数,那么需要提前给其他用户授权API函数的执行权限,否则其他无权限的用户无法调用API函数。
API函数的日志和输出诊断信息
如果想要查看API函数的调用日志,那么可以在用户数据函数服务中的左侧函数浏览窗口中,将鼠标放在函数名称中,然后点击弹出的三个点,并选择”查看历史日志“选项即可,如下图所示:
然后点击第一列的日期,就可以进入查看对应调用的详细日志信息,如下图所示:
上面详细日志信息中的内容是在API函数的代码中输出的,可以使用以下命令进行输出:
# 需要导入logging库
import logging
logger.info('This is a INFO message')
logger.warning('This is a WARNING message')
logger.error('This is an ERROR message')
logger.critical('This is a CRITICAL message')
通过上面的命令,就可以在API函数的执行中输出关键的诊断信息,以便定位问题点,这与print命令的使用基本一致。
另外要注意的是,在API函数中的print命令输出的内容是无法被记录和查看的,因此一般是使用上面的日志命令来代替print命令。
API函数的权限与共享
API函数的调用是需要执行权限的,如果想让其他用户也可以调用API函数,那么需要给其他用户授予权限。
可以在工作区列表中找到用户数据函数项,然后在三个点中选择管理权限,或者直接点击共享按钮,就可以打开授权界面,如下图所示:
被授权的用户默认拥有查看权限,可以查看API函数的代码。同时,还可以授予用户其他权限,包括:重新共享、写入权限、执行权限。
其中,重新共享与执行权限应该都比较好理解,就不过多赘述。下面重点介绍下写入权限,即授权界面中的“编辑用户数据函数属性”选项。
经过实际测试发现,有了写入权限也并不能直接编辑API函数的代码,只能是编辑用户数据函数设置里的说明以及提升认可,看起来是很鸡肋的一个权限。但有意思的地方在于,有了写入权限后还可以接管用户数据函数的所有权。
目前只有用户数据函数的所有者可以编辑API函数的代码,因此当有了写入权限后就可以通过接管成为所有者来得到API函数的代码编辑权限。
但更有意思的地方在于,即使通过写入权限接管成为了所有者,如果原来的所有者没有给你授权重新共享与执行权限的话,你即使成为了所有者,也依然不能重新共享和调用函数。
你可能会想着,既然我已经是所有者了,那我可以自己给自己添加重新共享和执行权限。但答案是不行,目前能够编辑用户数据函数权限的角色只有工作区参与者及以上的角色,而工作区参与者及以上的角色也默认拥有用户数据函数的查看、写入、重新共享和执行等所有权限。
另外,只有具有工作区角色的用户才可以找到用户数据函数的项,否则即使拥有了查看、写入、重新共享、执行等所有权限,也是找不到用户数据函数的项的,包括在浏览界面和OneLake界面都是看不到的。
也就是说,如果一个人拥有用户数据函数的写入权限,那他是可以通过接管成为所有者来获得API函数的代码编辑权限的。但如果他不具有工作区的任意角色,那么即使他可以接管成为所有者,那他也接管不了,因为前提是他要能找到这个用户数据函数才能进行设置!
综上所述,用户数据函数的权限管理的最佳实践就是,给普通用户开放查看和执行权限就足够了,即下图所示的勾选内容:
总结
Fabric用户数据函数可以让用户轻松的创建各种API函数,并无缝集成了Fabric的多种服务,能够让用户打造强大的应用解决方案。
而且借助Python和API接口这两个胶水,还可以将Fabric和微软的其他软件生态、以及各种第三方服务进行打通,比如在API函数里可以通过HTTP请求来触发PowerPlatform中的PowerAutomate Flow,从而借助PowerAutomate来调用Teams、SharePoint、Outlook等各种Microsoft365的应用和服务。
而且用户数据函数还可以通过跨事务任务流来与PowerBI报表进行集成,从而让PowerBI报表具有了调用API接口的能力,让其可以通过API接口来调用各种软件生态的服务,从而实现更多的可能,为用户带来更大的价值。
这种软件生态互相打通所带来的端到端体验是无比强大的,不得不说微软的软件生态做得真好。