Source of truth (2026)
  • DataManagementEngineStandard/Editor/MultiDataSourceUnitOfWork.cs

Overview

The MultiDataSourceUnitOfWork class provides a unified interface for managing entities across multiple data sources while maintaining relationships and ensuring data consistency. It orchestrates multiple UnitOfWork instances and handles cross-datasource transactions, enabling you to work with complex data architectures seamlessly.

🎯 Key Benefits
  • Unified Interface: Work with multiple data sources through a single API
  • Automatic Relationship Management: Define relationships once, navigate automatically
  • Transaction Coordination: Atomic commits across all data sources
  • Parent-Child Navigation: Automatic loading of related entities
  • Cross-Source Relationships: Maintain referential integrity across different databases
  • Validation & Rollback: Comprehensive error handling and rollback capabilities

πŸ”— Cross-Platform Support

Connect SQL Server, PostgreSQL, MongoDB, SQLite, and more in a single transaction

⚑ Automatic Loading

Related entities are loaded automatically when navigating parent records

πŸ›‘οΈ Data Integrity

Enforce referential integrity and business rules across multiple systems

🎯 Event-Driven

Responsive UI updates through comprehensive event notifications

Signature-Verified Snapshot

public partial class MultiDataSourceUnitOfWork : IDisposable
{
    public event EventHandler<ChildDataChangedEventArgs> ChildDataChanged;
    public dynamic CurrentParentEntity => _currentParentEntity;

    public Task AddUnitOfWorkAsync<T>(string dataSourceName, string entityName, string primaryKey)
        where T : Entity, new();

    public void AddRelationship(string parentEntityName, string childEntityName, string parentKeyField,
        string childForeignKeyField, RelationshipBehavior deleteBehavior = RelationshipBehavior.CascadeDelete,
        bool isRequired = false);

    public ObservableBindingList<T> GetEntities<T>(string entityName) where T : Entity, new();
    public Task<Dictionary<string, ObservableBindingList<TChild>>> GetAllRelatedChildrenAsync<TChild, TParent>(string parentEntityName, TParent parentEntity)
        where TChild : Entity, new()
        where TParent : Entity, new();

    public Task<IErrorsInfo> InsertAsync<T>(string entityName, T entity) where T : Entity, new();
    public Task<IErrorsInfo> UpdateAsync<T>(string entityName, T entity) where T : Entity, new();
    public Task<IErrorsInfo> DeleteAsync<T>(string entityName, T entity) where T : Entity, new();
    public Task<IErrorsInfo> CommitAsync();
}

πŸ”§ Constructor

Basic Initialization

public MultiDataSourceUnitOfWork(IDMEEditor dmeEditor)

Initializes a new instance of the MultiDataSourceUnitOfWork class.

ParameterTypeDescription
dmeEditor IDMEEditor The data management editor instance providing access to data sources and configuration

Basic Usage

// Initialize the multi-datasource UnitOfWork
using var multiUoW = new MultiDataSourceUnitOfWork(dmeEditor);

// Now ready to add entities and relationships
Console.WriteLine("Multi-DataSource UnitOfWork initialized successfully");

πŸ”§ Setting Up Entities & Relationships

Adding Entity Types

public async Task AddUnitOfWorkAsync<T>(string dataSourceName, string entityName, string primaryKey) 
    where T : Entity, new()

Adds a new UnitOfWork for a specific entity type and data source.

ParameterTypeDescription
dataSourceNamestringName of the data source (must exist in DME configuration)
entityNamestringName of the entity/table
primaryKeystringName of the primary key field

Adding Multiple Entity Types

