EPR类企业管理系统

在我们现有系统基础上或全新开发,提供定制服务
为您的企业高效快速实施ERP,WMS,MES,CRM管理系统
全面管控物料仓库、销售业务、采购业务、仓库业务
生产过程、质量检验、组织架构、业务报表


定制
QQ:460-3528

开发
QQ群:3360-90194

源码
微信:136-3650-3721

如何:基于明细集合中的值计算属性值

本主题描述如何实现业务类,以便根据子对象集合中包含的对象的一个​​或多个属性来计算其业务之一。

CalculatePropertyBasedOnDetailCollection

提示

可在http://www.devexpress.com/example=E305的DevExpress代码示例数据库中找到完整的示例项目。

初始课程实施

一个产品类有集合的对象。该产品订购类由相关的一个一对多的关系,这意味着一个产品对象可以与几个相关订单的对象。Order对象的集合被聚合。将创建订单对象,该对象属于“产品”对象之一。删除主对象时,其聚合集合中的所有对象也将被删除。

以下代码段说明了Product类的实现。

[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)); }
    }
}

以下代码段说明了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); }
    }
}

在上面的代码中,Order类包含Total属性,而Product类具有MaximumOrderOrdersTotal属性。这些产品的属性是基于汇总订单的“总计”属性来计算的。该定单计数属性也被添加到产品类。此属性公开汇总的Orders的数量。

注意

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

实现非持久计算属性

本节介绍了计算(按需计算)属性的“惰性”实现。

省略属性设置器以实现非持久属性。以下代码段演示了三个计算属性的实现-OrdersCountOrdersTotalMaximumOrder

[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;
        }
    }
}

属性的业务逻辑包含成三个独立的方法- UpdateOrdersCountUpdateOrdersTotalUpdateMaximumOrder。这些方法在属性获取器中调用。将业务逻辑放在不同的方法中,使您可以在需要时通过调用相应的方法来更新属性的值。该OrdersCount是一个简单的计算非持久性属性。此属性是使用XPO标准语言计算的。的OrdersTotalMaximumOrder是复杂的计算非持久性,使用的标准语言不表达。因此,遍历Orders集合以计算这些属性。

注意

在本主题中,OrdersTotalMaximumOrder属性被认为是复杂的,以说明如何计算此类属性。实际上,可以使用XPO标准语言轻松地计算它们的值。例如,您可以使用AvgCountExistsMaxMin函数对集合执行聚合操作。有关详细信息,请参阅“标准语言语法”主题。

下面的代码片段说明了UpdateOrdersCountUpdateOrdersTotalUpdateMaximumOrder方法定义。

[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);
    }
}

注意,fOrdersCount是使用从内部装入的对象在客户端评估XPO在缓存UpdateOrdersCount方法。您可以使用以下代码在服务器端评估fOrdersCount,因此不会考虑未提交的对象。

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

Order类的TotalProduct属性设置器中,当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);
            }
        }
    }
}

Product类中,将重写OnLoaded方法,因为在使用“惰性”计算时有必要重置缓存的值。

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

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

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

为了避免性能问题,可以将计算出的属性值存储在数据库中。您可以应用PersistentAttribute将值保存到数据库中(请参阅如何:使用只读持久性属性)。另外,如果假定要在过滤条件中或排序时使用计算所得的属性,则可以应用PersistentAliasAttribute

[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 {
        // ...
    }
    // ...

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

相关文章

转载保留此链接,注明出处