Table of Contents

DBTools.Core

DBTools.Core is the foundational library providing shared infrastructure for all DBTools components. It contains Revit execution abstractions, transaction management, safe execution patterns, logging infrastructure, settings management, tool module system, and shared UI components.

Overview

DBTools.Core serves as the central infrastructure layer, abstracting away Revit API complexities while providing consistent patterns for error handling, logging, and dependency injection across all tools.

Property Value
Assembly Name DBTools.Core.dll
Target Frameworks net48, net8.0-windows
WPF Support Yes (UseWPF=true)
Nullable Enabled

Source: src/DBTools.Core/DBTools.Core.csproj:1-10

Responsibilities

  1. Revit Execution Abstraction - Thread-safe API access via call gates and run scopes
  2. Transaction Management - Unified transaction/subtransaction handling with automatic rollback
  3. Safe Execution - Centralized error handling with user notification and logging
  4. Logging Infrastructure - Serilog-based structured logging with UI sink
  5. Settings System - Type-safe options pattern with persistence and validation
  6. Tool Module System - Discovery, registration, and lifecycle management for tools
  7. Shared UI Components - Window bases, alert dialogs, progress overlays, and behaviors

Key Components

Revit Execution Layer

IRevitCallGate

The unified entry point for executing Revit API callbacks, supporting both inline (modal) and queued (modeless) execution modes.

public interface IRevitCallGate
{
    bool InGate { get; }
    Task<T> RunAsync<T>(Func<UIApplication, T> work, CancellationToken ct = default,
        RevitCallMode mode = RevitCallMode.Auto);
    Task RunAsync(Action<UIApplication> work, CancellationToken ct = default,
        RevitCallMode mode = RevitCallMode.Auto);
}

Source: src/DBTools.Core/Revit/Execution/IRevitCallGate.cs:8-28

Execution Modes:

Mode Description
Auto Execute inline if valid context exists; otherwise queue
InlineOnly Execute inline only; throw if context unavailable
ForceQueue Always queue via ExternalEvent/RevitTask
RequiresActiveView Require an active UIDocument before executing

Source: src/DBTools.Core/Revit/Execution/IRevitCallGate.cs:30-51

IRevitRunScope

Represents a single Revit run scope (command, modeless session, or test). Owns UI/document context plus call gate and transaction services.

public interface IRevitRunScope
{
    UIApplication UIApplication { get; }
    IRevitCallGate CallGate { get; }
    ITransactionRunner TransactionRunner { get; }
    ITransactionGroupService TransactionGroupService { get; }
    UIDocument GetActiveUiDocument();
    Document GetActiveDocument();
    Document GetLockedDocument(Document expectedDocument, string? context = null);
}

Source: src/DBTools.Core/Revit/Execution/IRevitRunScope.cs:11-20

Run Scope Profiles:

Profile Use Case Implementation
InlineUi Modal commands and RevitTest ModalInlineCallGate + CallGateTransactionRunner
QueuedModeless Long-running/modeless tools ModelessQueuedCallGate + RevitTaskService

Source: src/DBTools.Core/Revit/Execution/IRevitRunScope.cs:22-36

ModalInlineCallGate

Call-gate implementation that executes delegates inline on the Revit UI thread using a supplied UIApplication. Used for modal command execution.

public sealed class ModalInlineCallGate : IRevitCallGate
{
    public ModalInlineCallGate(UIApplication uiapp) { ... }
    public bool InGate => _inGate.Value;
    public Task<T> RunAsync<T>(Func<UIApplication, T> work, ...) { ... }
}

Source: src/DBTools.Core/Revit/Execution/ModalInlineCallGate.cs:10-74

Transaction Management

ITransactionRunner

Provides a unified API for executing Revit model modifications with automatic transaction management.

public interface ITransactionRunner
{
    Task RunAsync(string name, Action action, CancellationToken ct = default);
    Task<T> RunAsync<T>(string name, Func<T> action, CancellationToken ct = default);
    Task RunAsync(string name, Action<Document> action, CancellationToken ct = default);
    Task<T> RunAsync<T>(string name, Func<Document, T> action, CancellationToken ct = default);
    // Additional overloads with UIApplication access and explicit Document
}

