Source of truth (2026)
  • DataManagementEngineStandard/Editor/UOW/UnitOfWorkFactory.cs (517 lines)

Overview

UnitOfWorkFactory is a static factory for creating UnitofWork<T> instances when the entity type is determined at runtime. It provides:

  • Type caching - Caches resolved types and constructors for performance
  • Validation - Validates entity types before creating UnitOfWork instances
  • Batch operations - Create multiple UnitOfWork instances in one call
  • Assembly scanning - Finds types across all loaded assemblies
  • Error handling - Try-pattern methods that don't throw exceptions

For compile-time known types, use UnitofWork<T> constructors directly.

Architecture

public static class UnitOfWorkFactory
{
    // Internal caches
    private static readonly ConcurrentDictionary<string, Type> _typeCache;
    private static readonly ConcurrentDictionary<string, ConstructorInfo> _constructorCache;
    private static readonly ConcurrentDictionary<string, Type[]> _assemblyTypeCache;

    // Configuration
    public static class Configuration { ... }

    // Factory Methods
    public static IUnitOfWorkWrapper CreateUnitOfWork(...);
    public static IUnitOfWorkWrapper CreateWrapper(...);
    public static bool TryCreateUnitOfWork(...);

    // Batch Operations
    public static Dictionary<string, IUnitOfWorkWrapper> CreateUnitOfWorkBatch(...);
    public static IUnitOfWorkWrapper CreateUnitOfWorkFromInstance(...);

    // Utilities
    public static void ClearCaches();
    public static CacheStatistics GetCacheStatistics();
}

Configuration

The Configuration nested class controls factory behavior:

PropertyTypeDefaultDescription
EnableCachingbooltrueCache resolved types and constructors. Disable for memory-constrained environments.
EnableValidationbooltrueValidate entity types (not abstract, not interface, has parameterless constructor).
MaxCacheSizeint500Maximum entries in type and constructor caches before eviction.

Example

// Disable caching for testing
UnitOfWorkFactory.Configuration.EnableCaching = false;

// Increase cache size for large applications
UnitOfWorkFactory.Configuration.MaxCacheSize = 2000;

Factory Methods

CreateUnitOfWork (Type-based)

public static IUnitOfWorkWrapper CreateUnitOfWork(
    Type entityType,
    IDMEEditor dmeEditor,
    string datasourceName,
    string entityName,
    string primaryKey = null)

Creates a UnitOfWork when the entity type is known at runtime. This is the primary factory method.

  • entityType - The runtime-determined entity type (must have parameterless constructor)
  • dmeEditor - The IDMEEditor instance for configuration and datasources
  • datasourceName - Registered datasource name in ConfigEditor
  • entityName - Entity/table name in the datasource
  • primaryKey - Optional primary key field name

CreateUnitOfWork (with EntityStructure)

public static IUnitOfWorkWrapper CreateUnitOfWork(
    Type entityType,
    IDMEEditor dmeEditor,
    string datasourceName,
    string entityName,
    EntityStructure entityStructure,
    string primaryKey = null)

Overload that accepts an explicit EntityStructure for scenarios where schema metadata is pre-loaded.

CreateWrapper

public static IUnitOfWorkWrapper CreateWrapper(object unitOfWork)

Wraps an existing UnitOfWork instance. If the object is already an IUnitOfWorkWrapper, returns it directly.

TryCreateUnitOfWork

public static bool TryCreateUnitOfWork(
    Type entityType,
    IDMEEditor dmeEditor,
    string datasourceName,
    string entityName,
    out IUnitOfWorkWrapper wrapper,
    out string errorMessage,
    string primaryKey = null)

Non-throwing version. Returns false on failure with error message.

Legacy String-based Methods

// From type name (scans loaded assemblies)
public static IUnitOfWorkWrapper CreateUnitOfWorkWrapper(
    string entityTypeName, IDMEEditor dmeEditor, 
    string datasourceName, string entityName, string primaryKey = null);

// Legacy compatibility
public static object CreateUnitOfWork(string entityTypeName, ...);
public static object GetUnitOfWork(string entityName, string dataSourceName, IDMEEditor dmeEditor);

Type Resolution

