学习011-03-05 Calculate a Property Value Based on Values from a Detail Collection(根据明细集合中的值计算属性值)

Calculate a Property Value Based on Values from a Detail Collection(根据明细集合中的值计算属性值)

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.
本主题介绍如何实现业务类,以便根据子对象集合中包含的对象的属性计算其属性之一。
在这里插入图片描述

Tip
A complete sample project is available in the DevExpress Code Examples database at https://supportcenter.devexpress.com/ticket/details/e305/how-to-calculate-a-master-property-based-on-values-from-a-details-collection.
https://supportcenter.devexpress.com/ticket/details/e305/how-to-calculate-a-master-property-based-on-values-from-a-details-collection的DevExpress代码示例数据库中提供了完整的示例项目。

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.
Product类具有Order对象的集合。Product和Order类通过一对多关系相关联,这意味着一个Product对象可能与多个Order对象相关联。Order对象的集合被聚合。Order对象被创建,属于Product对象之一。当主对象被删除时,其聚合集合中的所有对象也会被删除。

The following snippet illustrates the Product class implementation.
以下代码片段说明了Product类的实现。

C#
[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类的实现。

C#
[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.
在上面的代码中,Order类包含Total属性,Product类具有MaximumOrder和OrdersTotal属性。这些Product的属性是根据聚合Orders的Total属性计算的。OrderCount属性也被添加到Product类中。此属性公开聚合Orders的数量。

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.
您可以在单独的详细信息视图中修改子集合中的对象并将其保存。在这种情况下,父对象也可能在单独的对象空间中被标记为已修改。如果集合属性未使用AggregatedAtcade装饰,则需要在保存更改之前刷新父对象。为避免这种情况,请在启动应用程序之前禁用XpoDefault.IsObjectModifiedOnNonPersistentPropertyChange选项。

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.
省略属性设置器以实现非持久性属性。以下代码片段演示了三个计算属性的实现-OrdersCount、OrdersTotal和MaximumOrder。

C#
[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.
属性的业务逻辑包含在三个单独的方法中——UpdateOrdersCount、UpdateOrdersTotal和UpdateMaximumOrder。这些方法在属性getter中调用。将业务逻辑放在单独的方法中允许您在需要时通过调用相应的方法来更新属性的值。OrdersCount是一个简单计算的非持久性属性。此属性使用XPO标准语言计算。OrdersTotal和MaximumOrder是复杂计算的非持久性属性,不使用标准语言表示。因此,遍历Orders集合来计算这些属性。

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.
在本主题中,OrdersTotal和MaximumOrder属性被认为是复杂的,以说明如何计算这些属性。实际上,它们的值可以使用XPO标准语言轻松计算。例如,您可以使用Avg、Count、Exist、Max和Min函数对集合执行聚合操作。有关详细信息,请参阅标准语言语法主题。

The following snippet illustrates the UpdateOrdersCount, UpdateOrdersTotal and UpdateMaximumOrder methods definitions.
以下代码片段说明了UpdateOrdersCount、UpdateOrdersTotal和UpdateMaximumOrder方法定义。

C# 
[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.
请注意,fOrdersCount在客户端使用UpdateOrdersCount方法中从内部XPO缓存加载的对象进行评估。您可以使用以下代码在服务器端评估fOrdersCount,因此不考虑未提交的对象。

C#
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类的Total和Product属性设置器中,当Order对象的属性值更改并且当前未初始化对象时,UI会更新:

C# 
[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类中,OnLoade方法被覆盖,因为在使用“惰性”计算时需要重置缓存值。

C# 
[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.
在某些情况下,非持久性计算的属性可能不合适,尤其是当应该操作大量对象时。每次访问这样的属性时,都会生成对数据库的查询,以评估每个主对象的属性。例如,假设您有Order业务类,它具有Total非持久性属性。该属性是根据Order的子对象集合中包含的对象的属性计算的。要在List View中显示Order对象,应确定Total属性的值。要确定该值,将生成一个数据库查询。如果List View应显示一千个对象,则将生成一千个查询。显然,这会对应用程序的性能产生负面影响。

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.
为避免性能问题,可以将计算的属性值存储在数据库中。您可以应用PersitentAtcade将值保存到数据库中(请参阅如何:使用只读持久属性)。此外,如果假设计算的属性将用于筛选条件或排序时,则可以应用PersistentAliasAttribute。

C# 
[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.
从主Order类中删除OnLoad方法重载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汤姆•猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值