This topic describes how to implement a business class, so that one of its properties is calculated based on a property(ies) of the objects contained in the child object collection.

本主题介绍如何实现 Business 类,以便基于子对象集合中包含的对象的属性计算其属性之一。

Tip 提示
A complete sample project is available in the DevExpress Code Examples database at http://www.devexpress.com/example=E305
完整的示例项目可在 DevExpress 代码示例数据库中找到,http://www.devexpress.com/example=E305

.

Initial Class Implementation

A Product class has a collection of Order objects. The Product and Order classes are associated by the One-to-Many relationship, which means that a Product object may be associated with several Order objects. The collection of Order objects is aggregated. Order objects are created, belonging to one of the Product objects. When the master object is removed, all the objects in its aggregated collection are removed as well.

The following snippet illustrates the Product class implementation.

初始类实现

产品类具有 Order 对象的集合。产品和订单类由"一对多"关系关联,这意味着产品对象可能与多个 Order 对象关联。将聚合 Order 对象的集合。将创建属于"产品"对象之一的顺序对象。删除主对象时,其聚合集合中的所有对象也都将被删除。

以下代码段演示了产品类实现。

[DefaultClassOptions]
public class Product : BaseObject {
public Product(Session session) : base(session) { }
private string fName;
public string Name {
get { return fName; }
set { SetPropertyValue(nameof(Name), ref fName, value); }
}
[Association("Product-Orders"), Aggregated]
public XPCollection<Order> Orders {
get { return GetCollection<Order>(nameof(Orders)); }
}
}

The following snippet illustrates the Order class implementation.

以下代码段演示了 Order 类实现。

[DefaultClassOptions]
public class Order : BaseObject {
public Order(Session session) : base(session) { }
private string fDescription;
public string Description {
get { return fDescription; }
set { SetPropertyValue(nameof(Description), ref fDescription, value); }
}
private decimal fTotal;
public decimal Total {
get { return fTotal; }
set { SetPropertyValue(nameof(Total), ref fTotal, value); }
}
private Product fProduct;
[Association("Product-Orders")]
public Product Product {
get { return fProduct; }
set { SetPropertyValue(nameof(Product), ref fProduct, value); }
}
}

In the code above, the Order class contains the Total property and the Product class has the MaximumOrder and OrdersTotal properties. These Product's properties are calculated based on Total properties of the aggregated Orders. The OrderCount property is also added to the Product class. This property exposes the number of aggregated Orders.

在上面的代码中,"订单"类包含"总计"属性,而"产品"类具有"最大订单"和"订单总计"属性。这些产品的属性基于聚合订单的总计属性计算。"OrderCount"属性也会添加到"产品"类中。此属性公开聚合订单的数量。

Note 注意
You can modify an object from the child collection in a separate Detail View and save it. In this scenario, the parent object may also be marked as modified in a separate object space. If the collection property is not decorated with the AggregatedAttribute, you need to refresh the parent object before saving changes. To avoid this, disable the XpoDefault.IsObjectModifiedOnNonPersistentPropertyChange option before starting the application.
您可以在单独的"详细视图"中修改子集合中的对象并保存它。在这种情况下,父对象也可以标记为在单独的对象空间中修改。如果集合属性未使用聚合属性进行修饰,则需要在保存更改之前刷新父对象。为了避免这种情况,在启动应用程序之前,请禁用 XpoDefault.IsObjectModifiedon 无持久性属性更改选项。

Implement Non-Persistent Calculated Properties

An implementation of "lazy" calculated (calculated on demand) properties is described in this section.

Omit the property setter to implement a non-persistent property. The following code snippet demonstrates the implementation of three calculated properties - the OrdersCount, OrdersTotal and MaximumOrder.

实现非持久计算属性

本节将介绍计算(按需计算)属性的"懒"属性的实现。

省略属性集器以实现非持久性属性。以下代码段演示了三个计算属性的实现 - 订单计数、订单总计和最大订单。