The factory resolves types through a cascading search:

  1. Type.GetType() - Checks if fully qualified name resolves directly
  2. Assembly scan - Searches all loaded assemblies for matching type name
  3. Assembly type cache - Caches GetTypes() per assembly to avoid repeated expensive calls

Type resolution is cached when EnableCaching is true.

Batch Operations

CreateUnitOfWorkBatch

public static Dictionary<string, IUnitOfWorkWrapper> CreateUnitOfWorkBatch(
    Dictionary<Type, string> entityTypes, 
    IDMEEditor dmeEditor, 
    string datasourceName)

Creates multiple UnitOfWork instances in one call. Useful for configuration-driven scenarios.

var entityTypes = new Dictionary<Type, string>
{
    [typeof(Customer)] = "Customers",
    [typeof(Order)] = "Orders",
    [typeof(Product)] = "Products"
};

var uowSet = UnitOfWorkFactory.CreateUnitOfWorkBatch(
    entityTypes, editor, "MyDatabase");

// Access individual UnitOfWork instances
var customerUow = uowSet["Customers"];
var orderUow = uowSet["Orders"];

CreateUnitOfWorkFromInstance

public static IUnitOfWorkWrapper CreateUnitOfWorkFromInstance(
    object entityInstance, 
    IDMEEditor dmeEditor, 
    string datasourceName, 
    string entityName = null)

Creates UnitOfWork from an existing entity instance (type auto-detected).

Caching Strategy

The factory maintains three caches:

CacheKeyValuePurpose
_typeCacheType name stringTypeAvoids repeated Type.GetType() and assembly scanning
_constructorCacheConstructor signature hashConstructorInfoAvoids repeated reflection to find compatible constructors
_assemblyTypeCacheAssembly full nameType[]Avoids repeated Assembly.GetTypes() calls

Cache eviction: When cache exceeds MaxCacheSize, oldest 50% of entries are removed.

Validation

When EnableValidation is true, entity types are validated:

  • Must not be abstract classes
  • Must not be interfaces
  • Must have a parameterless constructor

Validation occurs before type caching and constructor resolution.

Error Handling

The factory provides multiple error handling strategies:

StrategyMethodBehavior
ThrowCreateUnitOfWorkThrows InvalidOperationException on failure
Try-patternTryCreateUnitOfWorkReturns false, outputs error message
Partial successCreateUnitOfWorkBatchReturns successfully created instances even if some fail

All errors are logged through IDMEEditor.AddLogMessage when available.

Usage Examples

Plugin Architecture

// Load entity types from plugin assemblies
var pluginTypes = assembly.GetTypes()
    .Where(t => t.GetCustomAttribute<EntityAttribute>() != null);

foreach (var entityType in pluginTypes)
{
    var uow = UnitOfWorkFactory.CreateUnitOfWork(
        entityType, editor, "PluginDB", entityType.Name);
    // Configure plugin-specific UOW
}

Configuration-Driven UOW Creation

// Read entity configurations from file
var config = JsonSerializer.Deserialize<List<EntityConfig>>(File.ReadAllText("entities.json"));

var entityTypes = config.ToDictionary(
    c => Type.GetType(c.TypeName),
    c => c.EntityName);

var uowSet = UnitOfWorkFactory.CreateUnitOfWorkBatch(
    entityTypes, editor, "MainDatabase");

Dynamic UI Generation

// User selects entity from dropdown
var selectedType = availableTypes[comboBox.SelectedIndex];

var uow = UnitOfWorkFactory.CreateUnitOfWork(
    selectedType, editor, "AppDatabase", selectedType.Name, "Id");

// Bind to grid
grid.DataSource = uow.Units;

Cache Statistics

public class CacheStatistics
{
    public int TypeCacheCount { get; set; }
    public int ConstructorCacheCount { get; set; }
    public bool CachingEnabled { get; set; }
}

// Get statistics
var stats = UnitOfWorkFactory.GetCacheStatistics();
Console.WriteLine($"Types cached: {stats.TypeCacheCount}");
Console.WriteLine($"Constructors cached: {stats.ConstructorCacheCount}");

// Clear all caches
UnitOfWorkFactory.ClearCaches();