• How to: 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.

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

    CalculatePropertyBasedOnDetailCollection

    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 方法重载。

  • 相关阅读:
    表空间的改变
    特殊字符转换
    oracle下创建临时表
    约束
    删除数据库记录的同时删除磁盘文件
    数据文件
    级联删除case
    oracle net连接方式

    在Oracle服务器端配置监听器
  • 原文地址:https://www.cnblogs.com/foreachlife/p/How-to-Calculate-a-Property-Value-Based-on-Values-from-a-Detail-Collection.html
Copyright © 2020-2023  润新知