I am trying to apply some business logic.
My question about usage of LINQ on the following object models to apply the business logic. I have the following objects populated accordingly:
public class Waiver
{
public string Id { get; set; }
public int Type { get; set; }
public decimal Amount { get; set; }
}
Business Logic to be applied:
1.) Apply Line Item waiver
If LineItem Waiver [Type] is 1111 deduct the LineItem Waiver amount off Unit Price
If LineItem Waiver [Type] is 2222 deduct LineItem WaiverAmount as a Percentage Off Unit Price
If LineItem Waiver [Type] is 3333 deduct the LineItem Waiver amount off (Line Price = Qty * Unit Price)
If LineItem Waiver [Type] is 4444 deduct LineItem Waiver Amount as a Percentage Off Line Price
2.) Apply Order waiver
If Order Waiver [Type] is 4444 deduct Order Waiver amount Off Total Order Price after applying LineItem Waivers
If Order Waiver [Type] is 8888 deduct Order Waiver amount as Percentage Off Order Price after applying LineItem Waivers
What is the best way to achieve this?
GetWaivedPrice(decimal unitPrice, int qty, IEnumerable<Waiver> waiver)
Can GetWaivedPrice
be written as a single LINQ method with appropriate mapping for all the discount types?
This is what I am trying to achieve, preferably as a well written LINQ method:
private decimal GetWaivedPrice(decimal unitPrice, int qty,
IEnumerable<Waiver> waiver)
{
//Pseudo code shown for clarifying intent
decimal waivedLineItemAmount = 0m;
if waiver.Select(d => d.Type == 1111)
//then apply the business logic on the unit price accordingly
if waiver.Select(d => d.Type == 2222)
//then apply the business logic on the unit price accordingly
if waiver.Select(d => d.Type == 3333)
//then apply the business logic on the unit price accordingly
return waivedLineItemAmount;
}
I don't see the case for LINQ here. Just apply each Waver
in turn.
private decimal GetWaivedPrice(decimal unitPrice, int qty,
IEnumerable<Waiver> waiver)
{
//Pseudo code shown for clarifying intent
decimal waivedLineItemAmount = 0m;
//apply all waivers
foreach (var w in waiver) {
switch (w.Type) {
case 1111:
waivedLineItemAmout += someComputation();
break;
case 2222:
waivedLineItemAmout += someComputation();
break;
case 3333:
waivedLineItemAmout += someComputation();
break;
}
}
return waivedLineItemAmount;
}
You can formulate this with Enumerable.Aggregate
if you insist on LINQ and a purely functional style but a simple loop seems just fine here.
I am not seeing the need for linq per-se, but maybe Object Oriented design.
I would instead have the waiver handle the business logic in a method call such as what you have now.
public class Waiver
{
public string Id { get; set; }
public int Type { get; set; }
public decimal Amount { get; set; }
public decimal GetWaivedPrice(decimal unitPrice, int qty) { ... }
}
By doing that it would facilitate any future linq projections or operations such as this grouping and of course centralize the business logic for future maintenance.
var groupedResult = myWaivers.Select(wv => new
{
Type = wv.Type,
WaivedPrice = wv.GetWaivedPrice( unitPrice, qty)
} )
.GroupBy(wv => wv.Type);
How about that:
private static Dictionary<int, Func<decimal, int, Waiver, decimal>> _logic
= new Dictionary<int, Func<decimal, int, Waiver, decimal>>() {
{ 2222, (a, q, w) => a + w.Amount },
{ 3333, (a, q, w) => a + w.Amount },
{ 3333, (a, q, w) => a + w.Amount }
};
private static decimal GetWaivedPrice(decimal unitPrice, int qty,
IEnumerable<Waiver> waiver)
{
return waiver.Aggregate(0m, (a, s) => _logic[s.Type](a, qty, s), a => a);
}
Of course you have to update _logic
dictionary with your discount logic to make it work.
Business rules are isolated into separate concerns (deductions and strategy) and it's now pretty LINQ-ish!
// Create a data structure to model the deductions themselves
public class LineItemDeduction
{
public decimal UnitPriceAmount { get; set; }
public decimal UnitPricePercentage { get; set; }
public decimal LinePriceAmount { get; set; }
public decimal LinePricePercentage { get; set; }
// Assumed that waivers are distinct and are not composed together, only applied on the listed price.
public decimal CalculateWaivedPrice(decimal unitPrice, int qty)
{
return ((unitPrice - UnitPriceAmount - (unitPrice * UnitPricePercentage)) * qty) - LinePriceAmount - (unitPrice * qty * LinePricePercentage);
}
}
// Calculate the deductions
private LineItemDeduction CalculateLineItemDeductionStrategy(LineItemDeduction deduction, Waiver waiver)
{
switch (waiver.Type) {
case 1111:
deduction.UnitPriceAmount += waiver.Amount;
break;
case 2222:
deduction.UnitPricePercentage += waiver.Amount;
break;
case 3333:
deduction.LinePriceAmount += waiver.Amount;
break;
case 4444:
deduction.LinePricePercentage += waiver.Amount;
break;
}
return deduction;
}
// Extension method only for LineItem but it's the same principle for order waivers
public static decimal GetWaivedPrice(this IEnumerable<Waiver> waivers, decimal unitPrice, int qty, Func<LineItemDeduction, Waiver, LineItemDeduction> deductionStrategy)
{
return waivers.Aggregate(
new LineItemDeduction(),
deductionStrategy,
d => d.CalculateWaivedPrice(unitPrice, qty)
);
}
// Now to get the waived price
var waivedPrice = waivers.GetWaivedPrice(unitPrice, qty, CalculateLineItemDeductionStrategy);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.