链接实体单元格值集成来自外部数据源的数据类型,并且可以将数据显示为实体卡,如常规实体值。 借助它们,你可以缩放数据类型以表示大型数据集,而无需将所有数据下载到工作簿中。 通过 Excel UI 提供的 “股票”和“地理”数据域 提供链接的实体单元格值。 本文介绍如何在 Excel 外接程序中创建自己的数据提供程序,以便为最终用户提供自定义值。
链接的实体单元格值链接到外部数据源。 与常规实体值一样,它们具有以下优势。
- 链接实体单元格值可以嵌套,在引用之前不会检索嵌套链接实体单元格值;由用户或工作表。 这有助于减小文件大小并提高工作簿性能。
- Excel 使用缓存允许不同的单元格无缝引用相同的链接实体单元格值。 这还可以提高工作簿性能。
本文扩展了以下文章中所述的信息。 建议在了解如何生成自己的链接实体单元格值之前阅读以下文章。
主要概念
链接实体单元格值为用户提供从外部数据源链接的数据。 用户可以将它们作为实体值查看卡。
与常规实体值一样,可以在公式中引用链接的实体单元格值。
定义
以下定义是了解如何实现自己的链接实体单元格值的基础。
- 链接实体数据域 – 链接实体数据域描述实体所属的整体类别。 一些示例包括员工、组织或汽车。
- 链接实体单元格值 - 从数据域创建的实例。 例如,名为 Joe 的人员的员工值。 它可以显示为实体值卡。
- 数据提供程序 - Excel 会将数据提供程序识别为一个或多个已注册链接实体数据域的数据源。
- 链接实体加载服务函数 – 每个链接实体数据域都定义一个加载服务函数,充当该域的数据源。 链接实体加载服务函数处理来自 Excel 的请求,以获取工作簿的链接实体单元格值。 将其实现为 TypeScript 或 JavaScript 自定义函数。
加载项如何提供链接实体单元格值
此图显示了加载加载项,然后将新的链接实体单元格值插入单元格时发生的步骤。 以下说明说明了该过程的每个步骤发生的情况。
- Excel 加载加载项,加载项将注册它支持的所有链接实体数据域。 每个注册都包含链接实体加载服务函数的 ID。 Excel 稍后会调用此 ID,以从链接实体数据域请求链接实体单元格值的属性值。 在此示例中,注册了一个名为 Products 的数据域。
- Excel 跟踪链接实体数据域集合中每个已注册的链接实体数据域。 这样,当链接实体单元格值需要数据时,Excel 就可以调用链接实体加载服务函数。
- 加载项在工作表中插入新的链接实体单元格值。 在此示例中,为产品 Chai 创建新的链接实体单元格值。 这通常发生在用户选择加载项上的按钮时,该按钮会导致创建一个或多个链接的实体单元格值。 创建新的链接实体单元格值时,它们仅包含单元格中显示的初始文本字符串。 Excel 调用链接的实体加载服务函数以获取剩余的属性值。 加载项还可以从自定义函数创建链接实体单元格值。
- Excel 调用在步骤 1 中注册的链接实体加载服务函数。 每次创建新的链接实体单元格值时或发生数据刷新时,都会发生这种情况。 Excel 调用链接的实体加载服务函数以获取所有属性值。
- 链接实体加载服务函数为 Excel 请求的链接实体 ID (Excel.LinkedEntityId) 返回最新的链接实体单元格值 (Excel.LinkedEntityCellValue) 。 通常,链接实体加载服务函数查询外部数据源以获取值并创建链接实体单元格值。 在此示例中,返回 产品 ID、 类别、 数量和 价格 的值。
注意
如果 Excel 需要多个链接实体单元格值,则链接实体 ID 将作为批传递到链接实体加载服务函数。 然后,链接的实体加载服务将返回所有值的批处理结果。
以下部分提供有关本文前面定义的术语的其他详细信息。
数据提供程序
您的外接程序是数据提供程序,Excel 将其识别为一个或多个已注册数据域的数据源。 外接程序公开一个或多个数据提供程序函数,用于返回链接实体单元格值的数据。 数据提供程序由文本字符串(如 Contoso)或加载项名称标识。 该名称在外接程序中必须唯一。
链接实体数据域
外接程序 (数据提供程序) 注册一个或多个数据域。 数据域将实体描述为 Excel。 例如,数据提供程序可以提供产品和类别数据域。 域必须注册到 Excel,以便它可以使用这些域来检索和显示链接的实体单元格值并执行计算。
数据域向 Excel 描述以下属性:
- 与之关联的数据提供程序的名称。
- 用于唯一标识它的域 ID,例如 产品。
- 用户的显示名称,例如 Products。
- 当 Excel 需要链接实体单元格值时要调用的链接实体加载服务函数。
- 描述刷新频率的指定刷新模式和间隔。
链接实体数据域的一个示例是 Excel 中的 Geography 数据域,它为城市提供链接实体单元格值。
链接实体单元格值
链接实体单元格值是从数据域创建的实例。 例如,Geography 数据域中的 Seattle 值。 它显示实体值卡类似于常规实体单元格值。
由于链接的实体单元格值链接到数据域,因此可以刷新它们。 此外,除非用户请求嵌套链接实体单元格值 ((例如查看实体卡) ),否则不会检索这些值。 嵌套实体单元格值不会随工作表一起保存,除非从工作表 (引用它们,例如公式) 。 这可以减少文件大小并提高性能。
链接实体加载服务函数
每个数据域都需要 Excel 在需要链接实体单元格值时可以调用的函数。 外接程序将服务作为用 @linkedEntityLoadService 标记的 JavaScript 或 TypeScript 函数提供。 为了获得最佳性能,建议只创建一个加载服务函数。 Excel 将链接实体单元格值的所有请求作为批处理发送到加载服务函数。
使用数据域创建数据提供程序
本文的以下部分介绍如何编写 TypeScript 代码来实现作为 Contoso 的数据提供程序的 Excel 加载项。 它提供两个名为 “产品 ”和“ 类别”的数据域。
注册数据域
让我们看一下注册名为“产品和类别”的新域的代码。 数据提供程序名称为 Contoso。 加载加载项时,它首先向 Excel 注册数据域。
使用 Excel.LinkedEntityDataDomainCreateOptions 类型描述所需的选项,包括将哪个函数用作链接实体加载服务。 然后将域添加到 Workbook.linkedEntityDataDomains 集合。 建议在 初始化 Office 外接程序时注册域。 以下代码演示如何注册 “产品”、“ 类别”和“ 供应商” 数据域。
Office.onReady(async () => {
await Excel.run(async (context) => {
const productsDomain: Excel.LinkedEntityDataDomainCreateOptions = {
dataProvider: "Contoso",
id: "products",
name: "Products",
// ID of the custom function that is called on demand by Excel to resolve or refresh linked entity cell values of this data ___domain.
loadFunctionId: "CONTOSOLOADSERVICE",
// periodicRefreshInterval is only required when supportedRefreshModes contains "Periodic".
periodicRefreshInterval: 300,
// Manual refresh mode is always supported, even if unspecified.
supportedRefreshModes: [
Excel.LinkedEntityDataDomainRefreshMode.periodic,
Excel.LinkedEntityDataDomainRefreshMode.onLoad
]
};
const categoriesDomain: Excel.LinkedEntityDataDomainCreateOptions = {
dataProvider: "Contoso",
id: "categories",
name: "Categories",
loadFunctionId: "CONTOSOLOADSERVICE",
periodicRefreshInterval: 300,
supportedRefreshModes: [
Excel.LinkedEntityDataDomainRefreshMode.periodic,
Excel.LinkedEntityDataDomainRefreshMode.onLoad
]
};
const suppliersDomain: Excel.LinkedEntityDataDomainCreateOptions = {
dataProvider: "Contoso",
id: "suppliers",
name: "Suppliers",
loadFunctionId: "CONTOSOLOADSERVICE"
};
// Register the data domains by adding them to the collection.
context.workbook.linkedEntityDataDomains.add(productsDomain);
context.workbook.linkedEntityDataDomains.add(categoriesDomain);
context.workbook.linkedEntityDataDomains.add(suppliersDomain);
await context.sync();
});
});
插入链接的实体单元格值
可通过两种方法将链接的实体单元格值插入工作表上的单元格中。
- 在功能区上创建命令按钮或任务窗格中的按钮。 当用户选择按钮时,代码将插入链接的实体单元格值。
- 创建返回链接实体单元格值的自定义函数。
下面的代码示例演示如何在所选单元格中插入新的链接实体单元格值。 可以从功能区上的命令按钮或任务窗格中的按钮调用此代码。 有关以下代码的说明:
- 必须为返回的任何链接实体单元格值指定
serviceId
268436224
的 。 这会通知 Excel 链接的实体单元格值与 Excel 加载项相关联。 - 必须指定
culture
。 Excel 会将其传递给链接的实体加载服务函数,以便在工作簿在不同的区域性中打开时,可以保留原始区域性。 - 属性
text
在更新链接实体数据值时,在单元格中向用户显示。 这可以防止用户在更新完成时看到空白单元格。
async function insertProduct() {
await Excel.run(async (context) => {
const productLinkedEntity: Excel.LinkedEntityCellValue = {
type: Excel.CellValueType.linkedEntity,
id: {
entityId: "P1", // Don't use exclamation marks in this value.
domainId: "products", // Don't use exclamation marks in this value.
serviceId: 268436224,
culture: "en-US",
},
text: "Chai",
};
context.workbook.getActiveCell().valuesAsJson = [[productLinkedEntity]];
await context.sync();
});
}
注意
不要在 entityID
或 domainId
值中使用感叹号。
下面的代码示例演示如何使用自定义函数插入链接的实体单元格值。 用户可以通过输入 =CONTOSO.GETPRODUCTBYID("productid")
任何单元格来获取链接实体单元格值。 上一个代码示例的说明也适用于此示例。
/**
* Custom function that shows how to insert a `LinkedEntityCellValue`.
* @customfunction
* @param {string} productID Unique ID of the product.
* @return {any} `LinkedEntityCellValue` for the requested product, if found.
*/
function getProductById(productID: string): any {
const product = getProduct(productID);
if (product === null) {
throw new CustomFunctions.Error(CustomFunctions.ErrorCode.notAvailable, "Invalid productID");
}
const productLinkedEntity: Excel.LinkedEntityCellValue = {
type: Excel.CellValueType.linkedEntity,
id: {
entityId: product.productID,
domainId: "products",
serviceId: 268436224,
culture: "en-US",
},
text: product.productName
};
return productLinkedEntity;
}
实现链接实体加载服务函数
当任何链接实体单元格值需要属性值时,外接程序必须提供链接实体加载服务函数来处理来自 Excel 的请求。 函数使用 @linkedEntityLoadService
JSDoc 标记进行标识。
下面的代码示例演示如何创建一个函数来处理来自 Excel 的产品和类别数据域的数据请求。 有关以下代码的说明:
- 它使用帮助程序函数创建链接的实体单元格值。 稍后会显示该代码。
- 如果发生错误,则会
CustomFunctions.ErrorCode.notAvailable
引发错误。 这会在用户看到的单元格中显示#CONNECT!
。
// Linked entity data ___domain constants
const productsDomainId = "products";
const categoriesDomainId = "categories";
const suppliersDomainId = "suppliers";
// Linked entity cell value constants
const addinDomainServiceId = 268436224;
const defaultCulture = "en-US";
/**
* Custom function which acts as the "service" or the data provider for a `LinkedEntityDataDomain`, that is
* called on demand by Excel to resolve/refresh `LinkedEntityCellValue`s of that `LinkedEntityDataDomain`.
* @customfunction
* @linkedEntityLoadService
* @param {any} request Request to resolve/refresh `LinkedEntityCellValue` objects.
* @return {any} Resolved/Refreshed `LinkedEntityCellValue` objects that were requested in the passed-in request.
*/
function contosoLoadService(request: any): any {
const notAvailableError = new CustomFunctions.Error(CustomFunctions.ErrorCode.notAvailable);
console.log(`Fetching linked entities from request: ${request} ...`);
try {
// Parse the request that was passed-in by Excel.
const parsedRequest: Excel.LinkedEntityLoadServiceRequest = JSON.parse(request);
// Initialize result to populate and return to Excel.
const result: Excel.LinkedEntityLoadServiceResult = { entities: [] };
// Identify the domainId of the request and call the corresponding function to create
// linked entity cell values for that linked entity data ___domain.
for (const { entityId } of parsedRequest.entities) {
var linkedEntityResult = null;
switch (parsedRequest.domainId) {
case productsDomainId: {
linkedEntityResult = makeProductLinkedEntity(entityId);
break;
}
case categoriesDomainId: {
linkedEntityResult = makeCategoryLinkedEntity(entityId);
break;
}
case suppliersDomainId: {
linkedEntityResult = makeSupplierLinkedEntity(entityId);
break;
}
default:
throw notAvailableError;
}
if (!linkedEntityResult) {
// Throw an error to signify to Excel that resolution/refresh of the requested linkedEntityId failed.
throw notAvailableError;
}
result.entities.push(linkedEntityResult);
}
return result;
} catch (error) {
console.error(error);
throw notAvailableError;
}
}
以下代码示例演示帮助程序函数,用于创建产品链接实体单元格值。 此函数由前面的代码 contosoLoadService
调用,用于为特定产品 ID 创建链接实体。 有关以下代码的说明:
- 它使用与上
insertProduct
一个针对type
、id
和text
属性的示例相同的设置。 - 它包括特定于 Products 数据域的其他属性,例如
Product Name
和Unit Price
。 - 它为产品的类别创建延迟嵌套链接实体。 在需要这些属性之前,不会请求该类别的属性。
/** Helper function to create a linked entity from product properties. */
function makeProductLinkedEntity(productID: string): any {
// Search the product data in the data source for a matching product ID.
const product = getProduct(productID);
if (product === null) {
// Return null if no matching product is found.
return null;
}
const productLinkedEntity: Excel.LinkedEntityCellValue = {
type: "LinkedEntity",
text: product.productName,
id: {
entityId: product.productID,
domainId: productsDomainId,
serviceId: addinDomainServiceId,
culture: defaultCulture
},
properties: {
"Product ID": {
type: "String",
basicValue: product.productID
},
"Product Name": {
type: "String",
basicValue: product.productName
},
"Quantity Per Unit": {
type: "String",
basicValue: product.quantityPerUnit
},
// Add Unit Price as a formatted number.
"Unit Price": {
type: "FormattedNumber",
basicValue: product.unitPrice,
numberFormat: "$* #,##0.00"
},
Discontinued: {
type: "Boolean",
basicValue: product.discontinued
}
},
layouts: {
compact: {
icon: "ShoppingBag"
},
card: {
title: { property: "Product Name" },
sections: [
{
layout: "List",
properties: ["Product ID"]
},
{
layout: "List",
title: "Quantity and price",
collapsible: true,
collapsed: false,
properties: ["Quantity Per Unit", "Unit Price"]
},
{
layout: "List",
title: "Additional information",
collapsed: true,
properties: ["Discontinued"]
}
]
}
}
};
// Add image property to the linked entity and then add it to the card layout.
if (product.productImage) {
productLinkedEntity.properties["Image"] = {
type: "WebImage",
address: product.productImage
};
productLinkedEntity.layouts.card.mainImage = { property: "Image" };
}
// Add a deferred nested linked entity for the product category.
const category = getCategory(product.categoryID.toString());
if (category) {
productLinkedEntity.properties["Category"] = {
type: "LinkedEntity",
text: category.categoryName,
id: {
entityId: category.categoryID.toString(),
domainId: categoriesDomainId,
serviceId: addinDomainServiceId,
culture: defaultCulture
}
};
// Add nested product category to the card layout.
productLinkedEntity.layouts.card.sections[0].properties.push("Category");
}
// Add a deferred nested linked entity for the supplier.
const supplier = getSupplier(product.supplierID.toString());
if (supplier) {
productLinkedEntity.properties["Supplier"] = {
type: "LinkedEntity",
text: supplier.companyName,
id: {
entityId: supplier.supplierID.toString(),
domainId: suppliersDomainId,
serviceId: addinDomainServiceId,
culture: defaultCulture
}
};
// Add nested product supplier to the card layout.
productLinkedEntity.layouts.card.sections[2].properties.push("Supplier");
}
return productLinkedEntity;
}
下面的代码示例演示用于创建类别链接实体单元格值的帮助程序函数。 此函数由前面的代码 contosoLoadService
调用,用于为特定类别 ID 创建链接实体。
/** Helper function to create a linked entity from category properties. */
function makeCategoryLinkedEntity(categoryID: string): any {
// Search the sample JSON category data for a matching category ID.
const category = getCategory(categoryID);
if (category === null) {
// Return null if no matching category is found.
return null;
}
const categoryLinkedEntity: Excel.LinkedEntityCellValue = {
type: "LinkedEntity",
text: category.categoryName,
id: {
entityId: category.categoryID,
domainId: categoriesDomainId,
serviceId: addinDomainServiceId,
culture: defaultCulture
},
properties: {
"Category ID": {
type: "String",
basicValue: category.categoryID,
propertyMetadata: {
// Exclude the category ID property from the card view and auto-complete.
excludeFrom: {
cardView: true,
autoComplete: true
}
}
},
"Category Name": {
type: "String",
basicValue: category.categoryName
},
Description: {
type: "String",
basicValue: category.description
}
},
layouts: {
compact: {
icon: "Branch"
}
}
};
return categoryLinkedEntity;
}
以下代码示例演示用于创建供应商链接实体单元格值的帮助程序函数。 此函数由前面的代码 contosoLoadService
调用,用于为特定供应商 ID 创建链接实体。
/** Helper function to create linked entity from supplier properties. */
function makeSupplierLinkedEntity(supplierID: string): any {
// Search the sample JSON category data for a matching supplier ID.
const supplier = getSupplier(supplierID);
if (supplier === null) {
// Return null if no matching supplier is found.
return null;
}
const supplierLinkedEntity: Excel.LinkedEntityCellValue = {
type: "LinkedEntity",
text: supplier.companyName,
id: {
entityId: supplier.supplierID,
domainId: suppliersDomainId,
serviceId: addinDomainServiceId,
culture: defaultCulture
},
properties: {
"Supplier ID": {
type: "String",
basicValue: supplier.supplierID
},
"Company Name": {
type: "String",
basicValue: supplier.companyName
},
"Contact Name": {
type: "String",
basicValue: supplier.contactName
},
"Contact Title": {
type: "String",
basicValue: supplier.contactTitle
}
},
cardLayout: {
title: { property: "Company Name" },
sections: [
{
layout: "List",
properties: ["Supplier ID", "Company Name", "Contact Name", "Contact Title"]
}
]
}
};
return supplierLinkedEntity;
}
以下代码示例包含可用于前面的代码示例的示例数据。
/// Sample product data.
const products = [
{
productID: "P1",
productName: "Chai",
supplierID: "S1",
categoryID: "C1",
quantityPerUnit: "10 boxes x 20 bags",
unitPrice: 18,
discontinued: false,
productImage: "https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/Masala_Chai.JPG/320px-Masala_Chai.JPG"
}
];
/// Sample product category data.
const categories = [
{
categoryID: "C1",
categoryName: "Beverages",
description: "Soft drinks, coffees, teas, beers, and ales"
}];
/// Sample product supplier data.
const suppliers = [
{
supplierID: "S1",
companyName: "Exotic Liquids",
contactName: "Ema Vargova",
contactTitle: "Purchasing Manager"
}];
数据刷新选项
注册数据域时,用户可以随时手动刷新它,例如从“数据”选项卡中选择“全部刷新”。可以为数据域指定三种刷新模式。
-
manual
- 仅当用户选择刷新时,才会刷新数据。 这是默认模式。 即使刷新模式设置为onLoad
或periodic
,用户也始终可以执行手动刷新。 -
onLoad
- 在注册数据域时刷新数据, (通常在) 加载加载项时刷新数据。 之后,数据仅由用户手动刷新。 如果要在工作簿打开时刷新数据,请将加载项配置为在打开文档时加载。 有关详细信息,请参阅 打开文档时在 Office 外接程序中运行代码。 -
periodic
- 在注册数据域时刷新数据, (通常在) 加载加载项时刷新数据。 之后,数据在指定的时间间隔后持续更新。 例如,可以指定数据域每隔 300 秒刷新一次, (这是) 的最小值。 由于刷新间隔仅以分钟为单位执行,因此始终将秒数向上舍入到最接近的分钟数。
下面的代码示例演示如何将数据域配置为在加载时刷新,然后每隔 5 分钟继续刷新一次。
const productsDomain: Excel.LinkedEntityDataDomainCreateOptions = {
dataProvider: domainDataProvider,
id: "products",
name: "Products",
// ID of the custom function that is called on demand by Excel to resolve or refresh linked entity cell values of this data ___domain.
loadFunctionId: loadFunctionId,
// periodicRefreshInterval is only required when supportedRefreshModes contains "Periodic".
periodicRefreshInterval: 300, // equivalent to 5 minutes.
// Manual refresh mode is always supported, even if unspecified.
supportedRefreshModes: [
Excel.LinkedEntityDataDomainRefreshMode.periodic,
Excel.LinkedEntityDataDomainRefreshMode.onLoad
]
};
还可以使用以下方法之一以编程方式请求对链接实体数据域进行刷新。
-
LinkedEntityDataDomain.refresh()
- 刷新链接实体数据域的所有LinkedEntityCellValue
对象。 -
LinkedEntityDataDomainCollection.refreshAll()
- 刷新集合中LinkedEntityCellValue
所有链接实体数据域的所有对象。
刷新方法请求异步进行的刷新。 若要确定刷新结果,请侦 onRefreshCompleted
听 事件。 下面的代码示例演示侦听 onRefreshCompleted
事件的示例。
await Excel.run(async (context) => {
const dataDomains = context.workbook.linkedEntityDataDomains;
dataDomains.onRefreshCompleted.add(onLinkedEntityDomainRefreshed);
await context.sync();
});
async function onLinkedEntityDomainRefreshed(eventArgs: Excel.LinkedEntityDataDomainRefreshCompletedEventArgs): Promise<any> {
console.log("Linked entity ___domain refreshed: " + eventArgs.id);
console.log("Refresh status: " + eventArgs.refreshed);
console.log("Refresh error: " + eventArgs.errors);
return null;
}
使用链接实体加载服务处理错误
当 Excel 调用加载项以获取链接实体单元格值的数据时,可能会出现错误。 如果 Excel 根本无法连接到加载项(例如未加载加载项时),Excel 会向用户显示 #CONNECT!
错误。
如果链接的实体加载服务函数遇到错误,则应引发该 notAvailableError
错误。 这会导致 Excel 向用户显示 #CONNECT!
。
以下代码演示如何处理链接实体加载服务函数中的错误。
async function contosoLoadService(request: any): Promise<any> {
const notAvailableError = new CustomFunctions.Error(CustomFunctions.ErrorCode.notAvailable);
try {
// Create and return a new linked entity cell value.
let linkedEntityResult = ...
...
if (!linkedEntityResult) {
// Throw an error to signify to Excel that resolution or refresh of the requested linkedEntityId failed.
throw notAvailableError;
}
...
} catch (error) {
console.error(error);
throw notAvailableError;
}
}
调试链接实体加载服务
可以使用 Office 加载项调试概述中的指南来调试链接实体数据类型 的大多数加载项功能。但是,链接实体加载服务函数可以在共享运行时或仅限 JavaScript 的运行时中实现, (也称为自定义函数 runtime。) 如果选择在仅限 JavaScript 的运行时中实现该函数,请使用 非共享运行时指南中的自定义函数调试 。
无论使用哪个运行时,链接实体加载服务函数都使用自定义函数体系结构。 但是,与常规自定义函数存在显著差异。
链接实体加载服务函数与自定义函数有以下区别:
- 最终用户不会在公式中使用它们。
- 它们不支持 JSDoc 标记
@streaming
或@volatile
。 如果使用这些标记,用户将看到 #CALC! 错误。
链接实体加载服务函数与自定义函数有以下相似之处:
- 它们使用 自定义函数命名和本地化。
- 它们使用相同的错误处理方法。
Excel 2019 及更早版本中的行为
如果有人在不支持链接实体单元格值的旧版 Excel 上打开包含链接实体单元格值的工作表,Excel 会将单元格值显示为错误。 这是设计的行为。 这也是每次插入或更新链接实体单元格值时将 设置为 basicType
Error
和 basicValue
设置为 #VALUE!
的原因。 这是 Excel 在旧版本上用作回退的错误。
最佳做法
- 不要在
entityID
或domainId
值中使用感叹号。 - 在初始化代码
Office.OnReady
中注册链接实体数据域,以便用户具有即时功能,例如刷新链接实体单元格值的功能。 - 发布外接程序后,请勿更改链接的实体数据域 ID。 跨相同逻辑对象的一致 ID 有助于提高性能。
- 创建新的链接实体单元格值时,始终提供
text
属性。 当 Excel 调用数据提供程序函数以获取剩余属性值时,将显示此值。 否则,在检索数据之前,用户会看到一个空白单元格。