C#  

Building an AI-Powered Clinical Decision Support System with C# and .NET

Introduction

Today, I would like to share some of my experiences from my earlier job experiences. Clinical Decision Support Systems (CDSS) are essential tools in modern healthcare, designed to enhance medical decision-making by leveraging patient data, clinical knowledge, and artificial intelligence. With the rise of machine learning and big data, AI-powered CDSS can significantly improve diagnostic accuracy, treatment planning, and patient outcomes.

In this article, we’ll explore how to design and implement a Clinical Decision Support System using C# and .NET, integrated with AI models, healthcare datasets, and a multi-database backend including DynamoDB, MongoDB, and Amazon Redshift. This hybrid architecture allows the system to perform real-time operations, flexible data management, and advanced analytics.

What is Clinical Decision Support?

A Clinical Decision Support System (CDSS) is a health information technology system that is designed to provide physicians and other health professionals with clinical decision-making support. These systems analyze data within electronic health records (EHRs) to provide prompts and reminders to assist healthcare providers in implementing evidence-based clinical guidelines.

Common Examples

  • Suggesting drug dosages based on patient weight and kidney function
  • Alerting physicians of potential drug interactions
  • Recommending diagnostic tests based on presenting symptoms
  • Ranking likely diagnoses for differential consideration

Architecture Overview

Architecture Overview

Components

  1. User Interface: ASP.NET Core frontend for clinical input/output
  2. FHIR Data Layer: Interface to EHR systems via HL7 FHIR APIs
  3. AI Engine: ONNX or ML.NET integrated for real-time diagnosis support
  4. Database Layer
    • DynamoDB for fast session/log storage
    • MongoDB for flexible clinical notes and rules
    • Redshift for analytics and reporting
  5. Audit and Override Logging: Stores override logs and outcome decisions

Multi-Database Architecture

MongoDB: Clinical Notes & Rules Engine

public class ClinicalNote
{
    public string Id { get; set; }
    public string PatientId { get; set; }
    public string NoteText { get; set; }
    public DateTime CreatedAt { get; set; }
}

Used to store variable-length physician notes and dynamically evolving medical rules.

DynamoDB: Decision Logs

[DynamoDBTable("CDSDecisionLogs")]
public class DecisionLog
{
    [DynamoDBHashKey]
    public string PhysicianId { get; set; }

    [DynamoDBRangeKey]
    public DateTime Timestamp { get; set; }

    [DynamoDBProperty]
    public string AIRecommendation { get; set; }

    [DynamoDBProperty]
    public string FinalDecision { get; set; }
}

Stores real-time logs of decisions, overrides, and outcomes.

Amazon Redshift: Reporting and Analytics

-- Example Redshift query
SELECT
  diagnosis,
  COUNT(*) AS total,
  AVG(age) AS avg_age
FROM
  clinical_diagnoses
GROUP BY diagnosis
ORDER BY total DESC;

Used to track diagnostic trends, treatment effectiveness, and demographic breakdowns.

FHIR Integration

FHIR (Fast Healthcare Interoperability Resources) is a standardized way of representing and exchanging healthcare information electronically. We'll use the Hl7.Fhir.R4 NuGet package.

Step 1. Install FHIR SDK.

Install-Package Hl7.Fhir.R4

Step 2. Read Patient and Observation Data

using Hl7.Fhir.Rest;
using Hl7.Fhir.Model;

public class FhirService
{
    private readonly FhirClient _client;

    public FhirService(string fhirEndpoint)
    {
        _client = new FhirClient(fhirEndpoint);
    }

    public Patient GetPatientById(string id)
    {
        return _client.Read<Patient>($"Patient/{id}");
    }

    public List<Observation> GetPatientObservations(string patientId)
    {
        var bundle = _client.Search<Observation>(new string[]
        {
            $"patient=Patient/{patientId}"
        });

        return bundle.Entry.Select(e => (Observation)e.Resource).ToList();
    }
}

