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
- Revit Execution Abstraction - Thread-safe API access via call gates and run scopes
- Transaction Management - Unified transaction/subtransaction handling with automatic rollback
- Safe Execution - Centralized error handling with user notification and logging
- Logging Infrastructure - Serilog-based structured logging with UI sink
- Settings System - Type-safe options pattern with persistence and validation
- Tool Module System - Discovery, registration, and lifecycle management for tools
- 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
SubTransactionifdoc.IsModifiable(already in transaction), otherwise creates newTransaction - Cross-document protection: Prevents starting transactions on Document B while Document A is modifiable
- Failure handling: Attaches
SilentFailuresPreprocessorto 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,OnErrorAsynccallbacks
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
DbtTextFileSinkavoids 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 activatedIContextualRibbonInjector- 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 executionIRevitRunScope- Command execution scopeIRevitRunScopeFactory- Factory for run scopesDbtToolCommand- Base class for Revit commandsIDbtToolContext- Command context interface
Transaction Management
ITransactionRunner- Transaction executionITransactionGroupService- Transaction grouping
Safe Execution
ISafeExecutor- Centralized error handlingIErrorNotifier- User notification
Logging
IDbtLoggingHost- Logging host interfaceILogSink- Custom sink interface
Settings
ISettingsProvider- Settings accessDbtSettingsRegistry- Settings pack registryIDbtToolSettingsPack- Settings pack interface
Tool System
DbtToolModule- Tool module base classDbtToolRegistry- Tool registrationDbtToolManifest- Manifest structureDbtHookHost- Hook coordination
UI
DbtWindowBase- Window base classIAlertService- Alert dialogsIWindowOwnerProvider- 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
- Custom Call Gates - Implement
IRevitCallGatefor specialized execution modes - Hook Handlers - Register implementations of
IViewActivatedHookHandlerorIContextualRibbonInjector - Settings Packs - Create
DbtToolSettingsPack<T>instances for tool-specific settings UI - Alert Bodies - Implement
IAlertBodyfor 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.AppDBTools.GM.TestsDBTools.RecordSet.TestsDBTools.SGT.TestsDBTools.TDV.Tests
Source:
src/DBTools.Core/DBTools.Core.csproj:26-33
Related Documentation
- Architecture Overview - System-wide architecture
- Project References - Inter-project dependencies
- DBTools.Loader - Revit entry point
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