-1

I am using C# in a .NET 6.0 web application and I want to calculate a score based on medical formula with 15 variables. Is there a more structured way in order to avoid multiple and enfolded switch & if then statements? I tried using Microsoft's Rules Engine but the output of each rule is constant while in my scenario the output should be related on the variable input. For example the output of the rule if Ejection Fraction is between 20-24 should be +6

Should I use the rule design pattern or is it overkill for my scenario? I want to achieve something like the following example.

enter image description here

Avoiding code like that:

switch (patientDataJson.EjectionFraction)
                {
                    case int n when (n < 20):
                        score += 7;
                        break;
                    case int n when (n >= 20 && n <= 24):
                        score += 6;
                        break;
                    case int n when (n >= 25 && n <= 29):
                        score += 5;
                        break;
                    case int n when (n >= 30 && n <= 34):
                        score += 3;
                        break;
                    case int n when (n >= 35 && n <= 39):
                        score += 2;
                        break;
                    case int n when (n >= 40):
                        score += 0;
                        break;
                    default:
                        score += 0;
                        break;
                }

Any help will be highly appreciated ! Thank you !

nitse
  • 21
  • 7
  • 1
    Switch cases are evaluated in order. In your case, it suffices to check if n <= Max for each tier. For instance, case two will always be >= 20 as it would otherwise already have broken out of the first case, thus n <= 24 is enough to check. If you use switch case pattern matching, you can also reduce the verbosity a lot: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/switch-expression – Beltway Jan 25 '23 at 15:29
  • 2
    This seems like information that should be in a database not hard coded. – Servy Jan 25 '23 at 15:45
  • That's why I tried to use Micosoft's Rule Engine in order to have all the rules in Json or DB but it does not support multiple outputs for a given input – nitse Jan 25 '23 at 15:50
  • 1
    I prefer some sort of pattern for more complex operations. For what you're doing the code in your question (or comparable `if` statements) is much easier to read (and therefore modify) than anything in the answers. – Scott Hannen Jan 25 '23 at 15:58
  • Thank you ! The code that I quoted in my post is a really simplified scenario. If you actually check the formula, many variables lead to enfolded switch statements in order to evaluate them (e.g. Extra for systolic BP (mm Hg) if EF <30) I am leaning towards @Przemosz solution and to create my own rule engine – nitse Jan 25 '23 at 16:09

4 Answers4

3

I would say it depends :-)

  • If your rules are fixed on compile time => use the switch
  • If your rules are fixed on compile time but the switch gets to complicated => use something like @Przemosz suggested (But move the if(...) in Evaluate to CanEvaluate and rename CanEvaluate in ShouldEvaluate)
  • If the users or admins should manipulate the "rules" => use the rules framework or write your own
Markus
  • 2,184
  • 2
  • 22
  • 32
  • Thank you for your answer ! You summarized my thoughts. I think my scenario is the second one, therefore a simplified version of my own rule engine would be the most appropriate. Thanks again ! – nitse Jan 25 '23 at 16:13
1

Well, create your own rule engine. Rules should have methods that take object as argument and there evaluate rule based on conditions. Example:

public sealed class EjectionFractionRule: IRule
{
    public void Evalueate(PatientContext context)
    {
        if(context.Patient.EjectionFraction >=20 && context.Patient.EjectionFraction<=24)
        {
            context.TotalPoints +=6;
        }

    }

    public bool CanEvaluateRule(PatientContext context) => true;
}

public sealed class PatientContext
{
    public int TotalPoints { get;set; }
    public Patient { get; init; }
}

public interface IRule
{
    void Evaluate(PatientContext context);
    bool CanEvaluateRule(PatientContext context);
}
Przemosz
  • 101
  • 5
1

For that particular case, I reckon a Rule Engine would be quite over-engineered. Just Simplify your switch-statement.

    score += GetEFSummand(patientDataJson.EjectionFraction);

    private int GetEFSummand(int ef) => ef switch
    {
        int n when n < 20 => 7,
        int n when n <= 24 => 6,
        int n when n <= 34 => 5,
        // ...
        _ => 0
    };
Beltway
  • 508
  • 4
  • 17
  • That's true. Thanks ! I was just trying to make a point (that's not our final code of course). The problem is that if you see the whole formula there are enfolded switches statements. All those are enfolded switch statements: - Extra for systolic BP (mm Hg) if EF <30 - Extra for systolic BP (mm Hg) if EF 30-39 - Extra for systolic BP (mm Hg) if EF ≥40 - Extra for age (years) if EF 30-39 - Extra for age (years) if EF ≥40 – nitse Jan 25 '23 at 15:44
  • damn you were 2 minutes faster ;-) – noel Jan 25 '23 at 15:44
1

You could also just simplify your switch statement a bit and make it less verbose by using an expression

int ef = *some value*;
var efPoints = ef switch {
        >= 40 => 0,
        >= 35 => 2,
        >= 30 => 3,
        >= 25 => 5,
        >= 20 => 6,                 
        _ => 7
};

C# Fiddle

And maybe put it in an appropriately named method

noel
  • 531
  • 2
  • 7
  • Thanks for your reply. That would help but I mentioned the simplified scenario. If you see the medical formula [link](https://www.mdcalc.com/calc/3803/maggic-risk-calculator-heart-failure#evidence) there are cases that require enfolded switch statements and tha is something that I would prefer to avoid (e.g. Extra for systolic BP (mm Hg) if EF <30) – nitse Jan 25 '23 at 15:53
  • I see your point, in that case I would probably also look for a more elegant solution. I think @Markus points out some good considerations to make – noel Jan 25 '23 at 15:59
  • Yeap I saw his answer and I think I will go for his second solution, something like @Przemosz solution and create my own rule engine – nitse Jan 25 '23 at 16:06