// Set up entities from different data sources
await multiUoW.AddUnitOfWorkAsync<Customer>("SqlServer_CRM", "Customers", "CustomerID");
await multiUoW.AddUnitOfWorkAsync<Order>("MongoDB_Orders", "Orders", "OrderID");
await multiUoW.AddUnitOfWorkAsync<OrderDetail>("MongoDB_Orders", "OrderDetails", "DetailID");
await multiUoW.AddUnitOfWorkAsync<Product>("PostgreSQL_Inventory", "Products", "ProductID");
await multiUoW.AddUnitOfWorkAsync<Address>("SqlServer_CRM", "Addresses", "AddressID");

Console.WriteLine("All entity types configured across multiple data sources");

Defining Relationships

public void AddRelationship(
    string parentEntityName, 
    string childEntityName, 
    string parentKeyField, 
    string childForeignKeyField, 
    RelationshipBehavior deleteBehavior = RelationshipBehavior.CascadeDelete, 
    bool isRequired = false)
ParameterTypeDescription
parentEntityNamestringName of the parent entity
childEntityNamestringName of the child entity
parentKeyFieldstringPrimary key field in parent entity
childForeignKeyFieldstringForeign key field in child entity
deleteBehaviorRelationshipBehaviorHow to handle deletes (CascadeDelete, SetNull, Restrict)
isRequiredboolWhether the relationship is required for validation

Comprehensive Relationship Setup

// Define Customer -> Orders relationship (across SQL Server and MongoDB)
multiUoW.AddRelationship(
    "Customer", "Order",           // Parent -> Child
    "CustomerID", "CustomerID",    // Key mapping
    RelationshipBehavior.CascadeDelete,  // Delete orders when customer is deleted
    isRequired: false              // Customers can exist without orders
);

// Define Customer -> Addresses relationship (within SQL Server)
multiUoW.AddRelationship(
    "Customer", "Address",
    "CustomerID", "CustomerID",
    RelationshipBehavior.CascadeDelete,
    isRequired: false
);

// Define Order -> OrderDetails relationship (within MongoDB)
multiUoW.AddRelationship(
    "Order", "OrderDetail",
    "OrderID", "OrderID",
    RelationshipBehavior.CascadeDelete,
    isRequired: true               // Orders must have at least one detail
);

// Define OrderDetail -> Product relationship (across MongoDB and PostgreSQL)
multiUoW.AddRelationship(
    "Product", "OrderDetail",      // Product is parent of OrderDetail
    "ProductID", "ProductID",
    RelationshipBehavior.Restrict, // Can't delete products that are referenced
    isRequired: true               // OrderDetails must reference a valid product
);

Console.WriteLine("Cross-datasource relationships configured successfully");

πŸ“Š Data Access Methods

Getting Entity Collections

public ObservableBindingList<T> GetEntities<T>(string entityName) where T : Entity, new()

Gets the observable collection of entities for a specific entity type.

Getting Related Children

public async Task<Dictionary<string, ObservableBindingList<TChild>>> GetAllRelatedChildrenAsync<TChild, TParent>(
    string parentEntityName, 
    TParent parentEntity)

Retrieves all related child entities for a given parent entity across all defined relationships.

Data Access Examples

// Get entity collections
var customers = multiUoW.GetEntities<Customer>("Customer");
var orders = multiUoW.GetEntities<Order>("Order");
var products = multiUoW.GetEntities<Product>("Product");

Console.WriteLine($"Loaded {customers.Count} customers, {orders.Count} orders, {products.Count} products");

// Get all related children for a specific customer
var selectedCustomer = customers.FirstOrDefault(c => c.CustomerID == "CUST001");
if (selectedCustomer != null)
{
    // This will return a dictionary with all related child collections
    var relatedChildren = await multiUoW.GetAllRelatedChildrenAsync<object, Customer>(
        "Customer", selectedCustomer);
    
    // Access specific child collections
    if (relatedChildren.ContainsKey("Order"))
    {
        var customerOrders = relatedChildren["Order"];
        Console.WriteLine($"Customer {selectedCustomer.Name} has {customerOrders.Count} orders");
    }
    
    if (relatedChildren.ContainsKey("Address"))
    {
        var customerAddresses = relatedChildren["Address"];
        Console.WriteLine($"Customer {selectedCustomer.Name} has {customerAddresses.Count} addresses");
    }
}