Source: src/DBTools.Core/Revit/Transactions/ITransactionRunner.cs:6-53

CallGateTransactionRunner

The primary implementation that wraps all Revit API calls in properly managed transactions:

  • Auto-selects transaction type: Uses SubTransaction if doc.IsModifiable (already in transaction), otherwise creates new Transaction
  • Cross-document protection: Prevents starting transactions on Document B while Document A is modifiable
  • Failure handling: Attaches SilentFailuresPreprocessor to suppress non-critical Revit warnings

Source: src/DBTools.Core/Revit/Transactions/CallGateTransactionRunner.cs:9-390

Usage Example:

// Basic modification with automatic transaction
await _transactionRunner.RunAsync("Create Wall", doc =>
{
    Wall.Create(doc, curve, levelId, false);
});

// Get a result from a transaction
var wallId = await _transactionRunner.RunAsync("Create Wall", doc =>
{
    var wall = Wall.Create(doc, curve, levelId, false);
    return wall.Id;
});

Source: src/DBTools.Core/Revit/Transactions/CallGateTransactionRunner.cs:64-79

ITransactionGroupService

Manages transaction groups for operations requiring multiple undoable transactions to appear as a single undo item.

public interface ITransactionGroupService
{
    bool IsActive { get; }
    Task BeginAsync(string name, CancellationToken ct = default);
    Task BeginAsync(Document document, string name, CancellationToken ct = default);
    Task FinalizeAsync(bool commit, CancellationToken ct = default);

    Task<bool> IsGroupHealthyAsync(CancellationToken ct = default);
    Task RunAsync(string name, bool commit, Func<Task> work, CancellationToken ct = default);
    Task RunAsync(Document document, string name, bool commit, Func<Task> work, CancellationToken ct = default);

    // Atomic grouping: Begin + Work + Finalize executed within a single call-gate callback.
    // Work must complete synchronously (no async yields) or a spec failure is thrown.
    Task RunAtomicAsync(string name, bool commit, Func<CancellationToken, Task> work, CancellationToken ct = default);
}

Source: src/DBTools.Core/Revit/Transactions/ITransactionGroupService.cs:1

Safe Execution

ISafeExecutor

The central error handling service. All tool entrypoints must execute within ISafeExecutor.

public interface ISafeExecutor
{
    Task RunAsync(Func<Task> action, ILogger logger, IErrorNotifier? notifier = null,
        CancellationToken ct = default);
    Task RunAsync(Func<Task> action, ILogger logger, IErrorNotifier? notifier,
        SafeExecutor.SafeExecuteOptions? opts, CancellationToken ct = default);
}

Source: src/DBTools.Core/Execution/ISafeExecutor.cs:6-16

SafeExecutor Implementation

Provides comprehensive error handling:

  • Correlation IDs: Each execution gets a unique GUID for log correlation
  • Timing: Tracks elapsed milliseconds for performance analysis
  • Exception logging: Logs full exception details including inner exceptions and XAML parse context
  • User notification: Shows error banners via IErrorNotifier
  • Debug mode: Automatically enables debug mode and shows logger window on fatal errors
  • Lifecycle hooks: Optional OnSuccessAsync, OnCancelAsync, OnErrorAsync callbacks

Source: src/DBTools.Core/Execution/SafeExecutor.cs:12-467

SafeExecuteOptions:

public sealed class SafeExecuteOptions
{
    public string? Name { get; set; }
    public bool LogStart { get; set; } = true;
    public bool ShowCompletionToUser { get; set; }
    public Guid? CorrelationId { get; set; }
    public Func<Task>? OnSuccessAsync { get; set; }
    public Func<Task>? OnCancelAsync { get; set; }
    public Func<Exception, Task>? OnErrorAsync { get; set; }
    public NotifyKindKind NotifyKind { get; set; } = NotifyKindKind.Success;
}

