Best Practices

Best Practices

Follow these best practices to build robust, efficient applications with the Eriksson Beam API.

Connection Management

Always Use using Statements

Ensure proper cleanup of resources:

// Recommended: using statement
using var launcher = await ErikssonBeamLauncher.LaunchErikssonBeam(args);
var client = launcher.Client;
// Resources automatically cleaned up

// Alternative: try-finally
var client = await ErikssonBeamFinder.GetFirstSupportedBeamClient(args);
try
{
    // Work with client
}
finally
{
    client.Dispose();
}

Handle the 10-Minute Timeout

Eriksson Beam stops listening for API connections after 10 minutes:

// If connection fails, prompt user to restart
try
{
    var client = await ErikssonBeamFinder.GetFirstSupportedBeamClient(args);
}
catch (ErikssonBeamNotRunningException)
{
    Console.WriteLine("Cannot connect. Please restart Eriksson Beam and try again.");
}

Use Health Monitoring for Long-Running Applications

var clientArgs = new ErikssonBeamClientArgs
{
    LicenseKey = licenseKey,
    ProcessID = processId,
    EnableHealthMonitoring = true,
    HealthCheckIntervalMs = 5000
};

var client = new ErikssonBeamClient(clientArgs);

client.HealthChanged += (sender, e) =>
{
    if (e.NewHealth.Status == ProcessHealthStatus.Crashed)
    {
        Console.WriteLine("Eriksson Beam crashed! Attempting recovery...");
    }
};

Data Operations

Always Pull Before Push

This is critical to avoid data loss:

// CORRECT: Pull preserves existing data
var design = await client.PullBeamDesignerAsync();
design.DesignCriteria.ProjectInformation.DesignerName = "Jane Doe";
await client.PushBeamDesignerAsync("", design);

// WRONG: Direct creation overwrites EVERYTHING
var design = new BeamDesign();  // Don't do this!
design.DesignCriteria.ProjectInformation.DesignerName = "Jane Doe";
await client.PushBeamDesignerAsync("", design);  // Data loss!

Batch All Changes Before Pushing

Push operations are expensive. Batch modifications:

// CORRECT: One push
var design = await client.PullBeamDesignerAsync();
design.Property1 = value1;
design.Property2 = value2;
design.Property3 = value3;
design.Property4 = value4;
await client.PushBeamDesignerAsync("", design);  // Single push

// WRONG: Multiple pushes (slow!)
design.Property1 = value1;
await client.PushBeamDesignerAsync("", design);
design.Property2 = value2;
await client.PushBeamDesignerAsync("", design);
// ... and so on

Cache Pull Results When Appropriate

If you need to read multiple properties without modification:

// Pull once, read many
var design = await client.PullBeamDesignerAsync();

// Read multiple properties
var projectName = design.DesignCriteria.ProjectInformation.ProjectName;
var designerName = design.DesignCriteria.ProjectInformation.DesignerName;
var sectionType = design.ConcreteExtents?.GetType().Name;
var loadCount = design.StructuralModel.Loading.PointLoads?.Count ?? 0;

// Don't pull again for each property!

Security

Store API License Keys Securely

Never hardcode API license keys:

// CORRECT: Environment variable
var licenseKey = Environment.GetEnvironmentVariable("ERIKSSON_LICENSE_KEY");

// CORRECT: Configuration file (not in source control)
var licenseKey = Configuration["ErikssonBeam:LicenseKey"];

// WRONG: Hardcoded
var licenseKey = "XXXX-XXXX-XXXX-XXXX";  // Don't do this!

Add Configuration Files to .gitignore

# .gitignore
appsettings.Development.json
appsettings.Local.json
*.user

Error Handling

Use Specific Exception Types

try
{
    var design = await client.PullBeamDesignerAsync();
}
catch (ErikssonBeamNotRunningException)
{
    // Handle connection issue
}
catch (ServerErrorException ex) when (ex.Response.ResponseCode == ResponseCode.LicenseError)
{
    // Handle license issue
}
catch (ServerErrorException ex)
{
    // Handle other server errors
}
catch (OperationCanceledException)
{
    // Handle timeout
}

Implement Graceful Degradation

public async Task<BeamDesign> GetDesignOrDefault(ErikssonBeamClient client, string project)
{
    try
    {
        return await client.PullBeamDesignerAsync(project);
    }
    catch (ServerErrorException ex) when (ex.Response.ResponseCode == ResponseCode.InvalidRequest)
    {
        Console.WriteLine($"Project '{project}' not found");
        return null;
    }
}

Performance

Process Files in Batches

For multiple files, consider batch sizes:

public async Task ProcessFilesInBatches(string[] files, int batchSize = 5)
{
    for (int i = 0; i < files.Length; i += batchSize)
    {
        var batch = files.Skip(i).Take(batchSize).ToArray();

        var args = new ErikssonBeamLauncherArgs
        {
            LicenseKey = licenseKey,
            ExecutablePath = executablePath,
            FilesToOpen = batch,
            CloseErikssonBeamOnClientDisconnect = true
        };

        using var launcher = await ErikssonBeamLauncher.LaunchErikssonBeam(args);

        foreach (var file in batch)
        {
            var fileName = Path.GetFileNameWithoutExtension(file);
            await ProcessSingleFile(launcher.Client, fileName);
        }
    }
}

Use Appropriate Timeouts

Adjust timeouts based on operation complexity:

// Quick operations
var quickArgs = new ErikssonBeamConnectionAttemptArgs
{
    LicenseKey = licenseKey,
    IdleTimeoutMs = 15000  // 15 seconds
};

// Complex operations with large files
var longArgs = new ErikssonBeamConnectionAttemptArgs
{
    LicenseKey = licenseKey,
    IdleTimeoutMs = 120000  // 2 minutes
};

Logging

Enable Logging for Debugging

using Microsoft.Extensions.Logging;

var loggerFactory = LoggerFactory.Create(builder =>
{
    builder
        .AddConsole()
        .AddFile("logs/eriksson-beam-{Date}.log")
        .SetMinimumLevel(LogLevel.Debug);
});

var logger = loggerFactory.CreateLogger<MyApplication>();
var launcher = await ErikssonBeamLauncher.LaunchErikssonBeam(args, logger);

Log Important Operations

logger.LogInformation("Starting batch processing of {Count} files", files.Length);

foreach (var file in files)
{
    logger.LogDebug("Processing file: {FileName}", file);

    try
    {
        await ProcessFile(client, file);
        logger.LogInformation("Successfully processed: {FileName}", file);
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "Failed to process: {FileName}", file);
    }
}

Testing

Use Test API License Keys in Development

#if DEBUG
    // Debug mode may allow TEST_KEY
    var licenseKey = "TEST_KEY";
#else
    var licenseKey = Environment.GetEnvironmentVariable("ERIKSSON_LICENSE_KEY");
#endif

Create Integration Tests

[Test]
public async Task PullPush_PreservesData()
{
    using var launcher = await ErikssonBeamLauncher.LaunchErikssonBeam(testArgs);
    var client = launcher.Client;

    // Pull original
    var original = await client.PullBeamDesignerAsync();
    var originalName = original.DesignCriteria.ProjectInformation.ProjectName;

    // Modify and push
    original.DesignCriteria.ProjectInformation.DesignerName = "Test";
    await client.PushBeamDesignerAsync("", original);

    // Pull again and verify
    var updated = await client.PullBeamDesignerAsync();
    Assert.AreEqual(originalName, updated.DesignCriteria.ProjectInformation.ProjectName);
    Assert.AreEqual("Test", updated.DesignCriteria.ProjectInformation.DesignerName);
}

See Also