最新消息:ww12345678 的部落格重装上线,希望大家继续支持。

D365 F&O性能优先编码最佳实践Performance-First Coding Best Practices for D365 Finance & Operations

编写可行的代码只是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++代码。关键在于在交易量增长时,生成的代码依然可靠高效。

关键原则包括:

  1. 保持可变范围小且有目的。
  2. 移除未使用的声明和不必要的缓冲区。
  3. 只取你真正需要的字段。
  4. 通过使用合适的连接来减少数据库往返。
  5. 当相关记录仅用于过滤时,使用连接
  6. 在反复使用表格方法之前,先了解它们的作用。
  7. 尽早通过保护条款验证条件。
  8. 尽量避免在循环中反复访问数据库。
  9. 设计和审查时,考虑生产数据量。

在D365财务与运营中,几毫秒的非必要处理在开发过程中可能显得微不足道。然而,当相同的逻辑在生产环境中运行数千甚至数百万次时,这些小的低效可能成为显著的性能问题。

因此,性能应成为设计的一部分,而非事后考虑。

转载请注明:ww12345678 的部落格 | AX Helper » D365 F&O性能优先编码最佳实践Performance-First Coding Best Practices for D365 Finance & Operations

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址