Source: src/DBTools.Core/Execution/SafeExecutor.cs:451-466

DbtToolCommand Base Class

Abstract base class for Revit commands that enforces ISafeExecutor usage, run-scope creation, and centralized error notification.

public abstract class DbtToolCommand : IExternalCommand
{
    public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
    {
        // Wraps execution in ISafeExecutor, creates run scope
    }
    
    protected abstract Task RunAsync(IDbtToolContext context);
    
    // Convenience methods
    protected static T GetService<T>(IDbtToolContext context) where T : notnull;
    protected static Task<T> RunInRevitAsync<T>(IDbtToolContext context, Func<UIApplication, T> work, ...);
    protected static bool? ShowToolWindow<TWindow>(Func<TWindow> windowFactory) where TWindow : Window;
}

Source: src/DBTools.Core/Revit/Execution/DbtToolCommand.cs:35-238

Tool Context

IDbtToolContext

Ambient context provided to DBT tool commands, wrapping the current Revit run scope, DI scope, and shared services.

public interface IDbtToolContext
{
    ExternalCommandData CommandData { get; }
    UIApplication UIApplication { get; }
    UIDocument UIDocument { get; }
    Document Document { get; }
    IAppRunScope RunScope { get; }
    IRevitRunScope RevitRunScope { get; }
    ILogger Logger { get; }
    IErrorNotifier ErrorNotifier { get; }
    RevitRunScopeProfile RunProfile { get; }
    T Resolve<T>() where T : notnull;
}

Source: src/DBTools.Core/Revit/Execution/IDbtToolContext.cs:14-27

Runtime

AppRuntime