[DefaultClassOptions]
public class Product : BaseObject {
// ...
private int? fOrdersCount = null;
public int? OrdersCount {
get {
if(!IsLoading && !IsSaving && fOrdersCount == null)
UpdateOrdersCount(false);
return fOrdersCount;
}
}
private decimal? fOrdersTotal = null;
public decimal? OrdersTotal {
get {
if(!IsLoading && !IsSaving && fOrdersTotal == null)
UpdateOrdersTotal(false);
return fOrdersTotal;
}
}
private decimal? fMaximumOrder = null;
public decimal? MaximumOrder {
get {
if(!IsLoading && !IsSaving && fMaximumOrder == null)
UpdateMaximumOrder(false);
return fMaximumOrder;
}
}
}

The properties' business logic is contained into three separate methods - UpdateOrdersCount, UpdateOrdersTotal and UpdateMaximumOrder. These methods are invoked in the property getters. Having the business logic in separate methods allows you to update a property's value by calling the corresponding method, when required. The OrdersCount is a simple calculated non-persistent property. This property is calculated using XPO criteria language. The OrdersTotal and MaximumOrder are complex calculated non-persistent properties, not expressed using the criteria language. So, traverse the Orders collection to calculate these properties.

属性的业务逻辑包含在三个单独的方法中 - 更新订单计数、更新订单总计和更新最大订单。这些方法在属性 getter 中调用。将业务逻辑用到单独的方法中允许您在需要时通过调用相应的方法来更新属性的值。OrdersCount 是一个简单的计算非持久性属性。此属性是使用 XPO 条件语言计算的。订单总计和最大订单是复杂计算的非持久性属性,不是使用条件语言表示的。因此,遍历"订单"集合以计算这些属性。

Note 注意
In this topic, the OrdersTotal and MaximumOrder properties are considered to be complex to illustrate how such properties are calculated. Actually, their values can be easily calculated using XPO criteria language. For instance, you can use the Avg, Count, Exists, Max and Min functions to perform aggregate operations on collections. Refer to the Criteria Language Syntax topic for details.
在本主题中,"订单总计"和"最大订单"属性被视为复杂属性,以说明如何计算这些属性。实际上,可以使用 XPO 标准语言轻松计算其值。例如,您可以使用平均、计数、存在、最大值和最小值函数对集合执行聚合操作。有关详细信息,请参阅标准语言语法主题。

The following snippet illustrates the UpdateOrdersCount, UpdateOrdersTotal and UpdateMaximumOrder methods definitions.

以下代码段说明了更新订单计数、更新订单总计和更新最大订单方法定义。

[DefaultClassOptions]
public class Product : BaseObject {
// ...
public void UpdateOrdersCount(bool forceChangeEvents) {
int? oldOrdersCount = fOrdersCount;
fOrdersCount = Convert.ToInt32(Evaluate(CriteriaOperator.Parse("Orders.Count")));
if (forceChangeEvents)
OnChanged(nameof(OrdersCount), oldOrdersCount, fOrdersCount);
}
public void UpdateOrdersTotal(bool forceChangeEvents) {
decimal? oldOrdersTotal = fOrdersTotal;
decimal tempTotal = 0m;
foreach (Order detail in Orders)
tempTotal += detail.Total;
fOrdersTotal = tempTotal;
if (forceChangeEvents)
OnChanged(nameof(OrdersTotal), oldOrdersTotal, fOrdersTotal);
}
public void UpdateMaximumOrder(bool forceChangeEvents) {
decimal? oldMaximumOrder = fMaximumOrder;
decimal tempMaximum = 0m;
foreach (Order detail in Orders)
if (detail.Total > tempMaximum)
tempMaximum = detail.Total;
fMaximumOrder = tempMaximum;
if (forceChangeEvents)
OnChanged(nameof(MaximumOrder), oldMaximumOrder, fMaximumOrder);
}
}

Note that the fOrdersCount is evaluated on the client side using the objects loaded from an internal XPO cache in the UpdateOrdersCount method. You can use the following code to evaluate the fOrdersCount on the server side, so the uncommitted objects are not taken into account.

请注意,使用从 UpdateOrdersCount 方法中从内部 XPO 缓存加载的对象在客户端上计算 fOrdersCount。可以使用以下代码来评估服务器端的 fOrdersCount,因此不考虑未提交的对象。

fOrdersCount = Convert.ToInt32(Session.Evaluate<Product>(CriteriaOperator.Parse("Orders.Count"),
CriteriaOperator.Parse("Oid=?", Oid)));

In the Order class' Total and Product property setters, a UI is updated when an Order object's property values change and an object is not currently being initialized:

