Knowledge Base

SDK Documentation

DicomProxyCore SDK — core classes, service interfaces, data models, dependency injection, and image processing.

The DicomProxyCore SDK provides libraries and tools for integrating directly with the DICOM Router/PACS system from .NET applications.

SDK components

DicomProxyCore SDK
├── DicomProxyCore.dll
├── DicomProxyCore.Models.dll
├── DicomProxyCore.Services.dll
├── DicomProxyCore.Dicom.dll
├── DicomProxyCore.Commands.dll
└── DicomProxyCore.Imaging.SkiaSharp.dll

Dependencies: .NET 9.0, FellowOak DICOM 5.2.2, SkiaSharp, SQLite, NLog, ASP.NET Core, Jint, LazyCache.

NuGet installation

<PackageReference Include="DicomProxyCore" Version="1.09.1" />
<PackageReference Include="DicomProxyCore.Models" Version="1.09.1" />
<PackageReference Include="DicomProxyCore.Services" Version="1.09.1" />

Core classes

ConfigurationBase

Configuration.Setup("C:\\MyApp", false);
var config = Configuration.Instance;

string storagePath = config.StoragePath;
int dicomPort = config.DicomPort;
string aeTitle = config.ThisModality.AE_Title;

config.DicomPort = 11112;
config.Save();

DicomProxyService

public class MyDicomService
{
    private DicomProxyService _proxyService;

    public void Initialize()
    {
        _proxyService = new DicomProxyService("C:\\MyApp", false);
        _proxyService.Start();
    }

    public void Shutdown() => _proxyService?.Stop();
}

Custom CStoreSCP

public class CustomCStoreSCP : CStoreSCP
{
    public override DicomCStoreResponse OnCStoreRequest(DicomCStoreRequest request)
    {
        var dataset = request.Dataset;
        var studyUID = dataset.GetSingleValue<string>(DicomTag.StudyInstanceUID);
        var patientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, "Unknown");

        // Custom processing...

        return new DicomCStoreResponse(request, DicomStatus.Success);
    }
}

Data models

// Connection (PACS server)
var server = new Connection
{
    Id = "pacs1",
    AE_Title = "MAINPACS",
    Hostname = "192.168.1.100",
    Port = 11112,
    Enabled = true,
    Priority = 1
};

// DICOM query
var query = new DicomQuery
{
    Level = QueryLevel.Study,
    PatientName = "DOE^JOHN",
    StudyDate = "20240315",
    Modality = "CT"
};

Service interfaces

IStorageManager

public class CustomStorageManager : IStorageManager
{
    public string GetInstancePath(string studyUID, string seriesUID, string instanceUID)
        => Path.Combine(Configuration.Instance.StoragePath, "studies", studyUID, seriesUID, $"{instanceUID}.dcm");

    public async Task<bool> StoreInstanceAsync(DicomDataset dataset)
    {
        var studyUID = dataset.GetSingleValue<string>(DicomTag.StudyInstanceUID);
        var seriesUID = dataset.GetSingleValue<string>(DicomTag.SeriesInstanceUID);
        var instanceUID = dataset.GetSingleValue<string>(DicomTag.SOPInstanceUID);

        var path = GetInstancePath(studyUID, seriesUID, instanceUID);
        Directory.CreateDirectory(Path.GetDirectoryName(path));
        await new DicomFile(dataset).SaveAsync(path);
        return true;
    }
}

IMessaging

public class CustomMessaging : IMessaging
{
    public void SendMessage(string message, MessageType type = MessageType.Info)
    {
        MessageReceived?.Invoke(this, new MessageEventArgs
        {
            Message = message, Type = type, Timestamp = DateTime.Now
        });
    }
    public void Start() { }
    public void Stop() { }
}

Dependency injection

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IStorageManager, CustomStorageManager>();
    services.AddSingleton<IMessaging, CustomMessaging>();
    services.AddSingleton<ILicenseManager, CustomLicenseManager>();
    services.AddSingleton(Configuration.Instance);
    services.AddLogging(b => b.AddNLog("nlog.config"));
}

DICOM processing

public async Task ProcessDicomFile(string filePath)
{
    var dicomFile = await DicomFile.OpenAsync(filePath);
    var dataset = dicomFile.Dataset;

    var studyUID = dataset.GetSingleValue<string>(DicomTag.StudyInstanceUID);
    var modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, "");

    // Convert to JSON
    var json = new DicomJson2().WriteToString(dataset);

    // Image processing (if pixel data present)
    if (dataset.Contains(DicomTag.PixelData))
    {
        var image = new DicomImage(dataset);
        using var bitmap = image.RenderImage().AsClonedBitmap();
        // ...
    }
}

Image processing (SkiaSharp)

public async Task<byte[]> ConvertToJpegAsync(DicomDataset dataset, int quality = 90)
{
    using var image = new DicomImage(dataset);
    using var bitmap = image.RenderImage().AsClonedBitmap();
    using var stream = new MemoryStream();
    using var skBitmap = SKBitmap.FromImage(bitmap);
    skBitmap.Encode(SKEncodedImageFormat.Jpeg, quality).SaveTo(stream);
    return stream.ToArray();
}

JavaScript scripting (Jint)

public async Task<bool> ValidateDataAsync(DicomDataset dataset)
{
    var engine = new Engine();
    engine.SetValue("getTag", new Func<string, string>(tagName =>
        dataset.GetSingleValueOrDefault(DicomTag.Parse(tagName), "")));

    var result = engine.Evaluate(@"
        var patientName = getTag('00100010');
        var studyUID    = getTag('0020000D');
        patientName && patientName.trim() !== '' &&
        studyUID    && studyUID.trim()    !== '';
    ");

    return Convert.ToBoolean(result.ToObject());
}

Best practices

  1. Configuration — use strongly-typed config objects; support environment-specific overrides; keep secrets out of JSON.
  2. Error handling — global exception handling; structured logging with correlation IDs; retry policies for transient failures.
  3. Performance — connection pooling; async/await throughout; cache frequently accessed data; monitor memory and disposal.
  4. Security — validate all inputs; use TLS; authenticate API calls; log security events.
  5. Testing — unit-test core logic; integration-test DICOM operations; load-test high-throughput paths.

Docker

FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app
EXPOSE 8080
EXPOSE 11112

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["MyApp.csproj", "."]
RUN dotnet restore && dotnet build -c Release -o /app/build

FROM build AS publish
RUN dotnet publish -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]

See also: Examples & Tutorials