Static accessor for the application runtime. Provides global access to services when DI is not available (e.g., Revit's parameterless constructor requirement).

public static class AppRuntime
{
    public static bool IsInitialized { get; }
    public static void Initialize(IAppRuntime runtime);
    public static void InitializeIfNeeded(Func<IAppRuntime> factory);
    public static ILogger Logger { get; }
    public static ISettingsProvider Settings { get; }
    public static IAppRunScope CreateRunScope(UIApplication uiapp, RevitRunScopeProfile profile);
    public static T Resolve<T>() where T : notnull;
}

Source: src/DBTools.Core/Runtime/AppRuntime.cs:8-155

IAppRuntime

Interface for the application runtime container.

public interface IAppRuntime
{
    ILogger Logger { get; }
    ISettingsProvider Settings { get; }
    IAppRunScope CreateRunScope(UIApplication uiapp, RevitRunScopeProfile profile);
    T Resolve<T>() where T : notnull;
}

Source: src/DBTools.Core/Runtime/IAppRuntime.cs:8-16

IAppRunScope

Scoped DI container for per-command lifetimes.

public interface IAppRunScope : IDisposable
{
    T Resolve<T>() where T : notnull;
}

Source: src/DBTools.Core/Runtime/IAppRunScope.cs:5-8

Logging Infrastructure

IDbtLoggingHost

Central logging host that implements ILoggerFactory directly.

public interface IDbtLoggingHost : ILoggerFactory, IDisposable
{
    SerilogUiSink UiSink { get; }
    bool IsDebugMode { get; }
    void SetDebugMode(bool enabled);
    ILoggerFactory LoggerFactory { get; }
    string InstanceId { get; }
    int? RevitYear { get; }
    string? LogFilePath { get; }
    string ForceNewLogFileWithAutoSuffix();
    string ForceNewLogFile(string suffix);
}

Source: src/DBTools.Core/Logging/Features/DbtLoggingHost.cs:22-47

DbtLoggingHost Implementation

Features:

  • Serilog integration: Uses Serilog with structured logging
  • UI sink: Feeds log entries to the Logger Window
  • File sink: Custom DbtTextFileSink avoids version conflicts with Serilog.Sinks.File
  • Manual in-session rollover: Logger UI can force file logging to switch to a new file path on demand
  • Thread enrichment: Adds thread ID to all log entries
  • Instance identification: Unique instance ID per Revit process
  • Log rotation: Automatic cleanup of old log files by age, count, and total size

Source: src/DBTools.Core/Logging/Features/DbtLoggingHost.cs:49-302, src/DBTools.Core/Logging/Features/DbtTextFileSink.cs:91-118

Log File Naming:

dbtools-{revitYear}-{instanceId}-{timestamp}-{nonce}.log

Manual rollovers append the generated suffix before .log:

dbtools-{revitYear}-{instanceId}-{timestamp}-{nonce}-manual-{timestamp}.log

Source: src/DBTools.Core/Logging/Features/DbtLoggingHost.cs:111-123, src/DBTools.Core/Logging/Features/DbtLoggingHost.cs:150-182

Settings System

ISettingsProvider

Interface for reading and persisting settings.

public interface ISettingsProvider
{
    TSettings Get<TSettings>() where TSettings : class, new();
    Task SaveAsync<TSettings>(string section, TSettings settings, CancellationToken ct = default)
        where TSettings : class;
}

Source: src/DBTools.Core/Settings/ISettingsProvider.cs:1-8

DbtSettingsRegistry

Registry for settings pack definitions, enabling the Settings Window to discover and display tool-specific settings.

public sealed class DbtSettingsRegistry
{
    public IReadOnlyCollection<IDbtSettingsPackDefinition> Definitions { get; }
    public void Register(IDbtSettingsPackDefinition definition);
    public bool Unregister(string key);
    public IEnumerable<IDbtSettingsPackDefinition> ForPanel(string ribbonPanel);
    public IEnumerable<IDbtSettingsWarningDefinition> WarningDefinitions();
}

Source: src/DBTools.Core/Settings/DbtSettingsRegistry.cs:8-62

DbtToolSettingsPack

Generic container for tool settings with validation support.

public sealed class DbtToolSettingsPack<TOptions> : IDbtToolSettingsPack
{
    public string Key { get; }
    public string Title { get; }
    public string RibbonPanel { get; }
    public Type OptionsType { get; }
    public object Context { get; }
    public object CreateDefaultOptions();
    public object BuildOptionsSnapshot();
    public Task<DbtSettingsValidationResult> ValidateAsync(CancellationToken ct = default);
    public FrameworkElement View { get; }
}

Source: src/DBTools.Core/Settings/DbtToolSettingsPack.cs:37-85

Tool Module System

DbtToolModule

Base class for tool modules to contribute DI services and tool registrations.

public abstract class DbtToolModule
{
    public virtual void RegisterSettings(IServiceCollection services, IConfiguration configuration,
        DbtToolManifest manifest) { }
    public virtual void RegisterServices(IServiceCollection services, DbtToolManifest manifest) { }
    public virtual void RegisterSettingsPacks(IServiceCollection services, DbtToolManifest manifest) { }
    public virtual void RegisterHooks(DbtToolRegistry registry, DbtToolManifest manifest) { }
}

Source: src/DBTools.Core/Tools/DbtToolModule.cs:9-49

DbtToolRegistry

Central registry for tool registrations and hook handlers.

public sealed class DbtToolRegistry
{
    public ReadOnlyCollection<DbtToolRegistration> Tools { get; }
    public IReadOnlyDictionary<Type, IReadOnlyCollection<Type>> HookRegistrations { get; }
    public void RegisterTool(DbtToolRegistration registration);
    public void RegisterHook<THook, TImplementation>() where THook : class where TImplementation : class, THook;
    public Type[] GetHookImplementations<THook>() where THook : class;
}

Source: src/DBTools.Core/Tools/DbtToolRegistry.cs:9-83

DbtToolRegistration

Encapsulates a tool command registration with ribbon configuration.

public sealed class DbtToolRegistration
{
    public Type CommandType { get; }
    public Type? AvailabilityType { get; }
    public RevitRunScopeProfile RunProfile { get; }
    public DbtToolRibbonSpec Ribbon { get; }
    public Func<Flags, bool>? AvailabilityPredicate { get; init; }
}

Source: src/DBTools.Core/Tools/DbtToolRegistration.cs:8-38

DbtToolManifest

YAML manifest structure for tool discovery.

public sealed class DbtToolManifest
{
    public string Id { get; set; }
    public string Assembly { get; set; }
    public string ModuleType { get; set; }
    public int? Order { get; set; }
    public List<DbtSandboxWindowManifest>? SandboxWindows { get; set; }
    public DbtToolMetadataManifest? Tool { get; set; }
}

Source: src/DBTools.Core/Tools/DbtToolManifest.cs:5-13

DbtHookHost

Coordinates application-level hooks, dispatching events to registered handlers.

Supported Hook Interfaces:

  • IViewActivatedHookHandler - Called when views are activated
  • IContextualRibbonInjector - For contextual ribbon panel initialization
public sealed class DbtHookHost
{
    public void Attach(UIControlledApplication application);
    public Task AttachAsync(UIControlledApplication application, CancellationToken ct);
    public void Detach(UIControlledApplication application);
}

Source: src/DBTools.Core/Tools/DbtHookHost.cs:28-244

UI Components

DbtWindowBase

Modal-first base window for DBT. Hosts the progress overlay and central UI error boundary.

public class DbtWindowBase : Window, IWindowWithOwnerProvider, IThemeOptOut
{
    public IWindowOwnerProvider? OwnerProvider { get; set; }
    public bool UseDefaultTheme { get; set; }
}

Source: src/DBTools.Core/UI/Windows/DbtWindowBase.cs:27-47

IAlertService

Service for showing alert dialogs with various body types.

public interface IAlertService
{
    AlertResult Show(AlertRequest request);
    bool Confirm(string message, string title = "DB Tools");
    T? SelectSingle<T>(IEnumerable<T> items, Func<T, string> displayFunc, ...);
    IReadOnlyList<T> SelectMultiple<T>(IEnumerable<T> items, Func<T, string> displayFunc, ...);
}

Source: src/DBTools.Core/Alerts/Features/IAlertService.cs:1-30

IErrorNotifier

Interface for displaying error/success banners to users.

public interface IErrorNotifier
{
    void ShowBanner(string title, string message);         // Error (red)
    void ShowWarningBanner(string title, string message);  // Warning (yellow/orange)
    void ShowSuccessBanner(string title, string message);  // Success (green)
}

Source: src/DBTools.Core/Notifications/Features/IErrorNotifier.cs:1-18

Dependencies

NuGet Packages

Package Purpose
Newtonsoft.Json JSON serialization
CSharpFunctionalExtensions Result types and functional patterns
Ardalis.GuardClauses Argument validation
AutoMapper Object mapping
UnitsNet Units and measurements
Serilog + extensions Structured logging
Microsoft.Extensions.DependencyInjection Dependency injection
Microsoft.Extensions.Configuration.* Configuration binding
CommunityToolkit.Mvvm MVVM base classes
DynamicData Reactive collections
System.Reactive Reactive extensions
Ookii.Dialogs.Wpf Native file dialogs
ricaun.Revit.UI.Tasks Revit async task support

Source: src/DBTools.Core/DBTools.Core.csproj:102-161

Project References

Project Purpose
DBTools.Themes WPF theme resources

Source: src/DBTools.Core/DBTools.Core.csproj:153

Directory Structure

DBTools.Core/
+-- Assets/           # Shared resources (icons)
+-- Compat/           # Cross-framework compatibility utilities
+-- Constants/        # Application-wide constants
+-- Execution/        # Safe execution, error handling
+-- Hosting/          # Service hosting infrastructure
+-- IO/               # File system utilities
+-- Logging/          # Serilog infrastructure, UI sink
+-- Notifications/    # Banner notifications
+-- Revit/
|   +-- Context/      # Revit context accessors
|   +-- Execution/    # Call gates, run scopes, tool context
|   +-- Transactions/ # Transaction runners and groups
|   +-- Utilities/    # Revit helpers (RevitId, Failures, etc.)
+-- Runtime/          # AppRuntime, IAppRunScope
+-- Sandbox/          # Sandbox mode stubs
+-- Settings/         # Settings infrastructure
+-- Tools/            # Tool module system
+-- UI/
    +-- Alerts/       # Alert dialog system
    +-- Behaviors/    # WPF attached behaviors
    +-- Converters/   # Value converters
    +-- Icons/        # Icon loading
    +-- Progress/     # Progress overlay
    +-- Theming/      # Theme validation
    +-- Windows/      # Window base classes

Public API Summary

Revit Execution

  • IRevitCallGate - Thread-safe Revit API execution
  • IRevitRunScope - Command execution scope
  • IRevitRunScopeFactory - Factory for run scopes
  • DbtToolCommand - Base class for Revit commands
  • IDbtToolContext - Command context interface

Transaction Management

  • ITransactionRunner - Transaction execution
  • ITransactionGroupService - Transaction grouping

Safe Execution

  • ISafeExecutor - Centralized error handling
  • IErrorNotifier - User notification

Logging

  • IDbtLoggingHost - Logging host interface
  • ILogSink - Custom sink interface

Settings

  • ISettingsProvider - Settings access
  • DbtSettingsRegistry - Settings pack registry
  • IDbtToolSettingsPack - Settings pack interface

Tool System

  • DbtToolModule - Tool module base class
  • DbtToolRegistry - Tool registration
  • DbtToolManifest - Manifest structure
  • DbtHookHost - Hook coordination

UI

  • DbtWindowBase - Window base class
  • IAlertService - Alert dialogs
  • IWindowOwnerProvider - Window ownership

Usage Examples

Creating a Tool Command

public class MyToolCommand : DbtToolCommand
{
    protected override async Task RunAsync(IDbtToolContext context)
    {
        var myService = context.Resolve<IMyService>();
        
        // Execute Revit API work
        await context.RevitRunScope.TransactionRunner.RunAsync("My Operation", doc =>
        {
            // Modify document
        });
    }
}

Using the Call Gate

// Get element data from Revit (thread-safe)
var result = await callGate.RunAsync(uiapp =>
{
    var doc = uiapp.ActiveUIDocument.Document;
    return doc.GetElement(elementId);
}, ct, RevitCallMode.RequiresActiveView);

Registering a Tool Module

public class MyToolModule : DbtToolModule
{
    public override void RegisterServices(IServiceCollection services, DbtToolManifest manifest)
    {
        services.AddSingleton<IMyService, MyService>();
    }
    
    public override void RegisterHooks(DbtToolRegistry registry, DbtToolManifest manifest)
    {
        registry.RegisterHook<IViewActivatedHookHandler, MyViewHandler>();
    }
}

Extension Points

  1. Custom Call Gates - Implement IRevitCallGate for specialized execution modes
  2. Hook Handlers - Register implementations of IViewActivatedHookHandler or IContextualRibbonInjector
  3. Settings Packs - Create DbtToolSettingsPack<T> instances for tool-specific settings UI
  4. Alert Bodies - Implement IAlertBody for custom alert dialog content

Build Considerations

Conditional Compilation

  • Sandbox builds (DBT_IsSandboxBuild): Revit-dependent files excluded, sandbox stubs used
  • Designer builds (DBT_IsDesignerBuild): Revit sources excluded for XAML designer
  • wpftmp builds: Special handling for temporary projects during BAML compilation

Source: src/DBTools.Core/DBTools.Core.csproj:12-99

InternalsVisibleTo

Test projects and DBTools.App have access to internal types:

  • DBTools.App
  • DBTools.GM.Tests
  • DBTools.RecordSet.Tests
  • DBTools.SGT.Tests
  • DBTools.TDV.Tests

Source: src/DBTools.Core/DBTools.Core.csproj:26-33


Verification Status

Check Status
Source anchors for all claims Yes
UNVERIFIED markers where needed No (all verified)
Cross-references added Yes
Examples tested N/A
No assumptions without evidence Yes

Verified by: docs-run-20260124-020412
Date: 2026-01-24