// Access current parent entity (for navigation scenarios)
var currentParent = multiUoW.CurrentParentEntity;
if (currentParent != null)
{
    Console.WriteLine($"Current parent: {currentParent}");
}
πŸ’‘ Performance Tips
  • Entity collections are cached and reused across calls
  • Related children are loaded on-demand when navigating
  • Use specific generic types when possible for better performance
  • Consider using filters at the data source level for large datasets

πŸ”„ CRUD Operations

Insert Operations

public async Task<IErrorsInfo> InsertAsync<T>(string entityName, T entity) where T : Entity, new()

Inserts a new entity and handles any related entities according to defined relationships.

Update Operations

public async Task<IErrorsInfo> UpdateAsync<T>(string entityName, T entity) where T : Entity, new()

Updates an existing entity and triggers relationship-based updates if needed.

Delete Operations

public async Task<IErrorsInfo> DeleteAsync<T>(string entityName, T entity) where T : Entity, new()

Deletes an entity and handles related entities based on the defined relationship behavior.

Complete CRUD Example

// INSERT: Create a new customer with address
var newCustomer = new Customer 
{ 
    CustomerID = Guid.NewGuid().ToString(),
    Name = "John Doe", 
    Email = "john@example.com",
    Status = "Active"
};

var insertResult = await multiUoW.InsertAsync("Customer", newCustomer);
if (insertResult.Flag == Errors.Ok)
{
    Console.WriteLine($"Customer {newCustomer.Name} inserted successfully");
    
    // Add an address for the customer
    var address = new Address
    {
        AddressID = Guid.NewGuid().ToString(),
        CustomerID = newCustomer.CustomerID,
        Street = "123 Main St",
        City = "Anytown",
        State = "CA",
        ZipCode = "12345"
    };
    
    await multiUoW.InsertAsync("Address", address);
    Console.WriteLine("Customer address added");
}

// UPDATE: Modify customer information
newCustomer.Email = "john.doe@example.com";
newCustomer.Status = "Premium";

var updateResult = await multiUoW.UpdateAsync("Customer", newCustomer);
if (updateResult.Flag == Errors.Ok)
{
    Console.WriteLine("Customer updated successfully");
}

// INSERT: Create an order for the customer
var newOrder = new Order
{
    OrderID = Guid.NewGuid().ToString(),
    CustomerID = newCustomer.CustomerID,
    OrderDate = DateTime.Now,
    Status = "Pending",
    Total = 299.99m
};

await multiUoW.InsertAsync("Order", newOrder);

// INSERT: Add order details
var orderDetail = new OrderDetail
{
    DetailID = Guid.NewGuid().ToString(),
    OrderID = newOrder.OrderID,
    ProductID = "PROD001", // Assuming this product exists
    Quantity = 2,
    UnitPrice = 149.99m
};

await multiUoW.InsertAsync("OrderDetail", orderDetail);
Console.WriteLine("Order with details created");

// DELETE: Remove an order (will cascade to details if configured)
var orderToDelete = orders.FirstOrDefault(o => o.Status == "Cancelled");
if (orderToDelete != null)
{
    var deleteResult = await multiUoW.DeleteAsync("Order", orderToDelete);
    if (deleteResult.Flag == Errors.Ok)
    {
        Console.WriteLine("Cancelled order deleted (with cascade to details)");
    }
}
Òő ï¸ Important Notes
  • All operations respect the defined relationships and behaviors
  • Changes are tracked but not persisted until CommitAsync() is called
  • Failed operations will provide detailed error information
  • Use the entity name exactly as defined when adding the UnitOfWork

💾 Transaction Management

Atomic Commits

public async Task<IErrorsInfo> CommitAsync()

