使用保存和查询管道来 “存档”已删除记录
[原文发表地址] Using the Save and Query Pipeline to “Archive” Deleted Records
[原文发表时间]Fri, Nov 18 2011 3:35 PM
在进入微软之前,我曾经从事过卫生保健业,主要工作是为医院和医疗保险公司开发软件。在所有这些系统中,我们通过详细审核追踪(更改日志记录),授权系统和复杂的商务规则来保证患者的数据安全。一个常常特别提到的要求是我们从来不会在系统之外删除患者信息,它只是存档或"标记"为已删除。以这种方式我们可以轻松地维护历史数据,但这样数据集仅限于当前患者。
幸运的是 LightSwitch 使这非常简单,因为在保存数据之前,它允许我们进入保存管道来执行数据处理。我们也可以在它返回值之前,进入查询管道来筛选数据。在这篇博客中,我将会展示在数据库中没有删除记录的情况下,如何标注删除记录,以及如何过滤这些记录来让用户看不到它们。
进入到保存管道
保存管道在中间层 (即逻辑层) 中运行,任何时候实体都在更新、 插入或删除。你可以在其中编写商务逻辑,然后在中间层处理变更,再保存到数据存储区中。(关于保存管道的详细信息请查看在 Visual Studio LightSwitch 中从保存管道获取最多输出 。 )
假设我们有一个用于与客户工作的应用程序。但我们不想在数据库中物理上删除我们的客户记录。我们有很多方式可以实现它。一种方法是"移动"记录到另一个表中。这类似于我在这里展示的审计跟踪示例 。另一种方法是通过使用另一个字段,将此记录"标记"为已删除。例如,让我们来看一个客户表的简单数据模型和他们的订单。请注意,我在客户表上创建了一个名为"IsDeleted"的必填字段,它是布尔值类型的。我在属性窗口中已经取消选中" 默认显示",那样此字段在任何界面上都是不可见的。
为了以编程方式标记IsDeleted字段,当用户尝试删除一个客户记录时,只需选择数据设计器中的客户实体,然后在下拉菜单中选择" Write Code "按钮,并选择Customers_Deleting方法。
以下是我们需要编写的2行代码:
1: Private Sub Customers_Deleting(entity As Customer)
2: 'First discard the changes, in this case this reverts the deletion
3: entity.Details.DiscardChanges()
4: 'Next, change the IsDeleted flag to "True"
5: entity.IsDeleted = True
6: `End Sub
请注意,首先,为了将实体转换回它的未更改状态,我们必须调用 DiscardChanges。然后我们简单地将IsDeleted字段设置为 True。就是这样 !请记住,当我们像这样更改实体状态时,相应的存储管道方法仍然会运行。例如在这个案例中,Customers_Updating 现在将会触发,因为我们将实体的状态从已删除改为了未更改又改为了已修改。你可以通过使用entity.Details.EntityState属性来查看实体的状态。
进入查询管道
既然我们已成功标记已删除的客户,接下来做的是在查询中把它们过滤出来,以使它们在任何界面上不向用户显示。为了在系统中应用一个全局筛选器或者将任何客户实体排序,你可以使用一个技巧。与其创建一个自定义全局查询,并不得不记住使用在你所有的界面上的查询,相反,你只需修改LightSwitch 为你生成的内置查询。LightSwitch 在所有的实体中将会生成 xxx_All,xxx_Single 和 xxx_SingleOrDefault 查询,你可以在代码中修改它们。你可以以访问管道方法相同的方式访问它们。
在客户表中,在数据设计器的下拉菜单中选择" Write Code "按钮,拉动滚动条到Query methods,然后选择Customers_All_PreprocessQuery.
此查询是为客户表所创建的所有查询中的基础。我通常使用此方法来将记录进行有意义的排序,那样在系统中,每个实体的默认查询在每个界面上都进行了排序。在这个案例中,我们需要过滤掉IsDeleted被标记为 True的任何客户,意味着只返回IsDeleted = False的记录:
1: Private Sub Customers_All_PreprocessQuery(ByRef query As System.Linq.IQueryable(Of Customer))
2: query = From c In query
3: Where c.IsDeleted = False
4: Order By c.LastName, c.FirstName
5: End Sub
请注意,我们还有一个与客户表相关联的名为订单的表。我也可以在订单实体中做一个相似的筛选器。这取决于如何让用户导航到订单,这可能不是必要的。例如,如果你在客户详细信息界面上只显示订单,那样你就不必担心这里额外的筛选器。
1: Private Sub Orders_All_PreprocessQuery(ByRef query As System.Linq.IQueryable(Of Order))
2: query = From o In query
3: Where o.Customer.IsDeleted = False
4: Order By o.OrderDate Descending
5: End Sub
采取实际行动!
好的,是时候来测试它了,看看其是否正常运行。我在客户表中创建了一个列表和详细信息界面。当我们运行它时,我可以执行插入、 更新和删除等正常操作。在界面上也没有需要更改的,它和正常运行时一样。
你标记为 deletion… 的记录。
..然后单击保存来执行我们的 Customers_Deleting 逻辑。一旦删除一条记录然后保存,它会从我的界面列表中消失,但它在数据库中仍然存在。我们可以看到,如果我们看看实际数据库中的客户表。
利用保存和查询管道可以为你的数据提供巨大的力量。这只是多种方式的其中一种,你可以使用它们来操作数据和编写商务规则。
希望大家喜欢!