【问题标题】:StateMachine flow in a master-detail model主从模型中的 StateMachine 流
【发布时间】:2011-11-21 04:54:15
【问题描述】:

我需要问一个复杂的场景,所以我会尝试举例说明。

考虑以下模型:

public enum States { Created, Approved, Started, Completed }

public class Request {
    States State {get; set;}
    IEnumerable<RequestLine> Lines {get; set;}
}

public class RequestLine {
    States State {get; set;}
    Request Request {get; set;}
    IEnumerable<WorkOrder> WorkOrders {get; set;}
}   

public class WorkOrder {
    States State {get; set;}
    RequestLine RequestLine {get; set;}
    IEnumerable<WorkOrderAction> Actions {get; set;}
}

public class WorkOrderAction {
    States State {get; set;}
    WorkOrder WorkOrder {get; set;}
}

所以每条记录都有一个状态,它们都是相互关联的。创建 WorkOrderAction 时,我必须更新 WorkOrder 状态,然后是 RequesTLine 状态,然后是 Request 状态。对于每个 WorkOrderAction 记录,我应该检查其他子记录状态并更新父记录。

我可以使用我不喜欢的数据库触发器来做到这一点。我在应用程序级别使用 Devexpress XAF,因此我也可以在那里编写一些逻辑。但我仍然无法决定哪种方法更好。

链接状态机有一个共同的概念吗?

【问题讨论】:

    标签: c# oracle database-design devexpress xaf


    【解决方案1】:

    通常的做法与状态机没有任何关系。也就是说,在执行数据完整性规则方面,状态机并不特殊。

    如果它必须适用于所有用户,那么在触发器中实现它是有意义的。只有由 dbms 控制的代码才能对所有用户强制执行数据完整性规则。 (并且所有用户包括睡眠不足的 DBA 和她的命令行工具。)

    【讨论】:

    • 嗨,我想我无法正确描述这个问题,除了数据完整性之外,我的问题还在于性能。当我查询状态已完成的请求记录时,我不想查询所有子记录并确定它是否已完成。当 RequestLines 更改其状态时,我需要更新请求状态。
    • @xarux:当另一个表中的行发生变化时,您想更新一个表中的一个或多个行吗?这就是触发器的用途。我无法想象你为什么需要查询中间表来做到这一点。 (除非您在 SQL dbms 中重新发明了 IMS。)
    • 当然这就是触发器的用途,但这里我说的是一个应用程序框架,即 XAF。您可以看到应用程序模型上的所有内容,但是当您引入复杂的触发器时,开发人员团队可能会发现很难跟踪更新是如何发生的。因此,我考虑的不是触发器,而是可以更轻松地修改而无需对所有内容进行硬编码的工作流。
    • 1) 更新三个表的触发器可能适合一个屏幕。 2) 框架和语言来来去去;数据库是永远的。 (我在 OLTP 系统上工作了 30 多年,该系统有超过 25 种语言编写的程序。) 3)对“易于修改的工作流程”的需求表明您的完整性约束要么是流动的,要么不是很好理解.其中任何一个都将是一个大问题。
    【解决方案2】:

    不完全确定我是否理解这个问题,但如果您要询问有关强制执行数据完整性“级联”(级联更新)的问题,那么我会考虑设置“可延迟”约束,这会将检查约束检查推迟到提交提交。然后,您将根据需要对父/子进行更新,然后提交。

    这是来自 Oracle 的关于 deferrable constraints 的链接。

    【讨论】:

    • 嗨,我的问题不是关于约束。这样想,你有一个 4 级深度模型。因此,当我创建第 4 级记录时,我必须检查它的状态,因为它会影响上层的状态。 Request 的状态不会直接改变,当它的子 RequestLine 记录全部完成后,Request 的状态就应该完成。
    • 好的,现在更清楚了。听起来您仍然需要处理数据库中的事件触发(这意味着更新父级或上层状态的触发器),或设置可延迟约束,在您的应用程序中进行更新,然后提交。为什么要一起排除触发因素? (不太喜欢 db 中复杂的触发系统,但仍然很好奇)
    • 我不排除触发因素,我只是想找到另一种方法。这种复杂的触发器很难在一个大项目中遵循。 WorkOrderAction 的 BeforeInsert 触发器将尝试更新 WorkOrder,然后是 RequestLine,然后是 Request 本身。我正在考虑一个可以处理这个问题的工作流程。
    【解决方案3】:

    我们只需要知道“其他不完整的孩子”。

    1. 我们可以枚举所有它们:foreach(parent.Children) if(child.State != Completed)。但是这会加载它们,因此会影响性能
    2. 我们可以使用 Execute(new BinaryOperator("State", Completed, NotEquals), AggregateOperand.Count) 之类的方法执行到数据库的往返。这需要对域进行重组,并且对性能的影响比以前的要小。
    3. 当每个孩子被保存/删除/移除/添加时,我们可以更新父母的“IncompletedChildren”成员(整数)。始终加载 Parent 对象,因此我们可以在“IncompletedChildren == 0”时确定父对象已完成,将其标记为已完成并递归强制完成其父对象。没有额外的流量负载(一个“整数”不是“加载”恕我直言)。 然而,这种方法会为每个“IncompletedChildren”更新强制“OptimisticLoading”增量——这使得这种方法在某些情况下无法使用。

    我会推荐第二种方法。

    PS:在我们的项目/任务系统中,我们引入了带有表达式“Items[State Completed].Count 的计算字段。但是这在现实中行不通。大多数时候,至少在我们的系统中,Completed 状态没有” t意味着一切都完成了:)

    【讨论】:

    • Apostolis 你完全理解了我的场景,thanx :) 我将尝试第二种方法,看看它是否以可接受的性能执行。 PS:扩展框架做得很好,还没有完全尝试过,但我想在这个项目中使用它:)
    猜你喜欢
    • 1970-01-01
    • 2018-07-04
    • 1970-01-01
    • 2020-12-14
    • 2020-03-30
    • 1970-01-01
    • 1970-01-01
    • 2016-07-24
    • 1970-01-01
    相关资源
    最近更新 更多