Step 3. Map FHIR to Local PatientInput.

public static PatientInput ConvertFromFhir(Patient fhirPatient, List<Observation> observations)
{
    var input = new PatientInput
    {
        Age = (int)((DateTimeOffset.Now - fhirPatient.BirthDateElement.ToDateTimeOffset()).TotalDays / 365),
        Gender = fhirPatient.Gender.ToString(),
        LabResults = observations.ToDictionary(
            obs => obs.Code.Text ?? obs.Code.Coding.FirstOrDefault()?.Display,
            obs => obs.Value.ToString()
        ),
        Symptoms = new List<string>() // Could be pulled from Condition or QuestionnaireResponse in real EHR
    };
    return input;
}

Note. You could also extract conditions, allergies, and medications using FHIR endpoints Condition, AllergyIntolerance, and MedicationStatement.

Example Scenario: Symptom-Based Treatment Recommendations

Let’s assume a CDSS use case for diagnosing and recommending treatment options for a patient presenting with symptoms like fever, cough, and fatigue.

Model

public class PatientInput
{
    public int Age { get; set; }
    public string Gender { get; set; }
    public List<string> Symptoms { get; set; }
    public Dictionary<string, string> LabResults { get; set; }
}

Integrating AI with ML.NET or ONNX

AI Inference Service

public class AIModelService
{
    private readonly InferenceSession _session;

    public AIModelService(string modelPath)
    {
        _session = new InferenceSession(modelPath);
    }

    public string PredictDiagnosis(PatientInput input)
    {
        // Simplified conversion logic
        var tensor = new DenseTensor<float>(new[] { 1, input.Symptoms.Count });
        var inputs = new List<NamedOnnxValue>
        {
            NamedOnnxValue.CreateFromTensor("input", tensor)
        };

        using var results = _session.Run(inputs);
        return results.First().AsEnumerable<string>().First();
    }
}

Rule-Based Recommendation Engine

public class RecommendationEngine
{
    private static readonly Dictionary<string, List<string>> TreatmentMap = new()
    {
        { "Flu", new List<string> { "Rest", "Hydration", "Antivirals" } },
        { "COVID-19", new List<string> { "Isolation", "Monitoring O2", "Antivirals" } },
        { "Common Cold", new List<string> { "Rest", "Vitamin C", "Decongestants" } },
    };

    public List<string> GetTreatments(string diagnosis)
    {
        return TreatmentMap.ContainsKey(diagnosis) ? TreatmentMap[diagnosis] : new List<string> { "Consult Specialist" };
    }
}

Controller Example with Logging

public async Task<IActionResult> DiagnoseFromFhir(string patientId)
{
    var fhirPatient = _fhirService.GetPatientById(patientId);
    var observations = _fhirService.GetPatientObservations(patientId);
    var input = ConvertFromFhir(fhirPatient, observations);

    var diagnosis = _aiService.PredictDiagnosis(input);
    var treatments = _recommendationEngine.GetTreatments(diagnosis);

    // Log decision to DynamoDB
    var log = new DecisionLog
    {
        PhysicianId = User.Identity.Name,
        Timestamp = DateTime.UtcNow,
        AIRecommendation = diagnosis,
        FinalDecision = diagnosis // Could differ based on user override
    };
    await _dynamoContext.SaveAsync(log);

    ViewBag.Diagnosis = diagnosis;
    ViewBag.Treatments = treatments;
    return View("DiagnosisResult");
}

Conclusion

This article demonstrated my experiences from earlier projects I designed and architected how to build an AI-powered Clinical Decision Support System using C#, .NET, and a multi-database architecture.

  • MongoDB for unstructured and rule-based data
  • DynamoDB for the real-time session and decision-logging
  • Redshift for advanced analytics and reporting

Combined with FHIR integration and AI inference, this approach creates a powerful, scalable, and secure solution for clinical environments.