Commits all changes across all managed UnitOfWork instances. If any commit fails, all previously committed changes are rolled back.

Transaction Safety Example

try 
{
    // Make multiple changes across different data sources
    var customer = new Customer 
    { 
        CustomerID = "CUST123",
        Name = "Jane Smith", 
        Email = "jane@example.com" 
    };
    await multiUoW.InsertAsync("Customer", customer);
    
    var order = new Order 
    { 
        OrderID = "ORD456",
        CustomerID = customer.CustomerID,
        OrderDate = DateTime.Now,
        Total = 150.00m
    };
    await multiUoW.InsertAsync("Order", order);
    
    var address = new Address
    {
        AddressID = "ADDR789",
        CustomerID = customer.CustomerID,
        Street = "456 Oak Ave",
        City = "Somewhere"
    };
    await multiUoW.InsertAsync("Address", address);
    
    // Commit all changes atomically across all data sources
    var result = await multiUoW.CommitAsync();
    
    if (result.Flag == Errors.Ok)
    {
        Console.WriteLine("βœ… All changes committed successfully across:");
        Console.WriteLine("   - SQL Server (Customer, Address)");
        Console.WriteLine("   - MongoDB (Order)");
        Console.WriteLine("   - Transaction completed atomically");
    }
    else
    {
        Console.WriteLine($"❌ Commit failed: {result.Message}");
        Console.WriteLine("All changes have been rolled back automatically");
        
        // Detailed error information
        if (result.Ex != null)
        {
            Console.WriteLine($"Exception details: {result.Ex.Message}");
        }
    }
}
catch (Exception ex)
{
    Console.WriteLine($"❌ Transaction error: {ex.Message}");
    Console.WriteLine("All changes have been rolled back automatically");
}
πŸ›‘οΈ Transaction Guarantees
  • Atomicity: All changes succeed or all fail
  • Consistency: Relationships are validated before commit
  • Isolation: Changes are isolated until commit
  • Durability: Committed changes are persisted across all data sources
πŸ“ Rollback Behavior

If any individual UnitOfWork fails to commit, the system automatically rolls back all previously committed changes to maintain consistency across all data sources. This ensures your data remains in a valid state even when working with multiple databases.

πŸ”— Relationship Behaviors

The MultiDataSourceUnitOfWork supports three relationship behaviors that determine how related entities are handled during delete operations:

πŸ—‘οΈ CascadeDelete

When a parent entity is deleted, all related child entities are automatically deleted

// Customer -> Orders with cascade delete
multiUoW.AddRelationship(
    "Customer", "Order", 
    "CustomerID", "CustomerID",
    RelationshipBehavior.CascadeDelete
);

// Deleting customer will delete all their orders

Γ°ΕΈΕ‘Β« Restrict

Prevents deletion of a parent entity if related child entities exist

// Product -> OrderDetails with restrict
multiUoW.AddRelationship(
    "Product", "OrderDetail", 
    "ProductID", "ProductID",
                            RelationshipBehavior.Restrict
);

// Cannot delete products that are referenced in orders

πŸ”„ SetNull

When a parent entity is deleted, the foreign key fields in related child entities are set to null

// Manager -> Employees with set null
multiUoW.AddRelationship(
    "Manager", "Employee", 
    "ManagerID", "ManagerID",
    RelationshipBehavior.SetNull
);

// Deleting manager sets ManagerID to null in employees

Relationship Behavior in Action

// Set up different relationship behaviors
multiUoW.AddRelationship("Customer", "Order", "CustomerID", "CustomerID", 
                        RelationshipBehavior.CascadeDelete);

multiUoW.AddRelationship("Order", "OrderDetail", "OrderID", "OrderID", 
                        RelationshipBehavior.CascadeDelete);

multiUoW.AddRelationship("Product", "OrderDetail", "ProductID", "ProductID", 
                        RelationshipBehavior.Restrict);

