From abaf9c8ea3c7746ccbf9784442f8e180a9903114 Mon Sep 17 00:00:00 2001 From: solidarcsoftware Date: Wed, 1 Jul 2026 07:35:35 +0200 Subject: [PATCH] Context contracts updates --- ....EntityFrameworkCore.Demo.Benchmark.csproj | 6 + .../ProcessEmployeeFinanceDataBenchmark.cs | 14 ++- .../appsettings.json | 8 ++ .../Contracts/IFinanceDbContext.cs | 12 ++ .../Contracts/IHrDbContext.cs | 12 ++ .../Contracts/IItDbContext.cs | 12 ++ .../Contracts/IOperationsDbContext.cs | 12 ++ .../Database/DBContexts/FinanceDbContext.cs | 7 +- .../Database/DBContexts/HrDbContext.cs | 6 +- .../Database/DBContexts/ItDbContext.cs | 9 +- .../DBContexts/OperationsDbContext.cs | 9 +- .../PersistenceRegistration.cs | 18 ++- .../Services/EmployeePerformanceService.cs | 2 +- .../Services/SalaryAdjustmentService.cs | 3 +- .../Registration/FinanceModuleRegistration.cs | 5 +- DataArc.EntityFrameworkCore.Demo/Program.cs | 8 +- README.md | 107 +++++++++++------- 17 files changed, 168 insertions(+), 82 deletions(-) create mode 100644 DataArc.EntityFrameworkCore.Demo.Benchmark/appsettings.json create mode 100644 DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IFinanceDbContext.cs create mode 100644 DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IHrDbContext.cs create mode 100644 DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IItDbContext.cs create mode 100644 DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IOperationsDbContext.cs diff --git a/DataArc.EntityFrameworkCore.Demo.Benchmark/DataArc.EntityFrameworkCore.Demo.Benchmark.csproj b/DataArc.EntityFrameworkCore.Demo.Benchmark/DataArc.EntityFrameworkCore.Demo.Benchmark.csproj index 58af83b..9a1a3e9 100644 --- a/DataArc.EntityFrameworkCore.Demo.Benchmark/DataArc.EntityFrameworkCore.Demo.Benchmark.csproj +++ b/DataArc.EntityFrameworkCore.Demo.Benchmark/DataArc.EntityFrameworkCore.Demo.Benchmark.csproj @@ -22,4 +22,10 @@ + + + Always + + + \ No newline at end of file diff --git a/DataArc.EntityFrameworkCore.Demo.Benchmark/ProcessEmployeeFinanceDataBenchmark.cs b/DataArc.EntityFrameworkCore.Demo.Benchmark/ProcessEmployeeFinanceDataBenchmark.cs index 6cdb3b8..2564578 100644 --- a/DataArc.EntityFrameworkCore.Demo.Benchmark/ProcessEmployeeFinanceDataBenchmark.cs +++ b/DataArc.EntityFrameworkCore.Demo.Benchmark/ProcessEmployeeFinanceDataBenchmark.cs @@ -11,9 +11,10 @@ using DataArc.Core; using DataArc.EntityFrameworkCore.Demo.Persistence; using DataArc.EntityFrameworkCore.Demo.Persistence.Database.Creator; -using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts; using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels; using DataArc.EntityFrameworkCore.Demo.Persistence.Utils; +using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts; +using Microsoft.Extensions.Configuration; namespace DataArc.EntityFrameworkCore.Demo.Benchmark { @@ -38,14 +39,19 @@ public class RawEmployeeBulkDataBenchmark [Params(62_500, 125_000, 250_000)] public int RecordCount { get; set; } - [Params(62_500)] - public int BulkBatchSize { get; set; } + //[Params(62_500)] + public int BulkBatchSize => MaxRecordCount; [GlobalSetup] public void GlobalSetup() { + var configurationManager = new ConfigurationManager(); + configurationManager + .AddJsonFile("appsettings.json", optional: false) + .Build(); + _serviceProvider = new ServiceCollection() - .AddPersistence() + .AddPersistence(configurationManager) .BuildServiceProvider(); _databaseCreators = diff --git a/DataArc.EntityFrameworkCore.Demo.Benchmark/appsettings.json b/DataArc.EntityFrameworkCore.Demo.Benchmark/appsettings.json new file mode 100644 index 0000000..1f25904 --- /dev/null +++ b/DataArc.EntityFrameworkCore.Demo.Benchmark/appsettings.json @@ -0,0 +1,8 @@ +{ + "ConnectionStrings": { + "FinanceDb": "Server=.;Database=FinanceDb;Integrated Security=true;TrustServerCertificate=True;", + "HrDb": "Server=.;Database=HrDb;Integrated Security=true;TrustServerCertificate=True;", + "ItDb": "Server=.;Database=ItDb;Integrated Security=true;TrustServerCertificate=True;", + "OperationsDb": "Server=.;Database=OperationsDb;Integrated Security=true;TrustServerCertificate=True;" + } +} \ No newline at end of file diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IFinanceDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IFinanceDbContext.cs new file mode 100644 index 0000000..1cd0652 --- /dev/null +++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IFinanceDbContext.cs @@ -0,0 +1,12 @@ +using DataArc.Core; +using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels; +using Microsoft.EntityFrameworkCore; + +namespace DataArc.EntityFrameworkCore.Demo.Persistence.Contracts +{ + public interface IFinanceDbContext : IExecutionContext + { + DbSet? Employer { get; set; } + DbSet? Employee { get; set; } + } +} \ No newline at end of file diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IHrDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IHrDbContext.cs new file mode 100644 index 0000000..272ae7d --- /dev/null +++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IHrDbContext.cs @@ -0,0 +1,12 @@ +using DataArc.Core; +using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels; +using Microsoft.EntityFrameworkCore; + +namespace DataArc.EntityFrameworkCore.Demo.Persistence.Contracts +{ + public interface IHrDbContext : IExecutionContext + { + DbSet? Employer { get; set; } + DbSet? Employee { get; set; } + } +} \ No newline at end of file diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IItDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IItDbContext.cs new file mode 100644 index 0000000..02e2325 --- /dev/null +++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IItDbContext.cs @@ -0,0 +1,12 @@ +using DataArc.Core; +using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels; +using Microsoft.EntityFrameworkCore; + +namespace DataArc.EntityFrameworkCore.Demo.Persistence.Contracts +{ + public interface IItDbContext : IExecutionContext + { + DbSet? Employer { get; set; } + DbSet? Employee { get; set; } + } +} \ No newline at end of file diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IOperationsDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IOperationsDbContext.cs new file mode 100644 index 0000000..0a2777a --- /dev/null +++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IOperationsDbContext.cs @@ -0,0 +1,12 @@ +using DataArc.Core; +using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels; +using Microsoft.EntityFrameworkCore; + +namespace DataArc.EntityFrameworkCore.Demo.Persistence.Contracts +{ + public interface IOperationsDbContext : IExecutionContext + { + DbSet? Employer { get; set; } + DbSet? Employee { get; set; } + } +} \ No newline at end of file diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/FinanceDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/FinanceDbContext.cs index b1538a8..5a00d07 100644 --- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/FinanceDbContext.cs +++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/FinanceDbContext.cs @@ -1,14 +1,9 @@ -using DataArc.Core; +using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts; using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels; using Microsoft.EntityFrameworkCore; namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts { - public interface IFinanceDbContext : IExecutionContext - { - - } - internal class FinanceDbContext : DbContext, IFinanceDbContext { public FinanceDbContext(DbContextOptions dbContextOptions) diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/HrDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/HrDbContext.cs index da4ef34..949686b 100644 --- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/HrDbContext.cs +++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/HrDbContext.cs @@ -1,13 +1,9 @@ -using DataArc.Core; +using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts; using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels; using Microsoft.EntityFrameworkCore; namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts { - public interface IHrDbContext : IExecutionContext - { - } - internal class HrDbContext : DbContext, IHrDbContext { public HrDbContext(DbContextOptions dbContextOptions) diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/ItDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/ItDbContext.cs index 2149cc5..f649366 100644 --- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/ItDbContext.cs +++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/ItDbContext.cs @@ -1,16 +1,13 @@ -using DataArc.Core; +using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts; using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels; using Microsoft.EntityFrameworkCore; namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts { - public interface IItDbContext : IExecutionContext - { - } - internal class ItDbContext : DbContext, IItDbContext { - public ItDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions) { } + public ItDbContext(DbContextOptions dbContextOptions) + : base(dbContextOptions) { } public DbSet? Employer { get; set; } public DbSet? Employee { get; set; } diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/OperationsDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/OperationsDbContext.cs index 63d5e52..213c497 100644 --- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/OperationsDbContext.cs +++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/OperationsDbContext.cs @@ -1,16 +1,13 @@ -using DataArc.Core; +using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts; using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels; using Microsoft.EntityFrameworkCore; namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts { - public interface IOperationsDbContext : IExecutionContext - { - } - internal class OperationsDbContext : DbContext, IOperationsDbContext { - public OperationsDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions) { } + public OperationsDbContext(DbContextOptions dbContextOptions) + : base(dbContextOptions) { } public DbSet? Employer { get; set; } public DbSet? Employee { get; set; } diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/PersistenceRegistration.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/PersistenceRegistration.cs index ce66ffb..a195951 100644 --- a/DataArc.EntityFrameworkCore.Demo.Persistence/PersistenceRegistration.cs +++ b/DataArc.EntityFrameworkCore.Demo.Persistence/PersistenceRegistration.cs @@ -1,13 +1,15 @@ -using DataArc.Core; -using DataArc.EntityFrameworkCore.Demo.Persistence.Database.Creator; -using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts; -using DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeder; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; +using DataArc.Core; +using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts; +using DataArc.EntityFrameworkCore.Demo.Persistence.Database.Creator; +using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts; +using DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeder; + namespace DataArc.EntityFrameworkCore.Demo.Persistence { public static class PersistenceRegistration @@ -17,12 +19,8 @@ public static class PersistenceRegistration builder.AddConsole(); }); - public static IServiceCollection AddPersistence(this IServiceCollection services) + public static IServiceCollection AddPersistence(this IServiceCollection services, ConfigurationManager configurationManager) { - var configurationManager = new ConfigurationManager(); - configurationManager.AddJsonFile("appsettings.json", optional: false) - .Build(); - services .AddDataArcCore() .ConfigureDataArc(provider => diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/EmployeePerformanceService.cs b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/EmployeePerformanceService.cs index 927ebcb..e55a29e 100644 --- a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/EmployeePerformanceService.cs +++ b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/EmployeePerformanceService.cs @@ -1,7 +1,7 @@ using DataArc.Core; using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Dtos; -using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts; +using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts; using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels; namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/SalaryAdjustmentService.cs b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/SalaryAdjustmentService.cs index 329fb4e..b22feb0 100644 --- a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/SalaryAdjustmentService.cs +++ b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/SalaryAdjustmentService.cs @@ -1,6 +1,5 @@ using DataArc.Core; - -using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts; +using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts; using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels; namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Registration/FinanceModuleRegistration.cs b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Registration/FinanceModuleRegistration.cs index f48c428..57637ec 100644 --- a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Registration/FinanceModuleRegistration.cs +++ b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Registration/FinanceModuleRegistration.cs @@ -1,15 +1,16 @@ using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services; using DataArc.EntityFrameworkCore.Demo.Persistence; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Registration { public static class FinanceRegistrationModule { - public static IServiceCollection AddFinanceModule(this IServiceCollection services) + public static IServiceCollection AddFinanceModule(this IServiceCollection services, ConfigurationManager configurationManager) { // Register persistence layer for Finance module - services.AddPersistence(); + services.AddPersistence(configurationManager); // Register Services services.AddScoped(); services.AddScoped(); diff --git a/DataArc.EntityFrameworkCore.Demo/Program.cs b/DataArc.EntityFrameworkCore.Demo/Program.cs index edf016f..56fb258 100644 --- a/DataArc.EntityFrameworkCore.Demo/Program.cs +++ b/DataArc.EntityFrameworkCore.Demo/Program.cs @@ -6,6 +6,7 @@ using DataArc.EntityFrameworkCore.Demo.Application.Workers; using DataArc.EntityFrameworkCore.Demo.Persistence.Database.Creator; using DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeder; +using Microsoft.Extensions.Configuration; const int batchSize = 100_000; @@ -13,8 +14,13 @@ .CreateDefaultBuilder(args) .ConfigureServices(services => { + var configurationManager = new ConfigurationManager(); + configurationManager + .AddJsonFile("appsettings.json", optional: false) + .Build(); + services - .AddFinanceModule() + .AddFinanceModule(configurationManager) .AddHostedService(); }) .Build(); diff --git a/README.md b/README.md index 6a614de..a99f163 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ **DataArc.EntityFrameworkCore** uses EF Core. -It provides a dedicated execution layer that helps application code coordinate serious EF Core data workflows across multiple `DbContext` boundaries without turning the workflow into scattered repository calls, handler classes, or manual `DbContext` plumbing. +It provides a dedicated execution layer that helps application code coordinate serious EF Core data workflows across multiple `DbContext` boundaries without turning the workflow into scattered repository calls, handler classes, one-off bulk extensions, or manual `DbContext` plumbing. This demo runs as a small .NET background worker. @@ -47,6 +47,10 @@ Execute the workflow. Receive structured results. ``` +A .NET `BackgroundService` gives the workflow a familiar home. + +DataArc.EntityFrameworkCore gives the workflow its execution model. + --- ## Benchmark Snapshot @@ -64,11 +68,11 @@ Each benchmark case inserts `RecordCount` employees into each participating cont Total inserted records = RecordCount x 4 ``` -| Method | Record Count Per Context | Bulk Batch Size | Total Inserted Records | Mean | StdDev | Allocated | -|---|---:|---:|---:|---:|---:|---:| -| ExecuteParallelBulkInsertAsync | 62,500 | 62,500 | 250,000 | 552.2 ms | 84.94 ms | 60.83 MB | -| ExecuteParallelBulkInsertAsync | 125,000 | 62,500 | 500,000 | 818.4 ms | 90.35 ms | 121.19 MB | -| ExecuteParallelBulkInsertAsync | 250,000 | 62,500 | 1,000,000 | 1,775.7 ms | 344.70 ms | 242.31 MB | +| Method | Record Count Per Context | Total Inserted Records | Mean | StdDev | Completed Work Items | Lock Contentions | Gen0 | Allocated | +|---|---:|---:|---:|---:|---:|---:|---:|---:| +| ExecuteParallelBulkInsertAsync | 62,500 | 250,000 | 486.2 ms | 30.92 ms | 19,193 | - | 5,000 | 60.77 MB | +| ExecuteParallelBulkInsertAsync | 125,000 | 500,000 | 887.7 ms | 159.61 ms | 38,051 | - | 10,000 | 120.93 MB | +| ExecuteParallelBulkInsertAsync | 250,000 | 1,000,000 | 1,747.1 ms | 285.75 ms | 77,796 | - | 20,000 | 242.21 MB | These results are workload-specific evidence, not a universal performance guarantee. Hardware, SQL Server configuration, schema shape, indexes, batch size, runtime version, and database state all affect results. @@ -90,7 +94,11 @@ Many systems eventually need data workflows that move beyond one direct context - bulk process scheduled jobs - compose read models from more than one EF Core context -DataArc.EntityFrameworkCore gives those workflows an explicit C# execution model. +Plain EF Core does not provide native high-throughput bulk insert as a first-class API. + +Third-party bulk libraries can add bulk methods to a `DbContext`, although they do not provide a full execution pipeline across multiple EF Core context boundaries. + +DataArc.EntityFrameworkCore provides the coordination layer: command/query pipelines, explicit execution contexts, parallel execution, affected-record aggregation, failure handling, and structured results. It is designed for EF Core work that needs: @@ -137,6 +145,7 @@ The application flow: 9. Executes the command pipeline in parallel. 10. Queries top-rated employees across all four contexts. 11. Prints a short summary. +12. Stops the host after the workflow completes. ```mermaid flowchart LR @@ -170,7 +179,7 @@ Program.cs = host setup + demo database preparation DemoWorkflowWorker = background data workflow runner SalaryAdjustmentService = read-transform-write workflow EmployeePerformanceService = cross-context read model -Persistence = EF Core contexts, registration, seeding, and database creators +Persistence = EF Core contexts, contracts, registration, seeding, and database creators ``` This shape is familiar to .NET developers because many real data workflows run as background jobs, scheduled workers, import/export processors, reconciliation jobs, reporting projection builders, or internal batch processes. @@ -180,10 +189,10 @@ const int batchSize = 100_000; var host = Host .CreateDefaultBuilder(args) - .ConfigureServices(services => + .ConfigureServices((context, services) => { services - .AddFinanceModule() + .AddFinanceModule(context.Configuration) .AddHostedService(); }) .Build(); @@ -391,23 +400,24 @@ This creates one read model from multiple isolated EF Core contexts without expo --- -## Internal DbContexts, Public Execution Boundaries - -The demo keeps concrete EF Core `DbContext` implementations internal to the persistence project. +## Public Execution Contracts, Internal DbContexts -Application code does not depend directly on `FinanceDbContext`, `HrDbContext`, `ItDbContext`, or `OperationsDbContext`. +The demo exposes each EF Core boundary through a public execution-context contract. -Instead, each EF Core boundary is exposed through a small public execution-context interface. +Application code depends on these contracts, not on the concrete `DbContext` implementations. -In DataArc, an execution context represents a logical boundary where work is performed. In this demo, each execution context is backed by an EF Core `DbContext`. +The contracts define the DataArc execution boundaries and expose the EF Core sets available through those boundaries. ```csharp public interface IFinanceDbContext : IExecutionContext { + DbSet? Employer { get; set; } + + DbSet? Employee { get; set; } } ``` -The concrete context remains internal: +The concrete `DbContext` implementation remains internal to the persistence project: ```csharp internal class FinanceDbContext : DbContext, IFinanceDbContext @@ -433,7 +443,7 @@ internal class FinanceDbContext : DbContext, IFinanceDbContext } ``` -Application services route work through the public execution boundary: +DataArc uses the public execution contract to route work to the registered EF Core implementation: ```csharp commandBuilder @@ -441,7 +451,7 @@ commandBuilder .AddBulk(employees, batchSize); ``` -This keeps EF Core implementation details inside the persistence layer while still allowing application code to choose exactly where work executes. +This keeps the concrete EF Core implementation hidden while giving the application a clear execution boundary to target. --- @@ -449,6 +459,10 @@ This keeps EF Core implementation details inside the persistence layer while sti Each EF Core context is registered as a DataArc database execution context. +This demo uses SQL Server. + +The worker and application services do not configure SQL Server directly. They target DataArc execution contexts. SQL Server connection strings and EF Core provider configuration stay in `PersistenceRegistration.cs`. + ```csharp services.AddDataArcCore(); @@ -474,9 +488,9 @@ services.ConfigureDataArc(dataArc => The registration tells DataArc: ```text -This interface is the execution boundary. +This contract is the execution boundary. This concrete DbContext is the EF Core implementation. -This connection string points to the target database. +This connection string points to the SQL Server database. ``` --- @@ -594,6 +608,11 @@ DataArc.EntityFrameworkCore.Demo │ └── DemoWorkflowWorker.cs │ ├── Persistence +│ ├── Contracts +│ │ ├── IFinanceDbContext.cs +│ │ ├── IHrDbContext.cs +│ │ ├── IItDbContext.cs +│ │ └── IOperationsDbContext.cs │ ├── Database │ │ ├── Creator │ │ │ ├── FinanceDbCreator.cs @@ -618,28 +637,37 @@ DataArc.EntityFrameworkCore.Demo The application project owns the worker and use-case services. -The persistence project owns EF Core contexts, database models, database creation, seeding, and DataArc registration. +The persistence project owns EF Core contracts, contexts, database models, database creation, seeding, and DataArc registration. ---- +The demo application and benchmark project both reference the persistence project directly. -## Running The Demo +```text +DataArc.EntityFrameworkCore.Demo + -> DataArc.EntityFrameworkCore.Demo.Persistence -### 1. Configure Connection Strings +DataArc.EntityFrameworkCore.Demo.Benchmark + -> DataArc.EntityFrameworkCore.Demo.Persistence +``` -Update the SQL Server connection strings in `PersistenceRegistration.cs`. +The benchmark references the persistence project directly so it measures the DataArc EF Core execution path without depending on the demo application shell. -```csharp -var financeConnectionString = - "Server=YOUR_SERVER;Database=FinanceDb;Integrated Security=true;TrustServerCertificate=True;"; +--- -var hrConnectionString = - "Server=YOUR_SERVER;Database=HrDb;Integrated Security=true;TrustServerCertificate=True;"; +## Running The Demo -var itConnectionString = - "Server=YOUR_SERVER;Database=ItDb;Integrated Security=true;TrustServerCertificate=True;"; +### 1. Configure Connection Strings + +Update the SQL Server connection strings in `appsettings.json`. -var operationsConnectionString = - "Server=YOUR_SERVER;Database=OperationsDb;Integrated Security=true;TrustServerCertificate=True;"; +```json +{ + "ConnectionStrings": { + "FinanceDb": "Server=YOUR_SERVER;Database=FinanceDb;Integrated Security=true;TrustServerCertificate=True;", + "HrDb": "Server=YOUR_SERVER;Database=HrDb;Integrated Security=true;TrustServerCertificate=True;", + "ItDb": "Server=YOUR_SERVER;Database=ItDb;Integrated Security=true;TrustServerCertificate=True;", + "OperationsDb": "Server=YOUR_SERVER;Database=OperationsDb;Integrated Security=true;TrustServerCertificate=True;" + } +} ``` ### 2. Run The Application @@ -690,6 +718,8 @@ From the benchmark project: dotnet run -c Release --framework net8.0 ``` +The benchmark project has its own `appsettings.json` and references the persistence project directly. + Default benchmark shape: ```csharp @@ -726,7 +756,7 @@ xychart-beta title "Parallel bulk insert mean time" x-axis ["250k", "500k", "1M"] y-axis "Mean time in ms" 0 --> 2000 - bar [552.2, 818.4, 1775.7] + bar [486.2, 887.7, 1747.1] ``` ```mermaid @@ -734,7 +764,7 @@ xychart-beta title "Managed memory allocated per operation" x-axis ["250k", "500k", "1M"] y-axis "Allocated MB" 0 --> 260 - bar [60.83, 121.19, 242.31] + bar [60.77, 120.93, 242.21] ``` --- @@ -781,8 +811,7 @@ DataArc.EntityFrameworkCore gives EF Core an explicit execution layer for high-t It helps teams: -- keep concrete `DbContext` implementations internal -- expose public execution-context interfaces +- define public execution contracts while keeping concrete `DbContext` implementations internal - coordinate work across multiple EF Core contexts - execute bulk command pipelines - run parallel operations