在 Order 类的"总计"和"产品"属性设置器中,当 Order 对象的属性值发生更改且对象当前未初始化时,将更新 UI:

[DefaultClassOptions]
public class Order : BaseObject {
// ...
private decimal fTotal;
public decimal Total {
get { return fTotal; }
set {
bool modified = SetPropertyValue(nameof(Total), ref fTotal, value);
if(!IsLoading && !IsSaving && Product != null && modified) {
Product.UpdateOrdersTotal(true);
Product.UpdateMaximumOrder(true);
}
}
}
private Product fProduct;
[Association("Product-Orders")]
public Product Product {
get { return fProduct; }
set {
Product oldProduct = fProduct;
bool modified = SetPropertyValue(nameof(Product), ref fProduct, value);
if(!IsLoading && !IsSaving && oldProduct != fProduct && modified) {
oldProduct = oldProduct ?? fProduct;
oldProduct.UpdateOrdersCount(true);
oldProduct.UpdateOrdersTotal(true);
oldProduct.UpdateMaximumOrder(true);
}
}
}
}

In the Product class, the OnLoaded method is overridden, as it is necessary to reset cached values when using "lazy" calculations.

在 Product 类中,将重写 OnLoaded 方法,因为使用 "懒"计算时必须重置缓存的值。

[DefaultClassOptions]
public class Product : BaseObject {
// ...
protected override void OnLoaded() {
Reset();
base.OnLoaded();
}
private void Reset() {
fOrdersCount = null;
fOrdersTotal = null;
fMaximumOrder = null;
}
// ...

Store Calculated Property Values in the Database

The non-persistent calculated properties can be inappropriate in certain scenarios, especially when a large number of objects should be manipulated. Each time such a property is accessed, a query to the database is generated to evaluate the property for each master object. For instance, suppose you have the Order business class which has the Total non-persistent property. This property is calculated from the properties of the objects contained in the Order's child object collection. To display an Order object in a List View, the Total property's value should be determined. To determine that value, a database query is generated. If the List View should display a thousand objects, a thousand queries will be generated. Obviously, this can have a negative impact on the performance of the application.

To avoid the performance issues, the calculated property values can be stored in the database. You can apply the PersistentAttribute to save values to the database (see How to: Use Read-Only Persistent Properties). Additionally, if it is assumed that the calculated property is to be used in a filter criterion or while sorting, the PersistentAliasAttribute can be applied.

在数据库中存储计算的属性值

在某些情况下,非持久性计算属性可能不合适,尤其是在应操作大量对象时。每次访问此类属性时,都会生成对数据库的查询,以评估每个主对象的属性。例如,假设您具有具有总计非持久性属性的 Order 业务类。此属性是根据 Order 的子对象集合中包含的对象的属性计算的。要在列表视图中显示 Order 对象,应确定 Total 属性的值。要确定该值,将生成数据库查询。如果列表视图应显示一千个对象,则将生成一千个查询。显然,这可能对应用程序的性能产生负面影响。

为了避免性能问题,计算的属性值可以存储在数据库中。您可以应用"持久属性"将值保存到数据库(请参阅:如何使用只读持久性属性)。此外,如果假定计算属性将在筛选条件中使用或在排序时使用,则可以应用"持久别名属性"。

[DefaultClassOptions]
public class Product : BaseObject {
// ...
[Persistent("OrdersCount")]
private int? fOrdersCount = null;
[PersistentAlias(nameof(fOrdersCount))]
public int? OrdersCount {
// ...
}
[Persistent("OrdersTotal")]
private decimal? fOrdersTotal = null;
[PersistentAlias(nameof(fOrdersTotal))]
public decimal? OrdersTotal {
// ...
}
[Persistent("MaximumOrder")]
private decimal? fMaximumOrder = null;
[PersistentAlias(nameof(fMaximumOrder))]
public decimal? MaximumOrder {
// ...
}
// ...

Remove the OnLoaded method overload from the master Order class.

从主订单类中删除 OnLoaded 方法重载。

How to: Calculate a Property Value Based on Values from a Detail Collection 如何:基于详细信息集合中的值计算属性值的更多相关文章

  1. How to: Initialize Business Objects with Default Property Values in XPO 如何:在 XPO 中用默认属性值初始化业务对象

