Change in property values of children and grandchildren objects (related to 3182) #3232
Replies: 6 comments 3 replies
-
Well, overriding the Save didn't work : ). |
Beta Was this translation helpful? Give feedback.
-
What I did was:
I put this in the business class and do my magic there. When the child or a property changes, it invokes the business rules to do the calculations. I do this in my purchase order classes when calculating the total line items. I think Rocky answered a similar question last week and has a method called something like "OnChildChange" but I can't remember. |
Beta Was this translation helpful? Give feedback.
-
This should work: // .NET 6 console app
// NuGet reference to Csla v6
using Csla;
using Csla.Configuration;
using Csla.Core;
using Csla.Rules;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
services.AddCsla();
var provider = services.BuildServiceProvider();
var ApplicationContext = provider.GetRequiredService<ApplicationContext>();
var portal = ApplicationContext.GetRequiredService<IDataPortal<Order>>();
var order = portal.Create();
var details = order.LineItems[0].LineItemDetails[0];
details.Quantity = 10;
details.Price = 4.2;
Console.WriteLine($"Detail: {details.Cost}");
Console.WriteLine($"Total: {order.LineItems[0].TotalCost}");
Console.WriteLine($"Grand: {order.TotalCost}");
// ==============================================================================
[Serializable]
public class Order : BusinessBase<Order>
{
public static readonly PropertyInfo<int> IdProperty = RegisterProperty<int>(nameof(Id));
public int Id
{
get => GetProperty(IdProperty);
set => SetProperty(IdProperty, value);
}
public static readonly PropertyInfo<LineItemList> LineItemsProperty = RegisterProperty<LineItemList>(nameof(LineItems));
public LineItemList LineItems
{
get => GetProperty(LineItemsProperty);
private set => LoadProperty(LineItemsProperty, value);
}
public static readonly PropertyInfo<double> TotalCostProperty = RegisterProperty<double>(nameof(TotalCost));
public double TotalCost
{
get => GetProperty(TotalCostProperty);
private set => LoadProperty(TotalCostProperty, value);
}
protected override void OnChildChanged(ChildChangedEventArgs e)
{
base.OnChildChanged(e);
if (e.PropertyChangedArgs?.PropertyName == "TotalCost")
{
BusinessRules.CheckRules(TotalCostProperty);
}
}
protected override void AddBusinessRules()
{
base.AddBusinessRules();
BusinessRules.AddRule(new CalcTotalCost() { PrimaryProperty = TotalCostProperty });
}
[Create]
private void Create()
{
LineItems = ApplicationContext.GetRequiredService<IChildDataPortal<LineItemList>>().CreateChild();
}
public class CalcTotalCost : BusinessRule
{
protected override void Execute(IRuleContext context)
{
var target = (Order)context.Target;
double total = 0;
foreach (var detail in target.LineItems)
{
total += detail.TotalCost;
}
context.AddOutValue(total);
}
}
}
[Serializable]
public class LineItem : BusinessBase<LineItem>
{
public static readonly PropertyInfo<int> IdProperty = RegisterProperty<int>(nameof(Id));
public int Id
{
get => GetProperty(IdProperty);
set => SetProperty(IdProperty, value);
}
public static readonly PropertyInfo<LineItemDetailList> LineItemDetailsProperty = RegisterProperty<LineItemDetailList>(nameof(LineItemDetails));
public LineItemDetailList LineItemDetails
{
get => GetProperty(LineItemDetailsProperty);
private set => LoadProperty(LineItemDetailsProperty, value);
}
public static readonly PropertyInfo<double> TotalCostProperty = RegisterProperty<double>(nameof(TotalCost));
public double TotalCost
{
get => GetProperty(TotalCostProperty);
private set => LoadProperty(TotalCostProperty, value);
}
protected override void OnChildChanged(ChildChangedEventArgs e)
{
base.OnChildChanged(e);
if (e.PropertyChangedArgs?.PropertyName == "Quantity" || e.PropertyChangedArgs?.PropertyName == "Price")
{
BusinessRules.CheckRules(TotalCostProperty);
OnPropertyChanged(TotalCostProperty);
}
}
protected override void AddBusinessRules()
{
base.AddBusinessRules();
BusinessRules.AddRule(new CalcTotalCost() { PrimaryProperty = TotalCostProperty });
}
[CreateChild]
private void Create()
{
LineItemDetails = ApplicationContext.GetRequiredService<IChildDataPortal<LineItemDetailList>>().CreateChild();
}
public class CalcTotalCost : BusinessRule
{
protected override void Execute(IRuleContext context)
{
var target = (LineItem)context.Target;
double total = 0;
foreach (var detail in target.LineItemDetails)
{
total += detail.Cost;
}
context.AddOutValue(total);
}
}
}
[Serializable]
public class LineItemDetail : BusinessBase<LineItemDetail>
{
public static readonly PropertyInfo<int> IdProperty = RegisterProperty<int>(nameof(Id));
public int Id
{
get => GetProperty(IdProperty);
set => SetProperty(IdProperty, value);
}
public static readonly PropertyInfo<int> QuantityProperty = RegisterProperty<int>(nameof(Quantity));
public int Quantity
{
get => GetProperty(QuantityProperty);
set => SetProperty(QuantityProperty, value);
}
public static readonly PropertyInfo<double> PriceProperty = RegisterProperty<double>(nameof(Price));
public double Price
{
get => GetProperty(PriceProperty);
set => SetProperty(PriceProperty, value);
}
public static readonly PropertyInfo<double> CostProperty = RegisterProperty<double>(nameof(Cost));
public double Cost
{
get => GetProperty(CostProperty);
private set => LoadProperty(CostProperty, value);
}
protected override void AddBusinessRules()
{
base.AddBusinessRules();
BusinessRules.AddRule(new CostRule(QuantityProperty, PriceProperty, CostProperty));
BusinessRules.AddRule(new Csla.Rules.CommonRules.Dependency(PriceProperty, QuantityProperty));
}
[CreateChild]
private void Create()
{ }
}
public class CostRule : BusinessRule
{
public CostRule(IPropertyInfo quantityProperty, IPropertyInfo priceProperty, IPropertyInfo costProperty)
{
PrimaryProperty = quantityProperty;
InputProperties.Add(quantityProperty);
InputProperties.Add(priceProperty);
AffectedProperties.Add(costProperty);
}
protected override void Execute(IRuleContext context)
{
var quantity = (int)context.InputPropertyValues[InputProperties[0]];
var price = (double)context.InputPropertyValues[InputProperties[1]];
var cost = quantity * price;
context.AddOutValue(AffectedProperties[1], cost);
}
}
[Serializable]
public class LineItemList : BusinessListBase<LineItemList, LineItem>
{
[CreateChild]
private void Create()
{
Add(ApplicationContext.GetRequiredService<IChildDataPortal<LineItem>>().CreateChild());
}
}
[Serializable]
public class LineItemDetailList : BusinessListBase<LineItemDetailList, LineItemDetail>
{
[CreateChild]
private void Create()
{
Add(ApplicationContext.GetRequiredService<IChildDataPortal<LineItemDetail>>().CreateChild());
}
} |
Beta Was this translation helpful? Give feedback.
-
Something interesting is going on here, Let me recap the object graph LineItemDetail has cost property which is calculated by a business rule. LineItem has TotalCost which is sum of cost(s) of LineItemDetails while Order I added the following code to Order class protected override void OnChildChanged(Csla.Core.ChildChangedEventArgs e)
{
if (e.ChildObject is LineItems)
{
BusinessRules.CheckRules(LineItemsProperty);
OnPropertyChanged(LineItemsProperty);
}
base.OnChildChanged(e);
}
protected override void OnChildChanged(Csla.Core.ChildChangedEventArgs e)
{
if (e.ChildObject is LineItemDetails)
{
BusinessRules.CheckRules(LineItemDetailsProperty);
OnPropertyChanged(LineItemDetailsProperty);
}
base.OnChildChanged(e);
}
|
Beta Was this translation helpful? Give feedback.
-
@rockfordlhotka Thank you Sir, I am going to try your solution. |
Beta Was this translation helpful? Give feedback.
-
@rockfordlhotka putting a breakpoint showed that e.ChildObject is LineItemDetail (the individual object and not the collection) in the code below protected override void OnChildChanged(Csla.Core.ChildChangedEventArgs e)
{
if (e.ChildObject is LineItemDetails)
{
BusinessRules.CheckRules(TotalCostProperty);
OnPropertyChanged(TotalCostProperty);
}
base.OnChildChanged(e);
} If I understand correctly, since I wasn't adding or removing items (individual objects) from LineItemDetails the code wasn't working, instead, I was changing the state of individual object(s) so when I changed if (e.ChildObject is LineItemDetail) //To individual child object the code started working. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
This is related to #3182
We have a hierarchy of objects as follows
Order - Parent
LineItem - Child
LineItemDetail - Grand child.
In Order and LineItem objects, we have business rules that calculate the 'GrandCost' and TotalCost' respectively.
In LineItemDetail we have a property 'Cost' which is calculated by a business rule (inside LineItemDetail). In LineItem (Which is parent of LineItemDetail) 'TotalCost' is calculated from 'Cost' properties of LineItemDetails (it's children).
Since the Cost property is calculated inside business rule. It's not triggering the Rules Engine and the business rule for TotalCost in LineItem is not getting called.
Should we override the 'Save' methods in Order and LineItem objects and explicitly call the business rules for 'GrandTotal' and 'TotalCost'?
Kind Regards
Beta Was this translation helpful? Give feedback.
All reactions