编写可行的代码只是Dynamics 365财务与运营开发的第一步。在企业实施中,定制化还必须高效、可维护、可扩展,并与D365 F&O最佳实践保持一致。
一些以绩效为导向的实践最初可能看起来需要额外的开发努力。然而,目标不应是编写尽可能短的代码。目标是在处理实际生产量时编写高效运行的代码。
本文重点关注两个经常影响代码质量和性能的领域:
- 变量声明与作用域
- 使用 X++ 语句进行数据库访问select
1. 变量声明与范围
早期的Dynamics AX开发通常遵循在方法开头声明大多数变量的模式。在现代 X++ 开发中,变量通常应声明在实际需要的位置附近。
保持变量接近它们的使用情况
避免在方法顶部声明所有变量,因为这些变量只是在很久以后才需要。
例如,代替:
public void processOrder()
{
SalesTable salesTable;
SalesLine salesLine;
AmountCur totalAmount;
boolean isValid;
// Other processing...
totalAmount = salesTable.SalesBalance;
}
倾向于在变量相关时声明:
public void processOrder()
{
// Other processing...
AmountCur totalAmount = salesTable.SalesBalance;
}
这使代码更易阅读,减少不必要的变量作用域。
限制变量的范围
变量应仅存在于其要求范围内。
较小的示波器带来了多项优势:
- 提升可读性
- 减少意外重复使用
- 更便捷的调试
- 更清晰的价值所有权
- 更好的可维护性
这适用于原语类型、扩展数据类型、枚举、类和表缓冲区。
仅在需要时声明表缓冲区
表缓冲区不应仅仅因为以后可能需要而创建。
在实际需要时声明它们,并在适当情况下重复使用现有缓冲区,而不是引入不必要的额外缓冲区。
与其为同一目的维护多个缓冲区,不如先考虑现有缓冲区是否可以安全重复使用。
环圈内变量要小心
如果在循环处理过程中反复需要相同的变量或对象,请考虑是否应在循环之外声明该变量。
例如:
AmountCur lineAmount;
while select SalesLine
where SalesLine.SalesId == salesTable.SalesId
{
lineAmount = SalesLine.LineAmount;
// Process amount
}
关键点不仅仅是“圈内与外圈”。声明应反映所需的范围,避免不必要地重复初始化对象或昂贵资源。
移除未使用的变量
未使用的声明应始终被删除。
它们增加了代码噪声,使审查更困难,并可能造成变量是否有不明显用途的混淆。
一个干净的方法应仅包含当前实现所需的变量。
2. 优化数据库访问
数据库访问是D365财务与运营中最重要的性能考量之一。
设计不佳的查询在小型开发数据库中可能完美运行,但在面对数百万条生产记录时成本会很高。
基本原理很简单:
只获取你需要的数据,尽量减少不必要的数据库往返。
只选择必填字段
避免在只需少量字段时检索整条记录。
而不是:
select firstonly custTable
where custTable.AccountNum == _accountNum;
请考虑只选择必填字段:
select firstonly AccountNum, Name
from custTable
where custTable.AccountNum == _accountNum;
这使得查询的意图更加清晰,避免了不必要的数据获取。
这种做法对于大型表和性能敏感的处理尤为重要。
避免不必要的调用find()
标准表方法既方便又具有合理用途。然而,它们不应自动成为性能敏感代码中的默认选择。find()
例如:
custTable = CustTable::find(_accountNum);
如果定制只需要一两个字段,显式查询可能会更清楚地传达需求:
select firstonly AccountNum, Name
from custTable
where custTable.AccountNum == _accountNum;
重要的考虑是理解底层方法的功能,以及它是否检索或处理了比定制实际需要的信息更多的信息。
3. 减少带连接的数据库往返次数
一个常见的性能问题是执行多个数据库查询时,即使只需通过一次联合查询即可获取所需信息。
例如,代码可能先检索销售订单,然后执行另一个查询以获取相关的客户信息。
相反,考虑将以下操作组合起来:
select firstonly SalesId, CustAccount
from salesTable
join AccountNum, Name
from custTable
where salesTable.SalesId == _salesId
&& custTable.AccountNum == salesTable.CustAccount;
一般原理是:
当数据可以自然地一起检索时,更倾向于采用一个设计良好的查询,而不是多次连续的数据库调用。
减少数据库往返在批处理、集成、报告和大批量交易中变得越来越重要。
4. 当你只需要检查存在时使用exists join
有时仅需相关表以确定是否存在匹配记录。
在这种情况下,没有理由从该表中检索字段。
与其检索不必要的相关数据,不如使用:exists join
select firstonly AccountNum
from custTable
exists join salesTable
where salesTable.CustAccount == custTable.AccountNum
&& salesTable.SalesStatus == SalesStatus::Backorder;
An 在以下情况下特别有用:exists join
- 相关表格中无需字段。
- 相关表仅作为筛选。
- 你只需要确认有匹配的记录存在。
根据数据需求选择连接类型,而不是自动使用或。joinouter join
5. 在调用表方法之前,先了解它们
表方法可以改善封装和重用,因此不应仅仅因为它们是方法就被回避。
然而,开发者应了解所调用方法的成本。
看似简单的方法内部可能:
- 执行额外的SQL查询
- 调用另一张表的方法
find() - 进行计算
- 遍历相关纪录
- 执行当前场景中不必要的业务逻辑
当方法在循环内被反复调用时,这一点尤为重要。
例如:
while select salesLine
{
value = salesLine.someMethod();
}
如果执行数据库查询,处理1万条销售线可能会产生数千个额外的数据库操作。someMethod()
在使用这些方法进行性能关键处理前,请审查其实现情况。
如果方法包含重要的业务逻辑,请适当复用。如果它仅检索可以高效包含在主查询中的简单数据,请考虑将这些信息作为原始查询的一部分检索。
6. 在昂贵手术前使用保护条款
确认应尽早进行。
在执行数据库查询前,确定输入是否已经告诉你处理应该停止。
而不是:
select firstonly salesTable
where salesTable.SalesId == _salesId;
if (!_salesId)
{
return;
}
先验证:
if (!_salesId)
{
return;
}
select firstonly SalesId
from salesTable
where salesTable.SalesId == _salesId;
这种模式通常被称为守护条款。
守护条款用于检查:
- 缺失参数
- 无效枚举值
- 空记录标识符
- 无支持状态
- 功能被禁用
- 使得进一步加工变得不必要的条件
原理很简单:
当你已经知道不需要处理时,不要查询数据库。
7. 在循环内查询时要特别小心
X++代码中最重要的部分之一是循环中的数据库访问。
请考虑:
while select salesLine
{
custTable = CustTable::find(salesLine.CustAccount);
// Processing
}
如果处理数千条记录,这种模式可能导致大量数据库调用。
在可能的情况下,通过连接、基于集合的处理、缓存或预加载数据重新设计查询。
审查代码时,务必特别关注:
while select
for
do while
while
并检查数据库查询或昂贵方法是否在其中反复执行。
8. 开发过程中应考虑性能
性能优化不应仅仅被视为开发完成后的最终行动。
在实现和代码审查过程中,开发者应持续提出:
- 我是在获取不需要的字段吗?
- 多个查询可以合并吗?
- 这个查询是在循环中运行吗?
- 这种方法会在内部执行另一个数据库查询吗?
- 能用吗?
exists join - 处理可以提前停止吗?
- 有没有基于集合的替代方案?
- 这种方法在生产规模数据中还能表现良好吗?
一个适用于100条记录的自定义,在100万条记录中表现可能截然不同。
总结
好的D365 F&O开发不仅仅是产出技术上正确的X++代码。关键在于在交易量增长时,生成的代码依然可靠高效。
关键原则包括:
- 保持可变范围小且有目的。
- 移除未使用的声明和不必要的缓冲区。
- 只取你真正需要的字段。
- 通过使用合适的连接来减少数据库往返。
- 当相关记录仅用于过滤时
,使用连接。 - 在反复使用表格方法之前,先了解它们的作用。
- 尽早通过保护条款验证条件。
- 尽量避免在循环中反复访问数据库。
- 设计和审查时,考虑生产数据量。
在D365财务与运营中,几毫秒的非必要处理在开发过程中可能显得微不足道。然而,当相同的逻辑在生产环境中运行数千甚至数百万次时,这些小的低效可能成为显著的性能问题。
因此,性能应成为设计的一部分,而非事后考虑。
转载请注明:ww12345678 的部落格 | AX Helper » D365 F&O性能优先编码最佳实践Performance-First Coding Best Practices for D365 Finance & Operations