    When designing business classes, a common task is to ensure that a newly created business object is ...

  2. How to: Initialize Business Objects with Default Property Values in Entity Framework 如何:在EF中用默认属性值初始化业务对象

    When designing business classes, a common task is to ensure that a newly created business object is ...

  3. Format a Property Value 设置属性值的格式

    In this lesson, you will learn how to set a display format and an edit mask to a business class prop ...

  4. <s:property="a" value=""/>取的<s:debug></s:debug>中的value stack中的属性值

    <s:property="a"  value=""/>取的<s:debug></s:debug>中的value stack中 ...

  5. Objective-C中变量采用@property的各个属性值的含义

    我们在OC中定义变量,可以自己来定义变量的setter方法来设置变量值,用getter方法来获取变量值.但是当变量数量增多时,还采用手动添加setter/getter方法来操作变量,就会使得程序代码量 ...

  6. 【跟着stackoverflow学Pandas】Select rows from a DataFrame based on values in a column -pandas 筛选

    最近做一个系列博客,跟着stackoverflow学Pandas. 以 pandas作为关键词,在stackoverflow中进行搜索,随后安照 votes 数目进行排序: https://stack ...

  7. Implement Property Value Validation in the Application Model 在应用程序模型中实现属性值验证

    In this lesson, you will learn how to check whether or not a property value satisfies a particular r ...

  8. Implement Property Value Validation in Code 在代码中实现属性值验证(XPO)

    This lesson explains how to set rules for business classes and their properties. These rules are val ...

  9. 跟踪对象属性值的修改, 设置断点(Break on property change)

    代码 //Break on property change (function () { var localValue; Object.defineProperty(targetObject, 'pr ...

随机推荐

  1. CentOS7.4系统下,手动安装MySQL5.7的方法

    MySQL数据库应用广泛,尤其对于JAVA程序员,不会陌生.如果在不想采购云数据库的情况下,可以自行安装MySQL数据库.文章将介绍,手动在CentOS7.4环境下,安装MySQL5.7版本的方法. ...

  2. Spring通过IOC帮我们做火鸡

    一.IOC--setter注入 1.准备dmo 首先准备一只火鸡 public class Turkey { private int id; private String name; public i ...

  3. 转:Maven的默认中央仓库以及修改默认仓库&配置第三方jar包从私服下载

    当构建一个Maven项目时,首先检查pom.xml文件以确定依赖包的下载位置,执行顺序如下: 1.从本地资源库中查找并获得依赖包,如果没有,执行第2步. 2.从Maven默认中央仓库中查找并获得依赖包 ...

  4. k8s 开船记-首航:博客站点从 docker swarm 切换到 k8s

    昨天晚上,我们将博客站点的生产环境从 docker swarm 集群切换到了 k8s 集群,开船到目前,航行非常平稳,可以说首航成功! k8s 集群是我们用10台阿里云服务器自己搭建的,1台 mast ...

  5. GitHub 上值得参考的完整的 iOS-App 源码

    转自:https://www.zhihu.com/question/28518265 作者:wjh2005链接:https://www.zhihu.com/question/28518265/answ ...

  6. Selenium 4 Python的最佳测试框架

    随着Python语言的使用越来越流行,基于Python的测试自动化框架也越来越流行.在项目选择最佳框架时,开发人员和测试人员会有些无法下手.做出选择是应该判断很多事情,框架的脚本质量,测试用例的简单性 ...

  7. Mock接口依赖的使用

    mock 能做什么 1.前后端联调,如果你是一个前端页面开发,现在需要开发一个功能:下一个订单,支付页面的接口,根据支付结果,支付成功,展示支付成功页,支付失败,展示支付失败页.要完成此功能,你需要 ...

  8. 一篇文章看清楚JDK13的特性!

    1.switch优化更新 JDK11以及之前的版本: switch (day) { case MONDAY: case FRIDAY: case SUNDAY: System.out.println( ...

  9. webpack实践(二)- webpack配置文件

    webpack系列博客中代码均在github上:https://github.com/JEmbrace/webpack-practice <webpack实践(一)- 先入个门> < ...

  10. 简单学习【1】——使用webpack

    使用webpack webpack命令 webpack配置 第三方脚手架 1.webpack命令 webpack - h (webpack 所有的选项) webpack -v (查看webpack的版 ...