// Attempt to delete a customer
var customerToDelete = customers.FirstOrDefault(c => c.Status == "Inactive");
if (customerToDelete != null)
{
    try
    {
        var result = await multiUoW.DeleteAsync("Customer", customerToDelete);
        
        if (result.Flag == Errors.Ok)
        {
            Console.WriteLine("βœ… Customer deleted successfully");
            Console.WriteLine("   Ò†’ All customer orders were cascade deleted");
            Console.WriteLine("   Ò†’ All order details were cascade deleted");
        }
        else
        {
            Console.WriteLine($"❌ Delete failed: {result.Message}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"❌ Delete prevented by relationship constraint: {ex.Message}");
    }
}

// Attempt to delete a product that's referenced in orders
var productToDelete = products.FirstOrDefault();
if (productToDelete != null)
{
    try
    {
        var result = await multiUoW.DeleteAsync("Product", productToDelete);
        
        if (result.Flag != Errors.Ok)
        {
            Console.WriteLine($"❌ Product deletion restricted: {result.Message}");
            Console.WriteLine("   Ò†’ Product is referenced in existing order details");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"❌ Delete prevented: {ex.Message}");
    }
}
Òő ï¸ SetNull Behavior Notes
  • SetNull only works with nullable foreign key fields
  • For non-nullable fields, the behavior will fall back to Restrict
  • Always consider the business implications of orphaned records
  • Test relationship behaviors thoroughly in development

Γ°ΕΈΕ’ΕΈ Real-World Example: E-Commerce System

Complete E-Commerce Implementation

public class ECommerceService : IDisposable
{
    private readonly MultiDataSourceUnitOfWork _multiUoW;
    
    public ECommerceService(IDMEEditor dmeEditor)
    {
        _multiUoW = new MultiDataSourceUnitOfWork(dmeEditor);
        SetupDataStructure().Wait();
    }
    
    private async Task SetupDataStructure()
    {
        // Setup entities across multiple data sources
        await _multiUoW.AddUnitOfWorkAsync<Customer>("CRM_SqlServer", "Customers", "CustomerID");
        await _multiUoW.AddUnitOfWorkAsync<Order>("Orders_MongoDB", "Orders", "OrderID");
        await _multiUoW.AddUnitOfWorkAsync<OrderDetail>("Orders_MongoDB", "OrderDetails", "DetailID");
        await _multiUoW.AddUnitOfWorkAsync<Product>("Inventory_PostgreSQL", "Products", "ProductID");
        await _multiUoW.AddUnitOfWorkAsync<Address>("CRM_SqlServer", "Addresses", "AddressID");
        await _multiUoW.AddUnitOfWorkAsync<Payment>("Payments_Firebird", "Payments", "PaymentID");
        
        // Define business relationships
        _multiUoW.AddRelationship("Customer", "Order", "CustomerID", "CustomerID",
                                 RelationshipBehavior.CascadeDelete, false);
        
        _multiUoW.AddRelationship("Customer", "Address", "CustomerID", "CustomerID",
                                 RelationshipBehavior.CascadeDelete, false);
        
        _multiUoW.AddRelationship("Order", "OrderDetail", "OrderID", "OrderID",
                                 RelationshipBehavior.CascadeDelete, true);
        
        _multiUoW.AddRelationship("Order", "Payment", "OrderID", "OrderID",
                                 RelationshipBehavior.Restrict, false);
        
        _multiUoW.AddRelationship("Product", "OrderDetail", "ProductID", "ProductID",
                                 RelationshipBehavior.Restrict, true);
        
        // Setup event handling
        _multiUoW.ChildDataChanged += OnChildDataChanged;
        
        Console.WriteLine("🎯 E-Commerce data structure configured across:");
        Console.WriteLine("   πŸ“Š SQL Server: Customers, Addresses");
        Console.WriteLine("   πŸ“Š MongoDB: Orders, OrderDetails");
        Console.WriteLine("   πŸ“Š PostgreSQL: Products");
        Console.WriteLine("   πŸ“Š Firebird: Payments");
    }
    
    private async void OnChildDataChanged(object sender, ChildDataChangedEventArgs e)
    {
        Console.WriteLine($"πŸ”„ Data changed: {e.ChildEntityName} for {e.ParentEntityName}");
        
        // Update UI or trigger business logic
        switch (e.ChildEntityName)
        {
            case "Order":
                await UpdateCustomerOrderSummary();
                break;
            case "Address":
                await ValidateShippingOptions();
                break;
            case "Payment":
                await UpdateOrderStatus();
                break;
        }
    }
    
    public async Task<string> ProcessOrderAsync(OrderRequest request)
    {
        try
        {
            // 1. Validate customer
            var customers = _multiUoW.GetEntities<Customer>("Customer");
            var customer = customers.FirstOrDefault(c => c.CustomerID == request.CustomerID);
            
            if (customer == null)
            {
                throw new BusinessException("Customer not found");
            }
            
            // 2. Create order
            var order = new Order
            {
                OrderID = Guid.NewGuid().ToString(),
                CustomerID = request.CustomerID,
                OrderDate = DateTime.Now,
                Status = "Pending",
                ShippingAddress = request.ShippingAddress,
                Total = 0
            };
            
            await _multiUoW.InsertAsync("Order", order);
            
            // 3. Add order details and calculate total
            decimal orderTotal = 0;
            var products = _multiUoW.GetEntities<Product>("Product");
            
            foreach (var item in request.Items)
            {
                var product = products.FirstOrDefault(p => p.ProductID == item.ProductID);
                if (product == null)
                {
                    throw new BusinessException($"Product {item.ProductID} not found");
                }
                
                if (product.StockQuantity < item.Quantity)
                {
                    throw new BusinessException($"Insufficient stock for {product.Name}");
                }
                
                var detail = new OrderDetail
                {
                    DetailID = Guid.NewGuid().ToString(),
                    OrderID = order.OrderID,
                    ProductID = item.ProductID,
                    Quantity = item.Quantity,
                    UnitPrice = product.Price,
                    LineTotal = item.Quantity * product.Price
                };
                
                await _multiUoW.InsertAsync("OrderDetail", detail);
                orderTotal += detail.LineTotal;
                
                // Update product stock
                product.StockQuantity -= item.Quantity;
                await _multiUoW.UpdateAsync("Product", product);
            }
            
            // 4. Update order total
            order.Total = orderTotal;
            await _multiUoW.UpdateAsync("Order", order);
            
            // 5. Process payment
            var payment = new Payment
            {
                PaymentID = Guid.NewGuid().ToString(),
                OrderID = order.OrderID,
                Amount = orderTotal,
                PaymentMethod = request.PaymentMethod,
                PaymentDate = DateTime.Now,
                Status = "Processed"
            };
            
            await _multiUoW.InsertAsync("Payment", payment);
            
            // 6. Update order status
            order.Status = "Confirmed";
            await _multiUoW.UpdateAsync("Order", order);
            
            // 7. Commit all changes across all data sources
            var result = await _multiUoW.CommitAsync();
            
            if (result.Flag == Errors.Ok)
            {
                Console.WriteLine("βœ… Order processed successfully across all systems:");
                Console.WriteLine($"   πŸ“¦ Order: {order.OrderID}");
                Console.WriteLine($"   💰 Total: ${orderTotal:F2}");
                Console.WriteLine($"   ðŸΒͺ Items: {request.Items.Count}");
                Console.WriteLine($"   🎯 Customer: {customer.Name}");
                
                return order.OrderID;
            }
            else
            {
                throw new BusinessException($"Order processing failed: {result.Message}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"❌ Order processing failed: {ex.Message}");
            Console.WriteLine("πŸ”„ All changes have been rolled back automatically");
            throw;
        }
    }
    
    public async Task<CustomerOrderSummary> GetCustomerOrderSummaryAsync(string customerID)
    {
        // Navigate to the customer
        var customers = _multiUoW.GetEntities<Customer>("Customer");
        var customer = customers.FirstOrDefault(c => c.CustomerID == customerID);
        
        if (customer == null)
        {
            throw new ArgumentException("Customer not found");
        }
        
        // Navigate to load all related data
        await _multiUoW.NavigateToParentAsync("Customer", customer);
        
        // Get all related children
        var relatedData = await _multiUoW.GetAllRelatedChildrenAsync<object, Customer>("Customer", customer);
        
        var summary = new CustomerOrderSummary
        {
            Customer = customer,
            OrderCount = relatedData.ContainsKey("Order") ? relatedData["Order"].Count : 0,
            AddressCount = relatedData.ContainsKey("Address") ? relatedData["Address"].Count : 0
        };
        
        if (relatedData.ContainsKey("Order"))
        {
            var orders = relatedData["Order"].Cast<Order>();
            summary.TotalSpent = orders.Sum(o => o.Total);
            summary.LastOrderDate = orders.Max(o => o.OrderDate);
        }
        
        return summary;
    }
    
    private async Task UpdateCustomerOrderSummary()
    {
        // Business logic for updating customer metrics
        Console.WriteLine("πŸ“Š Updating customer order summary...");
    }
    
    private async Task ValidateShippingOptions()
    {
        // Business logic for shipping validation
        Console.WriteLine("Γ°ΕΈΕ‘Ε‘ Validating shipping options...");
    }
    
    private async Task UpdateOrderStatus()
    {
        // Business logic for order status updates
        Console.WriteLine("πŸ“‹ Updating order status...");
    }
    
    public void Dispose()
    {
        _multiUoW?.Dispose();
    }
}

// Usage
public class OrderRequest
{
    public string CustomerID { get; set; }
    public string ShippingAddress { get; set; }
    public string PaymentMethod { get; set; }
    public List<OrderItem> Items { get; set; } = new List<OrderItem>();
}

public class OrderItem
{
    public string ProductID { get; set; }
    public int Quantity { get; set; }
}

public class CustomerOrderSummary
{
    public Customer Customer { get; set; }
    public int OrderCount { get; set; }
    public int AddressCount { get; set; }
    public decimal TotalSpent { get; set; }
    public DateTime? LastOrderDate { get; set; }
}

Best Practices

πŸ—οΈ Setup Phase

  • Define all entities and relationships before starting operations
  • Use meaningful entity names that match your domain
  • Consider relationship behaviors carefully for each business rule
  • Test relationship constraints in development

⚑ Performance

  • Use specific generic types when possible
  • Leverage caching of entity collections
  • Consider pagination for large datasets
  • Monitor cross-datasource query performance

πŸ›‘οΈ Error Handling

  • Always wrap commits in try-catch blocks
  • Handle relationship constraint violations gracefully
  • Provide meaningful error messages to users
  • Log detailed error information for debugging

πŸ”§ Resource Management

  • Use using statements for automatic disposal
  • Subscribe to events appropriately
  • Unsubscribe from events when disposing
  • Monitor memory usage with large datasets
Òő ï¸ Common Pitfalls to Avoid
  • Missing Relationships: Not defining relationships can lead to data inconsistencies
  • Wrong Behaviors: Using CascadeDelete inappropriately can cause unintended data loss
  • Performance Issues: Loading too much related data at once
  • Transaction Scope: Making changes outside the UoW that aren't part of the transaction
  • Event Leaks: Not unsubscribing from events can cause memory leaks
βœ… Success Checklist
  • All data sources are properly configured in DME
  • Entity relationships are clearly defined and tested
  • Business rules are implemented through relationship behaviors
  • Error handling is comprehensive and user-friendly
  • Performance has been tested with realistic data volumes
  • UI responds appropriately to data change events