diff --git a/DataArc.EntityFrameworkCore.Demo.Benchmark/DataArc.EntityFrameworkCore.Demo.Benchmark.csproj b/DataArc.EntityFrameworkCore.Demo.Benchmark/DataArc.EntityFrameworkCore.Demo.Benchmark.csproj
index 9a1a3e9..4141338 100644
--- a/DataArc.EntityFrameworkCore.Demo.Benchmark/DataArc.EntityFrameworkCore.Demo.Benchmark.csproj
+++ b/DataArc.EntityFrameworkCore.Demo.Benchmark/DataArc.EntityFrameworkCore.Demo.Benchmark.csproj
@@ -2,29 +2,60 @@
Exe
- net8.0
+ net6.0;net7.0;net8.0;net9.0;net10.0
+ $(NoWarn);NETSDK1138
false
enable
enable
Program
- $(NoWarn);NETSDK1138
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- Always
+ PreserveNewest
diff --git a/DataArc.EntityFrameworkCore.Demo.Benchmark/ProcessEmployeeFinanceDataBenchmark.cs b/DataArc.EntityFrameworkCore.Demo.Benchmark/ProcessEmployeeFinanceDataBenchmark.cs
index 2564578..9c616b2 100644
--- a/DataArc.EntityFrameworkCore.Demo.Benchmark/ProcessEmployeeFinanceDataBenchmark.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Benchmark/ProcessEmployeeFinanceDataBenchmark.cs
@@ -30,16 +30,15 @@ public class RawEmployeeBulkDataBenchmark
private const int MaxRecordCount = 1_000_000;
private IServiceProvider? _serviceProvider;
- private IReadOnlyCollection _databaseCreators = [];
+ private IReadOnlyCollection _databaseCreators = new List();
private ICommandFactory? _commandFactory;
- private List _allEmployees = [];
- private List _benchmarkEmployees = [];
+ private List _allEmployees = new List();
+ private List _benchmarkEmployees = new List();
[Params(62_500, 125_000, 250_000)]
public int RecordCount { get; set; }
- //[Params(62_500)]
public int BulkBatchSize => MaxRecordCount;
[GlobalSetup]
@@ -54,13 +53,13 @@ public void GlobalSetup()
.AddPersistence(configurationManager)
.BuildServiceProvider();
- _databaseCreators =
- [
- _serviceProvider.GetRequiredService(),
- _serviceProvider.GetRequiredService(),
- _serviceProvider.GetRequiredService(),
- _serviceProvider.GetRequiredService()
- ];
+ _databaseCreators = new IDatabaseCreator[]
+ {
+ _serviceProvider.GetRequiredService(),
+ _serviceProvider.GetRequiredService(),
+ _serviceProvider.GetRequiredService(),
+ _serviceProvider.GetRequiredService()
+ };
_commandFactory = _serviceProvider.GetRequiredService();
@@ -109,19 +108,19 @@ public async Task ExecuteParallelBulkInsertAsync()
throw new InvalidOperationException($"{nameof(commandBuilder)} was not resolved.");
commandBuilder
- .UseDbExecutionContext()
+ .UseDbExecutionContext()
.AddBulk(_benchmarkEmployees, BulkBatchSize);
commandBuilder
- .UseDbExecutionContext()
+ .UseDbExecutionContext()
.AddBulk(_benchmarkEmployees, BulkBatchSize);
commandBuilder
- .UseDbExecutionContext()
+ .UseDbExecutionContext()
.AddBulk(_benchmarkEmployees, BulkBatchSize);
commandBuilder
- .UseDbExecutionContext()
+ .UseDbExecutionContext()
.AddBulk(_benchmarkEmployees, BulkBatchSize);
var command = await commandBuilder.BuildAsync();
diff --git a/DataArc.EntityFrameworkCore.Demo.Benchmark/appsettings.json b/DataArc.EntityFrameworkCore.Demo.Benchmark/appsettings.json
index 1f25904..e38bbf5 100644
--- a/DataArc.EntityFrameworkCore.Demo.Benchmark/appsettings.json
+++ b/DataArc.EntityFrameworkCore.Demo.Benchmark/appsettings.json
@@ -1,8 +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;"
+ "SASDb": "Server=.;Database=SAS_Db;Integrated Security=true;TrustServerCertificate=True;",
+ "GoogleDb": "Server=.;Database=SAS_GoogleDb;Integrated Security=true;TrustServerCertificate=True;",
+ "MicrosoftDb": "Server=.;Database=SAS_MicrosoftDb;Integrated Security=true;TrustServerCertificate=True;",
+ "OpenAiDb": "Server=.;Database=SAS_OpenAiDb;Integrated Security=true;TrustServerCertificate=True;"
}
}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Dtos/EmployeeDto.cs b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Dtos/EmployeeDto.cs
similarity index 66%
rename from DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Dtos/EmployeeDto.cs
rename to DataArc.EntityFrameworkCore.Demo.Contracts/Application/Dtos/EmployeeDto.cs
index 2d69aac..a304d9e 100644
--- a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Dtos/EmployeeDto.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Dtos/EmployeeDto.cs
@@ -1,4 +1,4 @@
-namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Dtos
+namespace DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos
{
public class EmployeeDto
{
diff --git a/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Features/EmployeePerformance/Services/IEmployeePerformanceService.cs b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Features/EmployeePerformance/Services/IEmployeePerformanceService.cs
new file mode 100644
index 0000000..bb18d74
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Features/EmployeePerformance/Services/IEmployeePerformanceService.cs
@@ -0,0 +1,9 @@
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos;
+
+namespace DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Features.EmployeePerformance.Services
+{
+ public interface IEmployeePerformanceService
+ {
+ Task> GetConsolidatedTopRatedEmployeesAsync(double rating);
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/ISalaryAdjustmentService.cs b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Features/SalaryAdjustments/Services/ISalaryAdjustmentService.cs
similarity index 63%
rename from DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/ISalaryAdjustmentService.cs
rename to DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Features/SalaryAdjustments/Services/ISalaryAdjustmentService.cs
index b5b1ae5..0e7f477 100644
--- a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/ISalaryAdjustmentService.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Features/SalaryAdjustments/Services/ISalaryAdjustmentService.cs
@@ -1,4 +1,4 @@
-namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services
+namespace DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Features.SalaryAdjustments.Services
{
public interface ISalaryAdjustmentService
{
diff --git a/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Google/Repository/IGoogleRepository.cs b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Google/Repository/IGoogleRepository.cs
new file mode 100644
index 0000000..46c29c7
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Google/Repository/IGoogleRepository.cs
@@ -0,0 +1,9 @@
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos;
+
+namespace DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Google.Repository
+{
+ public interface IGoogleRepository
+ {
+ Task> GetTopRatedEmployeesAsync(double rating);
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Microsoft/Repository/IMicrosoftRepository.cs b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Microsoft/Repository/IMicrosoftRepository.cs
new file mode 100644
index 0000000..541aa42
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/Microsoft/Repository/IMicrosoftRepository.cs
@@ -0,0 +1,9 @@
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos;
+
+namespace DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Microsoft.Repository
+{
+ public interface IMicrosoftRepository
+ {
+ Task> GetTopRatedEmployeesAsync(double rating);
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/OpenAI/Repository/IOpenAiRepository.cs b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/OpenAI/Repository/IOpenAiRepository.cs
new file mode 100644
index 0000000..a4aff98
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/OpenAI/Repository/IOpenAiRepository.cs
@@ -0,0 +1,9 @@
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos;
+
+namespace DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.OpenAi.Repository
+{
+ public interface IOpenAiRepository
+ {
+ Task> GetTopRatedEmployeesAsync(double rating);
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/SolidArc/Repository/ISolidArcRepository.cs b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/SolidArc/Repository/ISolidArcRepository.cs
new file mode 100644
index 0000000..4db1611
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Contracts/Application/Modules/SolidArc/Repository/ISolidArcRepository.cs
@@ -0,0 +1,9 @@
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos;
+
+namespace DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.SolidArc.Repository
+{
+ public interface ISolidArcRepository
+ {
+ Task> GetTopRatedEmployeesAsync(double rating);
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Contracts/DataArc.EntityFrameworkCore.Demo.Contracts.csproj b/DataArc.EntityFrameworkCore.Demo.Contracts/DataArc.EntityFrameworkCore.Demo.Contracts.csproj
new file mode 100644
index 0000000..4e133fb
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Contracts/DataArc.EntityFrameworkCore.Demo.Contracts.csproj
@@ -0,0 +1,42 @@
+
+
+
+ Library
+ net6.0;net7.0;net8.0;net9.0;net10.0
+ $(NoWarn);NETSDK1138
+ false
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Host/DataArc.EntityFrameworkCore.Demo.Host.csproj b/DataArc.EntityFrameworkCore.Demo.Host/DataArc.EntityFrameworkCore.Demo.Host.csproj
new file mode 100644
index 0000000..4b69467
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Host/DataArc.EntityFrameworkCore.Demo.Host.csproj
@@ -0,0 +1,42 @@
+
+
+
+ Exe
+ net6.0;net7.0;net8.0;net9.0;net10.0
+ $(NoWarn);NETSDK1138
+ false
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Host/HostRegistration.cs b/DataArc.EntityFrameworkCore.Demo.Host/HostRegistration.cs
new file mode 100644
index 0000000..8d88df8
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Host/HostRegistration.cs
@@ -0,0 +1,24 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Features.EmployeePerformance.Services;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Features.SalaryAdjustments.Services;
+using DataArc.EntityFrameworkCore.Demo.Host.BackgroundServices;
+using DataArc.EntityFrameworkCore.Demo.Persistence;
+
+namespace DataArc.EntityFrameworkCore.Demo.Host
+{
+ public static class HostRegistration
+ {
+ public static IServiceCollection AddBackgroundServices(this IServiceCollection services, ConfigurationManager configurationManager)
+ {
+ // Register Persistence
+ services.AddPersistence(configurationManager);
+
+ // Register Background worker services
+ services.AddScoped();
+ services.AddScoped();
+ return services;
+ }
+ }
+}
diff --git a/DataArc.EntityFrameworkCore.Demo/Program.cs b/DataArc.EntityFrameworkCore.Demo.Host/Program.cs
similarity index 75%
rename from DataArc.EntityFrameworkCore.Demo/Program.cs
rename to DataArc.EntityFrameworkCore.Demo.Host/Program.cs
index 243ab65..6f5996e 100644
--- a/DataArc.EntityFrameworkCore.Demo/Program.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Host/Program.cs
@@ -2,9 +2,10 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
-using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Registration;
-using DataArc.EntityFrameworkCore.Demo.Application.Workers;
using DataArc.EntityFrameworkCore.Demo.Persistence;
+using DataArc.EntityFrameworkCore.Demo.Host.Workers;
+using DataArc.EntityFrameworkCore.Demo.Host;
+using DataArc.EntityFrameworkCore.Demo;
var host = Host
.CreateDefaultBuilder(args)
@@ -17,7 +18,8 @@
.Build();
services
- .AddFinanceModule(configurationManager)
+ .AddRepositories(configurationManager)
+ .AddBackgroundServices(configurationManager)
.AddHostedService();
})
.Build();
diff --git a/DataArc.EntityFrameworkCore.Demo.Host/WorkerServices/EmployeePerformanceService.cs b/DataArc.EntityFrameworkCore.Demo.Host/WorkerServices/EmployeePerformanceService.cs
new file mode 100644
index 0000000..62f4727
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Host/WorkerServices/EmployeePerformanceService.cs
@@ -0,0 +1,61 @@
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Features.EmployeePerformance.Services;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Google.Repository;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Microsoft.Repository;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.OpenAi.Repository;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.SolidArc.Repository;
+
+namespace DataArc.EntityFrameworkCore.Demo.Host.BackgroundServices
+{
+ internal class EmployeePerformanceService : IEmployeePerformanceService
+ {
+ private readonly IGoogleRepository _googleRepository;
+ private readonly IOpenAiRepository _openAiRepository;
+ private readonly IMicrosoftRepository _microsoftRepository;
+ private readonly ISolidArcRepository _solidArcRepository;
+
+ public EmployeePerformanceService(
+ IGoogleRepository googleRepository,
+ IOpenAiRepository openAiRepository,
+ IMicrosoftRepository microsoftRepository,
+ ISolidArcRepository solidArcRepository
+ )
+ {
+ _googleRepository = googleRepository;
+ _microsoftRepository = microsoftRepository;
+ _solidArcRepository = solidArcRepository;
+ _openAiRepository = openAiRepository;
+ }
+
+ public async Task> GetConsolidatedTopRatedEmployeesAsync(double rating)
+ {
+ var topratedGoogleEmployees = await _googleRepository
+ .GetTopRatedEmployeesAsync(rating);
+
+ var toprateMicrosoftEmployees = await _microsoftRepository
+ .GetTopRatedEmployeesAsync(rating);
+
+ var topratedOpenAiEmployees = await _openAiRepository
+ .GetTopRatedEmployeesAsync(rating);
+
+ var topratedSolidArcEmployees = await _solidArcRepository
+ .GetTopRatedEmployeesAsync(rating);
+
+ var consolidatedTopRatedEmployees = new List();
+
+ consolidatedTopRatedEmployees
+ .AddRange(topratedGoogleEmployees);
+
+ consolidatedTopRatedEmployees
+ .AddRange(toprateMicrosoftEmployees);
+
+ consolidatedTopRatedEmployees
+ .AddRange(topratedOpenAiEmployees);
+
+ consolidatedTopRatedEmployees
+ .AddRange(topratedSolidArcEmployees);
+
+ return consolidatedTopRatedEmployees;
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/SalaryAdjustmentService.cs b/DataArc.EntityFrameworkCore.Demo.Host/WorkerServices/SalaryAdjustmentService.cs
similarity index 64%
rename from DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/SalaryAdjustmentService.cs
rename to DataArc.EntityFrameworkCore.Demo.Host/WorkerServices/SalaryAdjustmentService.cs
index b22feb0..9cc2f6d 100644
--- a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/SalaryAdjustmentService.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Host/WorkerServices/SalaryAdjustmentService.cs
@@ -1,15 +1,17 @@
using DataArc.Core;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Features.SalaryAdjustments.Services;
using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts;
using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;
-namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services
+namespace DataArc.EntityFrameworkCore.Demo.Host.BackgroundServices
{
internal class SalaryAdjustmentService : ISalaryAdjustmentService
{
private readonly ICommandFactory _commandFactory;
private readonly IQueryFactory _queryFactory;
- public SalaryAdjustmentService(ICommandFactory commandFactory, IQueryFactory queryFactory)
+ public SalaryAdjustmentService(
+ ICommandFactory commandFactory, IQueryFactory queryFactory)
{
_commandFactory = commandFactory;
_queryFactory = queryFactory;
@@ -20,13 +22,11 @@ public async Task ProcessEmployeeSalaryAdjustmentsAsync(
decimal salaryThreshold,
int batchSize)
{
- // Build a query to read employee data from the HR database context based on the salary threshold
var employeesQuery = await _queryFactory.CreateQueryAsync();
var employees = await employeesQuery
- .UseDbExecutionContext()
+ .UseDbExecutionContext()
.ReadWhereAsync(e => e.Salary > salaryThreshold);
- //Adjust salaries
foreach (var employee in employees)
{
employee.Salary += employee.Salary * salaryAdjustmentBaseRate;
@@ -36,28 +36,24 @@ public async Task ProcessEmployeeSalaryAdjustmentsAsync(
.CreateCommandBuilderAsync();
commandBuilder
- .UseDbExecutionContext()
+ .UseDbExecutionContext()
.AddBulk(employees, batchSize);
commandBuilder
- .UseDbExecutionContext()
+ .UseDbExecutionContext()
.AddBulk(employees, batchSize);
commandBuilder
- .UseDbExecutionContext()
+ .UseDbExecutionContext()
.AddBulk(employees, batchSize);
- // Build the command builder pipeline and execute the command in parallel across the different contexts
var command = await commandBuilder.BuildAsync();
var commandResult = await command.ExecuteAsync();
- // Check the command result for success and handle any errors or exceptions
if (!commandResult.Success) {
- // Handle command failure (e.g., log errors, throw exceptions, etc.)
throw new InvalidOperationException($"Failed to process salary adjustments. {commandResult?.Exception?.Message}");
}
- // Return the total number of affected records across all contexts
return commandResult.TotalAffected;
}
}
diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Workers/DemoWorkflowWorker.cs b/DataArc.EntityFrameworkCore.Demo.Host/Workers/DemoWorkflowWorker.cs
similarity index 88%
rename from DataArc.EntityFrameworkCore.Demo/Application/Workers/DemoWorkflowWorker.cs
rename to DataArc.EntityFrameworkCore.Demo.Host/Workers/DemoWorkflowWorker.cs
index d6d42b6..b490584 100644
--- a/DataArc.EntityFrameworkCore.Demo/Application/Workers/DemoWorkflowWorker.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Host/Workers/DemoWorkflowWorker.cs
@@ -1,8 +1,9 @@
using Microsoft.Extensions.Hosting;
-using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Features.EmployeePerformance.Services;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Features.SalaryAdjustments.Services;
-namespace DataArc.EntityFrameworkCore.Demo.Application.Workers
+namespace DataArc.EntityFrameworkCore.Demo.Host.Workers
{
internal sealed class DemoWorkflowWorker : BackgroundService
{
@@ -41,7 +42,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
if (processedCount > 0)
{
var topRatedEmployees = await _employeePerformanceService
- .GetTopRatedEmployeesAsync(ratingThreshold);
+ .GetConsolidatedTopRatedEmployeesAsync(ratingThreshold);
var topRatedEmployee = topRatedEmployees
.OrderByDescending(employee => employee.Salary)
diff --git a/DataArc.EntityFrameworkCore.Demo.Host/appsettings.json b/DataArc.EntityFrameworkCore.Demo.Host/appsettings.json
new file mode 100644
index 0000000..e38bbf5
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Host/appsettings.json
@@ -0,0 +1,8 @@
+{
+ "ConnectionStrings": {
+ "SASDb": "Server=.;Database=SAS_Db;Integrated Security=true;TrustServerCertificate=True;",
+ "GoogleDb": "Server=.;Database=SAS_GoogleDb;Integrated Security=true;TrustServerCertificate=True;",
+ "MicrosoftDb": "Server=.;Database=SAS_MicrosoftDb;Integrated Security=true;TrustServerCertificate=True;",
+ "OpenAiDb": "Server=.;Database=SAS_OpenAiDb;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
index 1cd0652..2c69554 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IFinanceDbContext.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IFinanceDbContext.cs
@@ -4,7 +4,7 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Contracts
{
- public interface IFinanceDbContext : IExecutionContext
+ public interface IGoogleDbContext : IExecutionContext
{
DbSet? Employer { get; set; }
DbSet? Employee { get; set; }
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IHrDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IHrDbContext.cs
index 272ae7d..f9a4284 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IHrDbContext.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IHrDbContext.cs
@@ -4,7 +4,7 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Contracts
{
- public interface IHrDbContext : IExecutionContext
+ public interface IMicrosoftDbContext : IExecutionContext
{
DbSet? Employer { get; set; }
DbSet? Employee { get; set; }
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IItDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IItDbContext.cs
index 02e2325..b694b1e 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IItDbContext.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IItDbContext.cs
@@ -4,7 +4,7 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Contracts
{
- public interface IItDbContext : IExecutionContext
+ public interface ISolidArcDbContext : IExecutionContext
{
DbSet? Employer { get; set; }
DbSet? Employee { get; set; }
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IOperationsDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IOperationsDbContext.cs
index 0a2777a..bb19e8c 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IOperationsDbContext.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Contracts/IOperationsDbContext.cs
@@ -4,7 +4,7 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Contracts
{
- public interface IOperationsDbContext : IExecutionContext
+ public interface IOpenAIDbContext : IExecutionContext
{
DbSet? Employer { get; set; }
DbSet? Employee { get; set; }
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/FinanceDbCreator.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/GoogleDbCreator.cs
similarity index 84%
rename from DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/FinanceDbCreator.cs
rename to DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/GoogleDbCreator.cs
index b5c103f..6fd48d0 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/FinanceDbCreator.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/GoogleDbCreator.cs
@@ -4,16 +4,16 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.Creator
{
- public interface IFinanceDbCreator : IDatabaseCreator
+ public interface IGoogleDbCreator : IDatabaseCreator
{
}
- internal class FinanceDbCreator : IFinanceDbCreator
+ internal class GoogleDbCreator : IGoogleDbCreator
{
private readonly IDatabaseFactory _databaseFactory;
- public FinanceDbCreator(IDatabaseFactory databaseFactory)
+ public GoogleDbCreator(IDatabaseFactory databaseFactory)
{
_databaseFactory = databaseFactory;
}
@@ -33,7 +33,7 @@ public bool EnsureCreated()
var dbBuilder = _databaseFactory.CreateDatabaseBuilder();
var db = dbBuilder
- .IncludeDbContext()
+ .IncludeDbContext()
.Build(generateScripts: true, applyChanges: true);
db.ExecuteCreate();
@@ -50,7 +50,7 @@ public bool EnsureDeleted()
var dbBuilder = _databaseFactory.CreateDatabaseBuilder();
var db = dbBuilder
- .IncludeDbContext()
+ .IncludeDbContext()
.Build(generateScripts: true, applyChanges: true);
db.ExecuteDrop();
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/OperationsDbCreator.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/MicrosoftDbCreator.cs
similarity index 83%
rename from DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/OperationsDbCreator.cs
rename to DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/MicrosoftDbCreator.cs
index da2686d..cd8bbe4 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/OperationsDbCreator.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/MicrosoftDbCreator.cs
@@ -4,16 +4,16 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.Creator
{
- public interface IOperationsDbCreator : IDatabaseCreator
+ public interface IMicrosoftDbCreator : IDatabaseCreator
{
}
- internal class OperationsDbCreator : IOperationsDbCreator
+ internal class MicrosoftDbCreator : IMicrosoftDbCreator
{
private readonly IDatabaseFactory _databaseFactory;
- public OperationsDbCreator(IDatabaseFactory databaseFactory)
+ public MicrosoftDbCreator(IDatabaseFactory databaseFactory)
{
_databaseFactory = databaseFactory;
}
@@ -33,7 +33,7 @@ public bool EnsureCreated()
var dbBuilder = _databaseFactory.CreateDatabaseBuilder();
var db = dbBuilder
- .IncludeDbContext()
+ .IncludeDbContext()
.Build(generateScripts: true, applyChanges: true);
db.ExecuteCreate();
@@ -50,7 +50,7 @@ public bool EnsureDeleted()
var dbBuilder = _databaseFactory.CreateDatabaseBuilder();
var db = dbBuilder
- .IncludeDbContext()
+ .IncludeDbContext()
.Build(generateScripts: true, applyChanges: true);
db.ExecuteDrop();
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/ItDbCreator.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/OpenAiDbCreator.cs
similarity index 84%
rename from DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/ItDbCreator.cs
rename to DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/OpenAiDbCreator.cs
index 4a5b8cf..7441f2c 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/ItDbCreator.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/OpenAiDbCreator.cs
@@ -4,16 +4,16 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.Creator
{
- public interface IItDbCreator : IDatabaseCreator
+ public interface IOpenAiDbCreator : IDatabaseCreator
{
}
- public class ItDbCreator : IItDbCreator
+ internal class OpenAiDbCreator : IOpenAiDbCreator
{
private readonly IDatabaseFactory _databaseFactory;
- public ItDbCreator(IDatabaseFactory databaseFactory)
+ public OpenAiDbCreator(IDatabaseFactory databaseFactory)
{
_databaseFactory = databaseFactory;
}
@@ -33,7 +33,7 @@ public bool EnsureCreated()
var dbBuilder = _databaseFactory.CreateDatabaseBuilder();
var db = dbBuilder
- .IncludeDbContext()
+ .IncludeDbContext()
.Build(generateScripts: true, applyChanges: true);
db.ExecuteCreate();
@@ -50,7 +50,7 @@ public bool EnsureDeleted()
var dbBuilder = _databaseFactory.CreateDatabaseBuilder();
var db = dbBuilder
- .IncludeDbContext()
+ .IncludeDbContext()
.Build(generateScripts: true, applyChanges: true);
db.ExecuteDrop();
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/HrDbCreator.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/SASDbCreator.cs
similarity index 84%
rename from DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/HrDbCreator.cs
rename to DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/SASDbCreator.cs
index 155f112..c615583 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/HrDbCreator.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Creator/SASDbCreator.cs
@@ -4,16 +4,16 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.Creator
{
- public interface IHrDbCreator : IDatabaseCreator
+ public interface ISASDbCreator : IDatabaseCreator
{
}
- internal class HrDbCreator : IHrDbCreator
+ public class SASDbCreator : ISASDbCreator
{
private readonly IDatabaseFactory _databaseFactory;
- public HrDbCreator(IDatabaseFactory databaseFactory)
+ public SASDbCreator(IDatabaseFactory databaseFactory)
{
_databaseFactory = databaseFactory;
}
@@ -33,7 +33,7 @@ public bool EnsureCreated()
var dbBuilder = _databaseFactory.CreateDatabaseBuilder();
var db = dbBuilder
- .IncludeDbContext()
+ .IncludeDbContext()
.Build(generateScripts: true, applyChanges: true);
db.ExecuteCreate();
@@ -50,7 +50,7 @@ public bool EnsureDeleted()
var dbBuilder = _databaseFactory.CreateDatabaseBuilder();
var db = dbBuilder
- .IncludeDbContext()
+ .IncludeDbContext()
.Build(generateScripts: true, applyChanges: true);
db.ExecuteDrop();
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/HrDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/GoogleDbContext.cs
similarity index 81%
rename from DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/HrDbContext.cs
rename to DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/GoogleDbContext.cs
index 949686b..dba53c5 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/HrDbContext.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/GoogleDbContext.cs
@@ -4,9 +4,9 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts
{
- internal class HrDbContext : DbContext, IHrDbContext
+ internal class GoogleDbContext : DbContext, IGoogleDbContext
{
- public HrDbContext(DbContextOptions dbContextOptions)
+ public GoogleDbContext(DbContextOptions dbContextOptions)
: base(dbContextOptions) { }
public DbSet? Employer { get; set; }
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/OperationsDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/MicrosoftDbContext.cs
similarity index 79%
rename from DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/OperationsDbContext.cs
rename to DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/MicrosoftDbContext.cs
index 213c497..0bf6253 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/OperationsDbContext.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/MicrosoftDbContext.cs
@@ -4,9 +4,9 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts
{
- internal class OperationsDbContext : DbContext, IOperationsDbContext
+ internal class MicrosoftDbContext : DbContext, IMicrosoftDbContext
{
- public OperationsDbContext(DbContextOptions dbContextOptions)
+ public MicrosoftDbContext(DbContextOptions dbContextOptions)
: base(dbContextOptions) { }
public DbSet? Employer { get; set; }
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/ItDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/OpenAIDbContext.cs
similarity index 81%
rename from DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/ItDbContext.cs
rename to DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/OpenAIDbContext.cs
index f649366..c95a248 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/ItDbContext.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/OpenAIDbContext.cs
@@ -4,9 +4,9 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts
{
- internal class ItDbContext : DbContext, IItDbContext
+ internal class OpenAIDbContext : DbContext, IOpenAIDbContext
{
- public ItDbContext(DbContextOptions dbContextOptions)
+ public OpenAIDbContext(DbContextOptions dbContextOptions)
: base(dbContextOptions) { }
public DbSet? Employer { get; set; }
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/FinanceDbContext.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/SolidArcDbContext.cs
similarity index 80%
rename from DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/FinanceDbContext.cs
rename to DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/SolidArcDbContext.cs
index 5a00d07..1c1cb11 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/FinanceDbContext.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/DBContexts/SolidArcDbContext.cs
@@ -4,9 +4,9 @@
namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts
{
- internal class FinanceDbContext : DbContext, IFinanceDbContext
+ internal class SolidArcDbContext : DbContext, ISolidArcDbContext
{
- public FinanceDbContext(DbContextOptions dbContextOptions)
+ public SolidArcDbContext(DbContextOptions dbContextOptions)
: base(dbContextOptions) { }
public DbSet? Employer { get; set; }
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeder/IDatabaseSeeder.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/IDatabaseSeeder.cs
similarity index 51%
rename from DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeder/IDatabaseSeeder.cs
rename to DataArc.EntityFrameworkCore.Demo.Persistence/Database/IDatabaseSeeder.cs
index 16b1054..43f09cc 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeder/IDatabaseSeeder.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/IDatabaseSeeder.cs
@@ -1,8 +1,8 @@
-namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeder
+namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database
{
public interface IDatabaseSeeder
{
- bool SeedDatabase(int recordCount);
- Task SeedDatabaseAsync(int recordCount);
+ bool SeedDatabase();
+ Task SeedDatabaseAsync();
}
}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeder/HrDbSeeder.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeder/HrDbSeeder.cs
deleted file mode 100644
index 6bbe267..0000000
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeder/HrDbSeeder.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using DataArc.Core;
-using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts;
-using DataArc.EntityFrameworkCore.Demo.Persistence.Utils;
-
-namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeder
-{
- public interface IHrDbSeeder : IDatabaseSeeder
- {
-
- }
-
- internal class HrDbSeeder : IHrDbSeeder
- {
- private readonly ICommandFactory _commandFactory;
-
- public HrDbSeeder(ICommandFactory commandFactory)
- {
- _commandFactory = commandFactory;
- }
-
- public bool SeedDatabase(int recordCount)
- {
- try
- {
- var seedingCommand = _commandFactory.CreateCommand();
-
- seedingCommand
- .UseDbExecutionContext()
- .AddBulk(SeedDataGenerator.GenerateHrSeedData(recordCount), recordCount)
- .Execute();
-
- return true;
- }
- catch
- {
- throw;
- }
- }
-
- public async Task SeedDatabaseAsync(int recordCount)
- {
- try
- {
- // Build and execute the seeding command
- var seedingCommand = await _commandFactory.CreateCommandAsync();
-
- await seedingCommand
- .UseDbExecutionContext()
- .AddBulk(SeedDataGenerator.GenerateHrSeedData(recordCount), recordCount)
- .ExecuteAsync();
-
- return true;
- }
- catch
- {
- throw;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/GoogleDBSeeder.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/GoogleDBSeeder.cs
new file mode 100644
index 0000000..5fd8a42
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/GoogleDBSeeder.cs
@@ -0,0 +1,72 @@
+using DataArc.Core;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;
+
+namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeders
+{
+ public interface IGoogleDBSeeder : IDatabaseSeeder
+ {
+
+ }
+
+ internal class GoogleDBSeeder : IGoogleDBSeeder
+ {
+ private readonly ICommandFactory _commandFactory;
+ public GoogleDBSeeder(ICommandFactory commandFactory)
+ {
+ _commandFactory = commandFactory;
+ }
+
+ public bool SeedDatabase()
+ {
+ try
+ {
+ var seedingCommand = _commandFactory.CreateCommand();
+
+ var commandResult = seedingCommand
+ .UseDbExecutionContext()
+ .Add(new Employer()
+ {
+ Name = "Solid Arc Software",
+ Description = "Software Company"
+ }).Execute();
+
+
+ if (!commandResult.Success)
+ throw new Exception($"Exception occured in {nameof(GoogleDBSeeder)}, {commandResult.Message}");
+
+ return true;
+ }
+ catch
+ {
+ throw;
+ }
+ }
+
+ public async Task SeedDatabaseAsync()
+ {
+ try
+ {
+ var seedingCommand = await _commandFactory.CreateCommandAsync();
+
+ var commandResult = await seedingCommand
+ .UseDbExecutionContext()
+ .Add(new Employer()
+ {
+ Name = "Solid Arc Software",
+ Description = "Software Company"
+ })
+ .ExecuteAsync();
+
+ if (!commandResult.Success)
+ throw new Exception($"Exception occured in {nameof(GoogleDBSeeder)}, {commandResult.Message}");
+
+ return true;
+ }
+ catch
+ {
+ throw;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/MicrosoftDbSeeder.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/MicrosoftDbSeeder.cs
new file mode 100644
index 0000000..7f2b07f
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/MicrosoftDbSeeder.cs
@@ -0,0 +1,73 @@
+using DataArc.Core;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;
+
+namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeders
+{
+ public interface IMicrosoftDbSeeder : IDatabaseSeeder
+ {
+
+ }
+
+ internal class MicrosoftDbSeeder : IMicrosoftDbSeeder
+ {
+ private readonly ICommandFactory _commandFactory;
+
+ public MicrosoftDbSeeder(ICommandFactory commandFactory)
+ {
+ _commandFactory = commandFactory;
+ }
+
+ public bool SeedDatabase()
+ {
+ try
+ {
+ var seedingCommand = _commandFactory.CreateCommand();
+
+ var commandResult = seedingCommand
+ .UseDbExecutionContext()
+ .Add(new Employer()
+ {
+ Name = "Google",
+ Description = "Google Company"
+ }).Execute();
+
+
+ if (!commandResult.Success)
+ throw new Exception($"Exception occured in {nameof(MicrosoftDbSeeder)}, {commandResult.Message}");
+
+ return true;
+ }
+ catch
+ {
+ throw;
+ }
+ }
+
+ public async Task SeedDatabaseAsync()
+ {
+ try
+ {
+ var seedingCommand = await _commandFactory.CreateCommandAsync();
+
+ var commandResult = await seedingCommand
+ .UseDbExecutionContext()
+ .Add(new Employer()
+ {
+ Name = "Google",
+ Description = "Google Company"
+ })
+ .ExecuteAsync();
+
+ if (!commandResult.Success)
+ throw new Exception($"Exception occured in {nameof(MicrosoftDbSeeder)}, {commandResult.Message}");
+
+ return true;
+ }
+ catch
+ {
+ throw;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/OpenAIDbSeeder.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/OpenAIDbSeeder.cs
new file mode 100644
index 0000000..3f9e52e
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/OpenAIDbSeeder.cs
@@ -0,0 +1,73 @@
+using DataArc.Core;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;
+
+namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeders
+{
+ public interface IOpenAIDbSeeder : IDatabaseSeeder
+ {
+
+ }
+
+ internal class OpenAIDbSeeder : IOpenAIDbSeeder
+ {
+ private readonly ICommandFactory _commandFactory;
+
+ public OpenAIDbSeeder(ICommandFactory commandFactory)
+ {
+ _commandFactory = commandFactory;
+ }
+
+ public bool SeedDatabase()
+ {
+ try
+ {
+ var seedingCommand = _commandFactory.CreateCommand();
+
+ var commandResult = seedingCommand
+ .UseDbExecutionContext()
+ .Add(new Employer()
+ {
+ Name = "Microsoft",
+ Description = "Microsoft Company"
+ }).Execute();
+
+
+ if (!commandResult.Success)
+ throw new Exception($"Exception occured in {nameof(OpenAIDbSeeder)}, {commandResult.Message}");
+
+ return true;
+ }
+ catch
+ {
+ throw;
+ }
+ }
+
+ public async Task SeedDatabaseAsync()
+ {
+ try
+ {
+ var seedingCommand = await _commandFactory.CreateCommandAsync();
+
+ var commandResult = await seedingCommand
+ .UseDbExecutionContext()
+ .Add(new Employer()
+ {
+ Name = "Microsoft",
+ Description = "Microsoft Company"
+ })
+ .ExecuteAsync();
+
+ if (!commandResult.Success)
+ throw new Exception($"Exception occured in {nameof(OpenAIDbSeeder)}, {commandResult.Message}");
+
+ return true;
+ }
+ catch
+ {
+ throw;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/SolidArcDbSeeder.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/SolidArcDbSeeder.cs
new file mode 100644
index 0000000..293475f
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/Database/Seeders/SolidArcDbSeeder.cs
@@ -0,0 +1,86 @@
+using DataArc.Core;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Utils;
+
+namespace DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeders
+{
+ public interface ISolidArcDbSeeder : IDatabaseSeeder
+ {
+
+ }
+
+ internal class SolidArcDbSeeder : ISolidArcDbSeeder
+ {
+ private readonly ICommandFactory _commandFactory;
+
+ public SolidArcDbSeeder(ICommandFactory commandFactory)
+ {
+ _commandFactory = commandFactory;
+ }
+
+ public bool SeedDatabase()
+ {
+ try
+ {
+ var seedingCommand = _commandFactory.CreateCommand();
+
+ var commandResult = seedingCommand
+ .UseDbExecutionContext()
+ .Add(new Employer()
+ {
+ Name = "OpenAI",
+ Description = "OpenAI Company"
+ }).Execute();
+
+
+ if (!commandResult.Success)
+ throw new Exception($"Exception occured in {nameof(SolidArcDbSeeder)}, {commandResult.Message}");
+
+ return true;
+ }
+ catch
+ {
+ throw;
+ }
+ }
+
+ public async Task SeedDatabaseAsync()
+ {
+ try
+ {
+ var seedingCommand = await _commandFactory.CreateCommandAsync();
+
+ var employer = new Employer()
+ {
+ Name = "OpenAI",
+ Description = "OpenAI Company"
+ };
+
+ var commandResult = await seedingCommand
+ .UseDbExecutionContext()
+ .Add(employer)
+ .ExecuteAsync();
+
+ if (!commandResult.Success)
+ throw new Exception($"Exception occured in {nameof(SolidArcDbSeeder)}, {commandResult.Message}");
+
+ var bulkSeedCommand = await _commandFactory.CreateCommandAsync();
+
+ var bulkSeedCommandResult = await seedingCommand
+ .UseDbExecutionContext()
+ .AddBulk(SeedDataGenerator.GenerateHrSeedData(100_000), 100_000)
+ .ExecuteAsync();
+
+ if(!bulkSeedCommandResult.Success)
+ throw new Exception($"Exception occured in {nameof(SolidArcDbSeeder)}, {bulkSeedCommandResult.Message}");
+
+ return true;
+ }
+ catch
+ {
+ throw;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/DemoDatabaseInitializer.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/DemoDatabaseInitializer.cs
index afe94f4..3fa19e2 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/DemoDatabaseInitializer.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/DemoDatabaseInitializer.cs
@@ -1,40 +1,40 @@
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.EntityFrameworkCore.Storage;
using DataArc.EntityFrameworkCore.Demo.Persistence.Database.Creator;
-using DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeder;
-using Microsoft.EntityFrameworkCore.Storage;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeders;
namespace DataArc.EntityFrameworkCore.Demo.Persistence
{
public static class DemoDatabaseInitializer
{
- private const int BatchSize = 100_000;
-
public static async Task InitializeAsync(IServiceProvider serviceProvider)
{
var databaseCreators = new IDatabaseCreator[]
{
- serviceProvider.GetRequiredService(),
- serviceProvider.GetRequiredService(),
- serviceProvider.GetRequiredService(),
- serviceProvider.GetRequiredService()
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService()
};
var databaseSeeders = new IDatabaseSeeder[]
{
- serviceProvider.GetRequiredService()
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService()
};
await ResetDatabasesAsync(
databaseCreators,
- databaseSeeders,
- BatchSize);
+ databaseSeeders);
}
private static async Task ResetDatabasesAsync(
IReadOnlyCollection databaseCreators,
- IReadOnlyCollection databaseSeeders,
- int batchSize)
+ IReadOnlyCollection databaseSeeders)
{
foreach (var databaseCreator in databaseCreators)
{
@@ -54,7 +54,7 @@ private static async Task ResetDatabasesAsync(
foreach (var databaseSeeder in databaseSeeders)
{
- if (!await databaseSeeder.SeedDatabaseAsync(batchSize))
+ if (!await databaseSeeder.SeedDatabaseAsync())
throw new InvalidOperationException("Failed to seed the demo databases.");
}
diff --git a/DataArc.EntityFrameworkCore.Demo.Persistence/PersistenceRegistration.cs b/DataArc.EntityFrameworkCore.Demo.Persistence/PersistenceRegistration.cs
index a195951..2c21a38 100644
--- a/DataArc.EntityFrameworkCore.Demo.Persistence/PersistenceRegistration.cs
+++ b/DataArc.EntityFrameworkCore.Demo.Persistence/PersistenceRegistration.cs
@@ -8,7 +8,7 @@
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;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.Seeders;
namespace DataArc.EntityFrameworkCore.Demo.Persistence
{
@@ -27,30 +27,33 @@ public static IServiceCollection AddPersistence(this IServiceCollection services
{
provider.UseEntityFrameworkCore(context =>
{
- context.AddDbExecutionContext(options => options
- .UseSqlServer(configurationManager.GetConnectionString("FinanceDb"))
+ context.AddDbExecutionContext(options => options
+ .UseSqlServer(configurationManager.GetConnectionString("GoogleDb"))
.UseLoggerFactory(factory));
- context.AddDbExecutionContext(options => options
- .UseSqlServer(configurationManager.GetConnectionString("HrDb"))
+ context.AddDbExecutionContext(options => options
+ .UseSqlServer(configurationManager.GetConnectionString("MicrosoftDb"))
.UseLoggerFactory(factory));
- context.AddDbExecutionContext(options => options
- .UseSqlServer(configurationManager.GetConnectionString("ItDb"))
+ context.AddDbExecutionContext(options => options
+ .UseSqlServer(configurationManager.GetConnectionString("OpenAiDb"))
.UseLoggerFactory(factory));
- context.AddDbExecutionContext(options => options
- .UseSqlServer(configurationManager.GetConnectionString("OperationsDb"))
+ context.AddDbExecutionContext(options => options
+ .UseSqlServer(configurationManager.GetConnectionString("SASDb"))
.UseLoggerFactory(factory));
});
});
- services.TryAddScoped();
- services.TryAddScoped();
- services.TryAddScoped();
- services.TryAddScoped();
+ services.TryAddScoped();
+ services.TryAddScoped();
+ services.TryAddScoped();
+ services.TryAddScoped();
- services.TryAddScoped();
+ services.TryAddScoped();
+ services.TryAddScoped();
+ services.TryAddScoped();
+ services.TryAddScoped();
return services;
}
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
deleted file mode 100644
index e55a29e..0000000
--- a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/EmployeePerformanceService.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using DataArc.Core;
-
-using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Dtos;
-using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts;
-using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;
-
-namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services
-{
- internal class EmployeePerformanceService : IEmployeePerformanceService
- {
- private readonly IQueryFactory _queryFactory;
- public EmployeePerformanceService(IQueryFactory queryFactory)
- {
- _queryFactory = queryFactory;
- }
-
- public async Task> GetTopRatedEmployeesAsync(double rating)
- {
- var topRatedEmployeesQuery = await _queryFactory.CreateQueryAsync();
-
- var topRatedEmployees = await topRatedEmployeesQuery
- .UseDbExecutionContext(e => e.Rating > rating)
- .Join
- (bag => bag.Get()!.Id, f => f.Id)
- .Join
- (bag => bag.Get()!.Id, i => i.Id)
- .Join(bag => bag.Get()!.Id, o => o.Id)
- .Select(bag => new EmployeeDto()
- {
- Id = bag.Get()!.Id,
- Name = bag.Get()!.Name!,
- Surname = bag.Get()!.Surname!,
- Salary = bag.Get()!.Salary!
-
- }).ToListAsync();
-
- return topRatedEmployees;
- }
- }
-}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/IEmployeePerformanceService.cs b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/IEmployeePerformanceService.cs
deleted file mode 100644
index 66004f5..0000000
--- a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Features/SalaryAdjustments/Services/IEmployeePerformanceService.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Dtos;
-
-namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services
-{
- public interface IEmployeePerformanceService
- {
- Task> GetTopRatedEmployeesAsync(double rating);
- }
-}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Registration/FinanceModuleRegistration.cs b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Registration/FinanceModuleRegistration.cs
deleted file mode 100644
index 57637ec..0000000
--- a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Finance/Registration/FinanceModuleRegistration.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-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, ConfigurationManager configurationManager)
- {
- // Register persistence layer for Finance module
- services.AddPersistence(configurationManager);
- // Register Services
- services.AddScoped();
- services.AddScoped();
- return services;
- }
- }
-}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Google/Repository/GoogleRepository.cs b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Google/Repository/GoogleRepository.cs
new file mode 100644
index 0000000..5f0f1a4
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Google/Repository/GoogleRepository.cs
@@ -0,0 +1,35 @@
+using DataArc.Core;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Google.Repository;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;
+
+namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Google.Repository
+{
+ internal class GoogleRepository : IGoogleRepository
+ {
+ private readonly IQueryFactory _queryFactory;
+ public GoogleRepository(IQueryFactory queryFactory)
+ {
+ _queryFactory = queryFactory;
+ }
+
+ public async Task> GetTopRatedEmployeesAsync(double rating)
+ {
+ var topRatedEmployeesQuery = await _queryFactory.CreateQueryAsync();
+
+ var topRatedEmployees = await topRatedEmployeesQuery
+ .UseDbExecutionContext()
+ .ReadWhereAsync(e => e.Rating > rating);
+
+ return topRatedEmployees
+ .Select(emp => new EmployeeDto()
+ {
+ Id = emp.Id,
+ Name = emp.Name,
+ Surname = emp.Surname,
+ Salary = emp.Salary,
+ }).ToList();
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/Microsoft/Repository/MicrosoftRepository.cs b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Microsoft/Repository/MicrosoftRepository.cs
new file mode 100644
index 0000000..8f82554
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo/Application/Modules/Microsoft/Repository/MicrosoftRepository.cs
@@ -0,0 +1,35 @@
+using DataArc.Core;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Microsoft.Repository;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;
+
+namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Microsoft.Repository
+{
+ internal class MicrosoftRepository : IMicrosoftRepository
+ {
+ private readonly IQueryFactory _queryFactory;
+ public MicrosoftRepository(IQueryFactory queryFactory)
+ {
+ _queryFactory = queryFactory;
+ }
+
+ public async Task> GetTopRatedEmployeesAsync(double rating)
+ {
+ var topRatedEmployeesQuery = await _queryFactory.CreateQueryAsync();
+
+ var topRatedEmployees = await topRatedEmployeesQuery
+ .UseDbExecutionContext()
+ .ReadWhereAsync(e => e.Rating > rating);
+
+ return topRatedEmployees
+ .Select(emp => new EmployeeDto()
+ {
+ Id = emp.Id,
+ Name = emp.Name,
+ Surname = emp.Surname,
+ Salary = emp.Salary,
+ }).ToList();
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/OpenAi/Repository/OpenAiRepository.cs b/DataArc.EntityFrameworkCore.Demo/Application/Modules/OpenAi/Repository/OpenAiRepository.cs
new file mode 100644
index 0000000..434b06d
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo/Application/Modules/OpenAi/Repository/OpenAiRepository.cs
@@ -0,0 +1,35 @@
+using DataArc.Core;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.OpenAi.Repository;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;
+
+namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.OpenAi.Repository
+{
+ internal class OpenAiRepository : IOpenAiRepository
+ {
+ private readonly IQueryFactory _queryFactory;
+ public OpenAiRepository(IQueryFactory queryFactory)
+ {
+ _queryFactory = queryFactory;
+ }
+
+ public async Task> GetTopRatedEmployeesAsync(double rating)
+ {
+ var topRatedEmployeesQuery = await _queryFactory.CreateQueryAsync();
+
+ var topRatedEmployees = await topRatedEmployeesQuery
+ .UseDbExecutionContext()
+ .ReadWhereAsync(e => e.Rating > rating);
+
+ return topRatedEmployees
+ .Select(emp => new EmployeeDto()
+ {
+ Id = emp.Id,
+ Name = emp.Name,
+ Surname = emp.Surname,
+ Salary = emp.Salary,
+ }).ToList();
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo/Application/Modules/SAS/Repository/SASRepository.cs b/DataArc.EntityFrameworkCore.Demo/Application/Modules/SAS/Repository/SASRepository.cs
new file mode 100644
index 0000000..4c8fb87
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo/Application/Modules/SAS/Repository/SASRepository.cs
@@ -0,0 +1,35 @@
+using DataArc.Core;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.SolidArc.Repository;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;
+
+namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.SAS.Repository
+{
+ internal class SASRepository : ISolidArcRepository
+ {
+ private readonly IQueryFactory _queryFactory;
+ public SASRepository(IQueryFactory queryFactory)
+ {
+ _queryFactory = queryFactory;
+ }
+
+ public async Task> GetTopRatedEmployeesAsync(double rating)
+ {
+ var topRatedEmployeesQuery = await _queryFactory.CreateQueryAsync();
+
+ var topRatedEmployees = await topRatedEmployeesQuery
+ .UseDbExecutionContext()
+ .ReadWhereAsync(e => e.Rating > rating);
+
+ return topRatedEmployees
+ .Select(emp => new EmployeeDto()
+ {
+ Id = emp.Id,
+ Name = emp.Name,
+ Surname = emp.Surname,
+ Salary = emp.Salary,
+ }).ToList();
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo/DataArc.EntityFrameworkCore.Demo.csproj b/DataArc.EntityFrameworkCore.Demo/DataArc.EntityFrameworkCore.Demo.csproj
index c8e4155..7d3b007 100644
--- a/DataArc.EntityFrameworkCore.Demo/DataArc.EntityFrameworkCore.Demo.csproj
+++ b/DataArc.EntityFrameworkCore.Demo/DataArc.EntityFrameworkCore.Demo.csproj
@@ -1,7 +1,7 @@
- Exe
+ Library
net6.0;net7.0;net8.0;net9.0;net10.0
$(NoWarn);NETSDK1138
false
@@ -10,33 +10,7 @@
-
- Always
- PreserveNewest
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/DataArc.EntityFrameworkCore.Demo/DemoRegistration.cs b/DataArc.EntityFrameworkCore.Demo/DemoRegistration.cs
new file mode 100644
index 0000000..7002267
--- /dev/null
+++ b/DataArc.EntityFrameworkCore.Demo/DemoRegistration.cs
@@ -0,0 +1,28 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+using DataArc.EntityFrameworkCore.Demo.Application.Modules.Google.Repository;
+using DataArc.EntityFrameworkCore.Demo.Application.Modules.Microsoft.Repository;
+using DataArc.EntityFrameworkCore.Demo.Application.Modules.OpenAi.Repository;
+using DataArc.EntityFrameworkCore.Demo.Application.Modules.SAS.Repository;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Google.Repository;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Microsoft.Repository;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.OpenAi.Repository;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.SolidArc.Repository;
+
+namespace DataArc.EntityFrameworkCore.Demo
+{
+ public static class DemoRegistration
+ {
+ public static IServiceCollection AddRepositories(this IServiceCollection services, ConfigurationManager configurationManager)
+ {
+ // Register repositories
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+ return services;
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataArc.EntityFrameworkCore.Demo/appsettings.json b/DataArc.EntityFrameworkCore.Demo/appsettings.json
deleted file mode 100644
index 1f25904..0000000
--- a/DataArc.EntityFrameworkCore.Demo/appsettings.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "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.Demos.sln b/DataArc.EntityFrameworkCore.Demos.sln
index 3d03e93..e93dd59 100644
--- a/DataArc.EntityFrameworkCore.Demos.sln
+++ b/DataArc.EntityFrameworkCore.Demos.sln
@@ -14,6 +14,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataArc.EntityFrameworkCore
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataArc.EntityFrameworkCore.Demo.Persistence", "DataArc.EntityFrameworkCore.Demo.Persistence\DataArc.EntityFrameworkCore.Demo.Persistence.csproj", "{1C6E657C-867F-495A-8487-70266BFDDD66}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataArc.EntityFrameworkCore.Demo.Host", "DataArc.EntityFrameworkCore.Demo.Host\DataArc.EntityFrameworkCore.Demo.Host.csproj", "{4519DD98-30FD-4606-AFED-B78702C70E71}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{A3B03307-7F8C-4F25-8462-5E1E2992A056}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Host", "Host", "{A7EF0A3E-E6A2-4133-BC21-11854A2329A0}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{65C31E51-37E4-4BBC-B758-E4AA01D42307}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{8A82B2F6-B34D-487B-BE37-947F8D5C7820}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataArc.EntityFrameworkCore.Demo.Contracts", "DataArc.EntityFrameworkCore.Demo.Contracts\DataArc.EntityFrameworkCore.Demo.Contracts.csproj", "{7B2B6DE8-AFB0-49FC-BB14-97D466049515}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -32,10 +44,25 @@ Global
{1C6E657C-867F-495A-8487-70266BFDDD66}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C6E657C-867F-495A-8487-70266BFDDD66}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C6E657C-867F-495A-8487-70266BFDDD66}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4519DD98-30FD-4606-AFED-B78702C70E71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4519DD98-30FD-4606-AFED-B78702C70E71}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4519DD98-30FD-4606-AFED-B78702C70E71}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4519DD98-30FD-4606-AFED-B78702C70E71}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7B2B6DE8-AFB0-49FC-BB14-97D466049515}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7B2B6DE8-AFB0-49FC-BB14-97D466049515}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7B2B6DE8-AFB0-49FC-BB14-97D466049515}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7B2B6DE8-AFB0-49FC-BB14-97D466049515}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {3D577C7C-117D-F53F-64F7-1B0C0079336E} = {A3B03307-7F8C-4F25-8462-5E1E2992A056}
+ {297A96A5-D98A-4DFE-AE5C-A7B61884C5BA} = {8A82B2F6-B34D-487B-BE37-947F8D5C7820}
+ {1C6E657C-867F-495A-8487-70266BFDDD66} = {65C31E51-37E4-4BBC-B758-E4AA01D42307}
+ {4519DD98-30FD-4606-AFED-B78702C70E71} = {A7EF0A3E-E6A2-4133-BC21-11854A2329A0}
+ {7B2B6DE8-AFB0-49FC-BB14-97D466049515} = {A3B03307-7F8C-4F25-8462-5E1E2992A056}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A15F1185-EE70-45D3-BB2F-B3B5F97AA8F6}
EndGlobalSection
diff --git a/README.md b/README.md
index aac9907..2c3a3ed 100644
--- a/README.md
+++ b/README.md
@@ -1,151 +1,181 @@
-# DataArc.EntityFrameworkCore
+# DataArc.EntityFrameworkCore Demo
-> EF Core execution for background workers, batch jobs, bulk operations, cross-context reads, parallel writes, and structured results.
+> Fast, explicit EF Core execution for background workers, batch jobs, company database onboarding, bulk operations, parallel writes, database/script generation, and structured execution results.
-**DataArc.EntityFrameworkCore** uses EF Core.
+**DataArc.EntityFrameworkCore** uses EF Core and adds a dedicated execution layer for workflows that need to target more than one `DbContext` or database boundary.
-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 generic-host worker. It models fast employee data processing across four separate company databases:
-This demo runs as a small .NET background worker.
+```text
+SAS_GoogleDb
+SAS_MicrosoftDb
+SAS_OpenAiDb
+SAS_Db
+```
-The worker executes a finance-style data workflow:
+Each company database has its own EF Core `DbContext`, its own public DataArc execution-context contract, and its own database creation/seed path.
+
+The workflow:
```text
-Read employees from HR.
+Create and seed four separate company databases.
+Read employees through explicit company execution boundaries.
Apply salary adjustment rules.
-Bulk write the adjusted data into Finance, IT, and Operations.
-Query the resulting data across all four contexts.
+Bulk write adjusted employee data.
+Query top-rated employees per company database.
+Consolidate the result in application code.
Return structured execution results.
```
----
+The demo intentionally avoids a repository-per-table design. Repositories describe meaningful workflow operations, such as getting top-rated employees from a company database, rather than wrapping every table with a generic repository.
-## What DataArc Adds To EF Core
-
-The demo proves four practical things:
-
-1. **Explicit execution boundaries**
- Application code chooses which EF Core boundary should execute the work.
+---
-2. **Parallel bulk writes**
- One command pipeline writes to multiple EF Core contexts in parallel.
+## What This Demo Shows
-3. **Cross-context reads**
- One query pipeline can compose a read model across multiple contexts.
+This demo focuses on a practical EF Core problem:
-4. **Structured results**
- Command execution returns success or failure, affected records, and exception detail in one result shape.
+> How do you run repeatable, high-volume data workflows across separate database boundaries without spreading EF Core plumbing across the application?
-The central idea:
+DataArc.EntityFrameworkCore gives the workflow an execution model:
```text
Use EF Core.
-Keep DbContexts isolated.
+Hide concrete DbContexts.
+Expose public execution-context contracts.
Choose execution boundaries explicitly.
Build command/query pipelines.
Execute the workflow.
Receive structured results.
```
-A .NET `BackgroundService` gives the workflow a familiar home.
+A .NET `BackgroundService` gives the workflow a familiar host.
-DataArc.EntityFrameworkCore gives the workflow its execution model.
+DataArc.EntityFrameworkCore gives the workflow its execution layer.
---
-## Benchmark Snapshot
+## Key Benefits Demonstrated
-The benchmark inserts employee records into four EF Core contexts:
+### 1. Hidden DbContexts
-- `HrDbContext`
-- `FinanceDbContext`
-- `ItDbContext`
-- `OperationsDbContext`
+Concrete EF Core `DbContext` classes stay inside the persistence layer.
-Each benchmark case inserts `RecordCount` employees into each participating context.
+Application code targets public DataArc execution-context contracts such as:
+
+```csharp
+IGoogleDbContext
+IMicrosoftDbContext
+IOpenAiDbContext
+ISolidArcDbContext
+```
+
+This keeps EF Core implementation detail out of the workflow code.
+
+### 2. Less Repository-Per-Table Sprawl
+
+The demo uses repositories as intention-revealing workflow boundaries.
+
+Instead of creating this kind of shape:
```text
-Total inserted records = RecordCount x 4
+IEmployeeRepository
+IEmployeeRatingRepository
+IEmployeeSalaryRepository
+IUnitOfWork
+TopRatedEmployeeService
```
-| Method | Record Count Per Context | Total Inserted Records | Mean | StdDev | Completed Work Items | Lock Contentions | Gen0 | Allocated |
-|---|---:|---:|---:|---:|---:|---:|---:|---:|
-| ExecuteParallelBulkInsertAsync | 62,500 | 250,000 | 543.5 ms | 43.35 ms | 19,109 | - | 5,000 | 60.73 MB |
-| ExecuteParallelBulkInsertAsync | 125,000 | 500,000 | 793.7 ms | 123.56 ms | 38,686 | - | 10,000 | 121.25 MB |
-| ExecuteParallelBulkInsertAsync | 250,000 | 1,000,000 | 1,879.3 ms | 194.56 ms | 77,531 | - | 20,000 | 242.11 MB |
+the demo uses focused operations:
-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.
+```csharp
+_googleRepository.GetTopRatedEmployeesAsync(rating);
+_microsoftRepository.GetTopRatedEmployeesAsync(rating);
+_openAiRepository.GetTopRatedEmployeesAsync(rating);
+_solidArcRepository.GetTopRatedEmployeesAsync(rating);
+```
-The useful signal is the execution shape: one explicit command pipeline, four EF Core contexts, parallel execution, structured result handling, and no reported lock contentions in this run.
+The repository describes the business operation. DataArc handles execution routing.
----
+### 3. No Command/Query Class Explosion
+
+The demo does not need command and query files for every table and database permutation.
+
+Instead of creating separate command/query classes for every operation, the workflow uses factory interfaces:
+
+```csharp
+ICommandFactory
+IQueryFactory
+```
-## Why DataArc.EntityFrameworkCore Exists
+That gives the application CQRS-style separation without forcing a file-per-operation structure for every database/table combination.
-EF Core is excellent inside one `DbContext`.
+### 4. Explicit Execution Boundaries
-Many systems eventually need data workflows that move beyond one direct context call:
+Every query or command chooses the database boundary that should execute the work.
-- read from one database and write to another
-- import data into multiple tables or contexts
-- synchronize data between operational stores
-- build reporting or workflow projections
-- reconcile data across multiple persistence boundaries
-- bulk process scheduled jobs
-- compose read models from more than one EF Core context
+Example:
+
+```csharp
+var topRatedEmployeesQuery = await _queryFactory.CreateQueryAsync();
+
+var topRatedEmployees = await topRatedEmployeesQuery
+ .UseDbExecutionContext()
+ .ReadWhereAsync(employee => employee.Rating > rating);
+```
-Plain EF Core does not provide native high-throughput bulk insert as a first-class API.
+The important part is explicit:
-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.
+```csharp
+.UseDbExecutionContext()
+```
-DataArc.EntityFrameworkCore provides the coordination layer: command/query pipelines, explicit execution contexts, parallel execution, affected-record aggregation, failure handling, and structured results.
+That tells DataArc which EF Core boundary should run the query.
-It is designed for EF Core work that needs:
+### 5. Bulk and Parallel Operations
-- isolated context boundaries
-- command/query separation
-- bulk operations
-- parallel execution
-- cross-context reads
-- structured execution results
-- transaction-aware command flows
-- logging-friendly outcomes
-- high-volume scheduled jobs
-- predictable workflow execution
+The demo runs high-volume EF Core work using DataArc command pipelines.
+
+The command pipeline can target multiple execution contexts and then execute the work in parallel.
+
+### 6. Database and Script Generation Without EF Core Migration Files
+
+The demo creates databases from the current EF Core models and generates SQL scripts without requiring EF Core migration files in this sample path.
-Small CRUD applications can often be scaffolded, generated, and maintained with standard EF Core patterns.
+That makes the demo easy to reset, inspect, and run.
-DataArc.EntityFrameworkCore adds the most value when the data work becomes coordinated: multiple boundaries, larger data movement, repeatable workflows, clear execution results, and predictable success or failure reporting.
+### 7. Persistence Layer Flexibility
-The value is giving EF Core work an explicit execution model.
+Application code targets contracts and factories instead of concrete EF Core classes.
+
+That makes the persistence layer easier to reorganize, replace, test, or split over time because workflow code is not coupled directly to concrete `DbContext` implementations.
---
## Demo Workflow
-The demo uses four isolated EF Core persistence boundaries:
+The demo uses four isolated company database boundaries:
```text
-HrDbContext
-FinanceDbContext
-ItDbContext
-OperationsDbContext
+GoogleDbContext
+MicrosoftDbContext
+OpenAiDbContext
+SolidArcDbContext
```
The application flow:
1. Deletes existing demo databases.
2. Creates the demo databases.
-3. Generates SQL scripts for the database operations.
-4. Seeds employee data into HR.
+3. Generates SQL scripts for database operations.
+4. Seeds company data.
5. Starts a .NET hosted worker.
-6. Reads employees from HR.
-7. Applies salary adjustment rules.
-8. Bulk writes adjusted data into Finance, IT, and Operations.
-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.
+6. Runs the salary adjustment workflow.
+7. Executes bulk work through DataArc command pipelines.
+8. Queries top-rated employees from each company database independently.
+9. Consolidates top-rated employees in application code.
+10. Prints a short summary.
+11. Stops the host after the workflow completes.
```mermaid
flowchart LR
@@ -155,85 +185,216 @@ flowchart LR
C --> Q[DataArc Query Pipeline]
C --> W[DataArc Command Pipeline]
- D --> R[DataArc Cross-Context Query]
- Q --> HR[HrDbContext]
- W --> FIN[FinanceDbContext]
- W --> IT[ItDbContext]
- W --> OPS[OperationsDbContext]
+ D --> GRepo[Google Repository]
+ D --> MRepo[Microsoft Repository]
+ D --> ORepo[OpenAI Repository]
+ D --> SRepo[SolidArc Repository]
+
+ GRepo --> GQ[Google Query Pipeline]
+ MRepo --> MQ[Microsoft Query Pipeline]
+ ORepo --> OQ[OpenAI Query Pipeline]
+ SRepo --> SQ[SolidArc Query Pipeline]
+ GQ --> GDB[GoogleDbContext]
+ MQ --> MDB[MicrosoftDbContext]
+ OQ --> ODB[OpenAiDbContext]
+ SQ --> SDB[SolidArcDbContext]
+
+ Q --> Source[Source Company Database]
+ W --> Targets[Company Database Targets]
W --> X[Parallel Execution]
X --> Y[Structured Result]
+
+ D --> Z[Application-Level Consolidation]
```
---
## Application Shape
-The demo uses the .NET generic host.
+The solution uses a host/application/contracts/persistence split.
+
+```text
+DataArc.EntityFrameworkCore.Demo.Host
+ Starts the generic host and hosted worker.
+
+DataArc.EntityFrameworkCore.Demo
+ Owns application services, modules, repositories, and workflow code.
+
+DataArc.EntityFrameworkCore.Demo.Contracts
+ Owns public DTOs and application contracts.
+
+DataArc.EntityFrameworkCore.Demo.Persistence
+ Owns EF Core contexts, execution-context contracts, database creators, seeders, and DataArc registration.
+
+DataArc.EntityFrameworkCore.Demo.Benchmark
+ Runs BenchmarkDotNet benchmarks against the DataArc EF Core execution path.
+```
+
+The host starts the workflow. The application project expresses the workflow. The persistence project owns EF Core implementation detail.
-`Program.cs` configures the generic host, registers the hosted worker, initializes the demo databases, and starts the host. The hosted worker runs the actual data workflow.
+---
+
+## Project Structure
+
+Current high-level structure:
```text
-Program.cs = host setup + startup flow
-DemoDatabaseInitializer = demo database reset, create, and seed
-DemoWorkflowWorker = background data workflow runner
-SalaryAdjustmentService = read-transform-write workflow
-EmployeePerformanceService = cross-context read model
-Persistence = EF Core contracts, contexts, registration, database creation, seeding, and initialization
+DataArc.EntityFrameworkCore.Demo
+│
+├── Application
+│ ├── Modules
+│ │ ├── Google
+│ │ │ └── Repository
+│ │ │ └── GoogleRepository.cs
+│ │ ├── Microsoft
+│ │ │ └── Repository
+│ │ │ └── MicrosoftRepository.cs
+│ │ ├── OpenAi
+│ │ │ └── Repository
+│ │ │ └── OpenAiRepository.cs
+│ │ └── SolidArc
+│ │ └── Repository
+│ │ └── SolidArcRepository.cs
+│ │
+│ ├── Features
+│ │ ├── EmployeePerformance
+│ │ │ └── Services
+│ │ │ ├── IEmployeePerformanceService.cs
+│ │ │ └── EmployeePerformanceService.cs
+│ │ └── SalaryAdjustments
+│ │ └── Services
+│ │ ├── ISalaryAdjustmentService.cs
+│ │ └── SalaryAdjustmentService.cs
+│ │
+│ └── Modules
+│ └── DemoRegistration.cs
+│
+├── Contracts
+│ └── Application
+│ ├── Dtos
+│ │ └── EmployeeDto.cs
+│ └── Modules
+│ ├── Google
+│ ├── Microsoft
+│ ├── OpenAi
+│ └── SolidArc
+│
+├── Persistence
+│ ├── DemoDatabaseInitializer.cs
+│ ├── Contracts
+│ │ ├── IGoogleDbContext.cs
+│ │ ├── IMicrosoftDbContext.cs
+│ │ ├── IOpenAiDbContext.cs
+│ │ └── ISolidArcDbContext.cs
+│ ├── Database
+│ │ ├── Creator
+│ │ ├── DBContexts
+│ │ │ ├── GoogleDbContext.cs
+│ │ │ ├── MicrosoftDbContext.cs
+│ │ │ ├── OpenAiDbContext.cs
+│ │ │ └── SolidArcDbContext.cs
+│ │ ├── DBModels
+│ │ │ ├── Employee.cs
+│ │ │ └── Employer.cs
+│ │ └── Seeders
+│ └── PersistenceRegistration.cs
+│
+├── Host
+│ ├── DemoWorkflowWorker.cs
+│ ├── Program.cs
+│ └── appsettings.json
+│
+└── Benchmark
+ └── BenchmarkDotNet benchmark project
```
-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.
+---
+
+## Repository Example
+
+A repository targets a meaningful workflow read, not a table wrapper.
```csharp
-var host = Host
- .CreateDefaultBuilder(args)
- .ConfigureServices(services =>
+using DataArc.Core;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Dtos;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Google.Repository;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Contracts;
+using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;
+
+namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Google.Repository
+{
+ internal class GoogleRepository : IGoogleRepository
{
- var configurationManager = new ConfigurationManager();
+ private readonly IQueryFactory _queryFactory;
+
+ public GoogleRepository(IQueryFactory queryFactory)
+ {
+ _queryFactory = queryFactory;
+ }
- configurationManager
- .AddJsonFile("appsettings.json", optional: false)
- .Build();
+ public async Task> GetTopRatedEmployeesAsync(double rating)
+ {
+ var topRatedEmployeesQuery = await _queryFactory.CreateQueryAsync();
+
+ var topRatedEmployees = await topRatedEmployeesQuery
+ .UseDbExecutionContext()
+ .ReadWhereAsync(employee => employee.Rating > rating);
+
+ return topRatedEmployees
+ .Select(employee => new EmployeeDto
+ {
+ Id = employee.Id,
+ Name = employee.Name,
+ Surname = employee.Surname,
+ Salary = employee.Salary
+ })
+ .ToList();
+ }
+ }
+}
+```
- services
- .AddFinanceModule(configurationManager)
- .AddHostedService();
- })
- .Build();
+The repository name and method name describe the intention.
-using var scope = host.Services.CreateScope();
+The execution context controls the database boundary.
-await DemoDatabaseInitializer.InitializeAsync(scope.ServiceProvider);
+---
-await host.RunAsync();
+## Consolidated Employee Performance
-Console.WriteLine();
-Console.WriteLine("Press any key to exit.");
-Console.ReadKey();
-```
+`EmployeePerformanceService` queries each company database independently and consolidates the result in application code.
-The database setup detail belongs in persistence. The workflow execution detail belongs inside the worker and use-case services, not in `Program.cs`.
+```csharp
+public async Task> GetConsolidatedTopRatedEmployeesAsync(double rating)
+{
+ var topRatedGoogleEmployees = await _googleRepository
+ .GetTopRatedEmployeesAsync(rating);
----
+ var topRatedMicrosoftEmployees = await _microsoftRepository
+ .GetTopRatedEmployeesAsync(rating);
-## Demo Database Initialization
+ var topRatedOpenAiEmployees = await _openAiRepository
+ .GetTopRatedEmployeesAsync(rating);
-`DemoDatabaseInitializer` lives in the persistence project because database reset, creation, script generation, and seeding are persistence setup concerns.
+ var topRatedSolidArcEmployees = await _solidArcRepository
+ .GetTopRatedEmployeesAsync(rating);
-`Program.cs` decides when the demo initialization runs. The persistence project owns how it runs.
+ var consolidatedTopRatedEmployees = new List();
-The initializer coordinates the individual database creators and seeders:
+ consolidatedTopRatedEmployees.AddRange(topRatedGoogleEmployees);
+ consolidatedTopRatedEmployees.AddRange(topRatedMicrosoftEmployees);
+ consolidatedTopRatedEmployees.AddRange(topRatedOpenAiEmployees);
+ consolidatedTopRatedEmployees.AddRange(topRatedSolidArcEmployees);
-```text
-FinanceDbCreator
-HrDbCreator
-ItDbCreator
-OperationsDbCreator
-HrDbSeeder
+ return consolidatedTopRatedEmployees;
+}
```
-Keeping the creators separate makes the setup easier to read, easier to test, and easier to troubleshoot. The more advanced database builder composition is still available, although this demo introduces database creation one database at a time for lower-friction adoption.
+This is intentional.
+
+The demo treats the company databases as separate databases. It does not join across them. Each database is read through its own execution boundary, then the application consolidates the read model.
---
@@ -264,18 +425,22 @@ internal sealed class DemoWorkflowWorker : BackgroundService
{
try
{
+ decimal salaryAdjustmentBaseRate = 0.05m;
+ decimal salaryThreshold = 10_000m;
+ double ratingThreshold = 4.5;
+
var processedCount = await _salaryAdjustmentService
.ProcessEmployeeSalaryAdjustmentsAsync(
- salaryAdjustmentBaseRate: 0.05m,
- salaryThreshold: 10_000m,
- batchSize: BatchSize);
+ salaryAdjustmentBaseRate,
+ salaryThreshold,
+ BatchSize);
Console.WriteLine($"Processed salary adjustment records: {processedCount:N0}");
if (processedCount > 0)
{
var topRatedEmployees = await _employeePerformanceService
- .GetTopRatedEmployeesAsync(rating: 4.5);
+ .GetConsolidatedTopRatedEmployeesAsync(ratingThreshold);
var topRatedEmployee = topRatedEmployees
.OrderByDescending(employee => employee.Salary)
@@ -293,6 +458,12 @@ internal sealed class DemoWorkflowWorker : BackgroundService
Console.WriteLine();
Console.WriteLine("Demo workflow completed.");
}
+ catch (Exception exception)
+ {
+ Console.WriteLine();
+ Console.WriteLine($"Demo workflow failed: {exception.Message}");
+ throw;
+ }
finally
{
_hostApplicationLifetime.StopApplication();
@@ -301,13 +472,13 @@ internal sealed class DemoWorkflowWorker : BackgroundService
}
```
-The worker is intentionally thin. It runs the workflow and leaves EF Core execution details inside the application services.
+The worker is intentionally thin. It runs the workflow and leaves EF Core execution details inside the application services and repositories.
---
## Salary Adjustment Use Case
-`SalaryAdjustmentService` reads from HR, applies the adjustment, then writes to three target contexts through one command pipeline.
+`SalaryAdjustmentService` reads employees, applies salary adjustment rules, and writes adjusted employee data through DataArc command pipelines.
```mermaid
sequenceDiagram
@@ -315,26 +486,26 @@ sequenceDiagram
participant Salary as SalaryAdjustmentService
participant Query as DataArc Query Pipeline
participant Command as DataArc Command Pipeline
- participant HR as HrDbContext
- participant Finance as FinanceDbContext
- participant IT as ItDbContext
- participant Ops as OperationsDbContext
+ participant Source as Company Source DbContext
+ participant Target1 as Company Target DbContext
+ participant Target2 as Company Target DbContext
+ participant Target3 as Company Target DbContext
Worker->>Salary: ProcessEmployeeSalaryAdjustmentsAsync(...)
- Salary->>Query: UseDbExecutionContext()
- Query->>HR: Read employees above salary threshold
- HR-->>Salary: Employee list
+ Salary->>Query: UseDbExecutionContext()
+ Query->>Source: Read employees above salary threshold
+ Source-->>Salary: Employee list
Salary->>Salary: Apply salary adjustment
- Salary->>Command: AddBulk to Finance
- Salary->>Command: AddBulk to IT
- Salary->>Command: AddBulk to Operations
+ Salary->>Command: AddBulk to target company database
+ Salary->>Command: AddBulk to target company database
+ Salary->>Command: AddBulk to target company database
Salary->>Command: ExecuteParallelAsync()
- Command->>Finance: Bulk insert
- Command->>IT: Bulk insert
- Command->>Ops: Bulk insert
+ Command->>Target1: Bulk insert
+ Command->>Target2: Bulk insert
+ Command->>Target3: Bulk insert
Command-->>Salary: Structured execution result
```
@@ -344,8 +515,8 @@ Core shape:
var employeesQuery = await _queryFactory.CreateQueryAsync();
var employees = await employeesQuery
- .UseDbExecutionContext()
- .ReadWhereAsync(employee => employee.Salary > salaryThreshold);
+ .UseDbExecutionContext()
+ .ReadWhereAsync(employee => employee.Salary > salaryThreshold);
foreach (var employee in employees)
{
@@ -355,16 +526,16 @@ foreach (var employee in employees)
var commandBuilder = await _commandFactory.CreateCommandBuilderAsync();
commandBuilder
- .UseDbExecutionContext()
- .AddBulk(employees, batchSize);
+ .UseDbExecutionContext()
+ .AddBulk(employees, batchSize);
commandBuilder
- .UseDbExecutionContext()
- .AddBulk(employees, batchSize);
+ .UseDbExecutionContext()
+ .AddBulk(employees, batchSize);
commandBuilder
- .UseDbExecutionContext()
- .AddBulk(employees, batchSize);
+ .UseDbExecutionContext()
+ .AddBulk(employees, batchSize);
var command = await commandBuilder.BuildAsync();
var commandResult = await command.ExecuteParallelAsync();
@@ -382,50 +553,14 @@ The repeated `UseDbExecutionContext()` calls are intentional. Each target is
---
-## Cross-Context Query Use Case
-
-`EmployeePerformanceService` demonstrates a cross-context read model.
-
-It starts from HR employees above a rating threshold, joins related employees across Finance, IT, and Operations, then projects the result into a DTO.
-
-```csharp
-var topRatedEmployeesQuery = await _queryFactory.CreateQueryAsync();
-
-var topRatedEmployees = await topRatedEmployeesQuery
- .UseDbExecutionContext(employee => employee.Rating > rating)
- .Join(
- bag => bag.Get()!.Id,
- financeEmployee => financeEmployee.Id)
- .Join(
- bag => bag.Get()!.Id,
- itEmployee => itEmployee.Id)
- .Join(
- bag => bag.Get()!.Id,
- operationsEmployee => operationsEmployee.Id)
- .Select(bag => new EmployeeDto
- {
- Id = bag.Get()!.Id,
- Name = bag.Get()!.Name!,
- Surname = bag.Get()!.Surname!,
- Salary = bag.Get()!.Salary
- })
- .ToListAsync();
-```
-
-This creates one read model from multiple isolated EF Core contexts without exposing the concrete `DbContext` implementations to application code.
-
----
-
## Public Execution Contracts, Internal DbContexts
-The demo exposes each EF Core boundary through a public execution-context contract.
+The demo exposes each EF Core database boundary through a public execution-context contract.
Application code depends on these contracts, not on the concrete `DbContext` implementations.
-The contracts define the DataArc execution boundaries and expose the EF Core sets available through those boundaries.
-
```csharp
-public interface IFinanceDbContext : IExecutionContext
+public interface IGoogleDbContext : IExecutionContext
{
DbSet? Employer { get; set; }
@@ -436,9 +571,9 @@ public interface IFinanceDbContext : IExecutionContext
The concrete `DbContext` implementation remains internal to the persistence project:
```csharp
-internal class FinanceDbContext : DbContext, IFinanceDbContext
+internal class GoogleDbContext : DbContext, IGoogleDbContext
{
- public FinanceDbContext(DbContextOptions dbContextOptions)
+ public GoogleDbContext(DbContextOptions dbContextOptions)
: base(dbContextOptions)
{
}
@@ -463,8 +598,8 @@ DataArc uses the public execution contract to route work to the registered EF Co
```csharp
commandBuilder
- .UseDbExecutionContext()
- .AddBulk(employees, batchSize);
+ .UseDbExecutionContext()
+ .AddBulk(employees, batchSize);
```
This keeps the concrete EF Core implementation hidden while giving the application a clear execution boundary to target.
@@ -477,7 +612,7 @@ 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`.
+The worker, services, and repositories 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();
@@ -486,17 +621,17 @@ services.ConfigureDataArc(dataArc =>
{
dataArc.UseEntityFrameworkCore(ef =>
{
- ef.AddDbExecutionContext(options =>
- options.UseSqlServer(financeConnectionString));
+ ef.AddDbExecutionContext(options =>
+ options.UseSqlServer(googleConnectionString));
- ef.AddDbExecutionContext(options =>
- options.UseSqlServer(hrConnectionString));
+ ef.AddDbExecutionContext(options =>
+ options.UseSqlServer(microsoftConnectionString));
- ef.AddDbExecutionContext(options =>
- options.UseSqlServer(itConnectionString));
+ ef.AddDbExecutionContext(options =>
+ options.UseSqlServer(openAiConnectionString));
- ef.AddDbExecutionContext(options =>
- options.UseSqlServer(operationsConnectionString));
+ ef.AddDbExecutionContext(options =>
+ options.UseSqlServer(solidArcConnectionString));
});
});
```
@@ -511,17 +646,102 @@ This connection string points to the SQL Server database.
---
+## Application Registration
+
+Application registration wires the intention-revealing repositories.
+
+```csharp
+using Microsoft.Extensions.DependencyInjection;
+
+using DataArc.EntityFrameworkCore.Demo.Application.Modules.Google.Repository;
+using DataArc.EntityFrameworkCore.Demo.Application.Modules.Microsoft.Repository;
+using DataArc.EntityFrameworkCore.Demo.Application.Modules.OpenAi.Repository;
+using DataArc.EntityFrameworkCore.Demo.Application.Modules.SolidArc.Repository;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Google.Repository;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.Microsoft.Repository;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.OpenAi.Repository;
+using DataArc.EntityFrameworkCore.Demo.Contracts.Application.Modules.SolidArc.Repository;
+
+namespace DataArc.EntityFrameworkCore.Demo.Application.Modules
+{
+ public static class DemoRegistration
+ {
+ public static IServiceCollection AddRepositories(this IServiceCollection services)
+ {
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+ return services;
+ }
+ }
+}
+```
+
+---
+
+## Demo Database Initialization
+
+`DemoDatabaseInitializer` lives in the persistence project because database reset, creation, script generation, and seeding are persistence setup concerns.
+
+`Program.cs` decides when the demo initialization runs. The persistence project owns how it runs.
+
+The initializer coordinates individual database creators and seeders:
+
+```text
+GoogleDbCreator
+MicrosoftDbCreator
+OpenAiDbCreator
+SolidArcDbCreator
+
+GoogleDbSeeder
+MicrosoftDbSeeder
+OpenAiDbSeeder
+SolidArcDbSeeder
+```
+
+```csharp
+public static class DemoDatabaseInitializer
+{
+ public static async Task InitializeAsync(IServiceProvider serviceProvider)
+ {
+ var databaseCreators = new IDatabaseCreator[]
+ {
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService()
+ };
+
+ var databaseSeeders = new IDatabaseSeeder[]
+ {
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService(),
+ serviceProvider.GetRequiredService()
+ };
+
+ await ResetDatabasesAsync(databaseCreators, databaseSeeders);
+ }
+}
+```
+
+Keeping creators and seeders separate makes setup easier to read, test, and troubleshoot.
+
+---
+
## Database Creation Without EF Core Migration Files
The demo does not use EF Core migration files.
-Each database has its own familiar creator:
+Each database has its own creator:
```text
-FinanceDbCreator
-HrDbCreator
-ItDbCreator
-OperationsDbCreator
+GoogleDbCreator
+MicrosoftDbCreator
+OpenAiDbCreator
+SolidArcDbCreator
```
Each creator implements EF Core's `IDatabaseCreator` shape and uses DataArc's database builder to create its database from the current `DbContext` model.
@@ -529,11 +749,11 @@ Each creator implements EF Core's `IDatabaseCreator` shape and uses DataArc's da
Example:
```csharp
-public class FinanceDbCreator : IFinanceDbCreator
+public class GoogleDbCreator : IGoogleDbCreator
{
private readonly IDatabaseFactory _databaseFactory;
- public FinanceDbCreator(IDatabaseFactory databaseFactory)
+ public GoogleDbCreator(IDatabaseFactory databaseFactory)
{
_databaseFactory = databaseFactory;
}
@@ -543,7 +763,7 @@ public class FinanceDbCreator : IFinanceDbCreator
var dbBuilder = _databaseFactory.CreateDatabaseBuilder();
var db = dbBuilder
- .IncludeDbContext()
+ .IncludeDbContext()
.Build(generateScripts: true, applyChanges: true);
db.ExecuteCreate();
@@ -556,7 +776,7 @@ public class FinanceDbCreator : IFinanceDbCreator
var dbBuilder = _databaseFactory.CreateDatabaseBuilder();
var db = dbBuilder
- .IncludeDbContext()
+ .IncludeDbContext()
.Build(generateScripts: true, applyChanges: true);
db.ExecuteDrop();
@@ -566,7 +786,7 @@ public class FinanceDbCreator : IFinanceDbCreator
}
```
-EF Core's `EnsureCreated()` can create a database from a model. DataArc's builder adds reviewable script generation without requiring EF Core migration files in this demo.
+EF Core can create a database from a model. DataArc's builder adds reviewable SQL script generation in this demo path.
Generated SQL scripts are written to the app output directory under `Scripts`.
@@ -579,10 +799,10 @@ bin/Debug/net8.0/Scripts
The generated scripts include database creation, schema creation, table creation, and constraints.
```sql
-CREATE DATABASE [FinanceDb];
+CREATE DATABASE [SAS_GoogleDb];
GO
-USE [FinanceDb];
+USE [SAS_GoogleDb];
GO
IF SCHEMA_ID('employees') IS NULL EXEC('CREATE SCHEMA [employees]');
@@ -590,8 +810,17 @@ GO
CREATE TABLE [employees].[Employees] (
[Id] int IDENTITY(1,1) NOT NULL,
+ [CreatedUtc] datetime2 NOT NULL,
+ [EmployerId] int NOT NULL,
+ [IsArchived] bit NOT NULL,
+ [LastUpdatedUtc] datetime2 NOT NULL,
[EmployeeName] varchar(50) NULL,
+ [Notes] text NULL,
+ [PositionOrder] int NOT NULL,
+ [Rating] float NOT NULL,
[EmployeeSalary] decimal(33,2) NOT NULL,
+ [Status] int NOT NULL,
+ [EmployeeNameSurname] varchar(101) NULL,
PRIMARY KEY ([Id])
);
GO
@@ -599,77 +828,6 @@ GO
---
-## Project Structure
-
-```text
-DataArc.EntityFrameworkCore.Demo
-│
-├── Application
-│ ├── Modules
-│ │ └── Finance
-│ │ ├── Features
-│ │ │ ├── SalaryAdjustments
-│ │ │ │ ├── Dtos
-│ │ │ │ │ └── EmployeeDto.cs
-│ │ │ │ └── Services
-│ │ │ │ ├── ISalaryAdjustmentService.cs
-│ │ │ │ └── SalaryAdjustmentService.cs
-│ │ │ └── EmployeePerformance
-│ │ │ └── Services
-│ │ │ ├── IEmployeePerformanceService.cs
-│ │ │ └── EmployeePerformanceService.cs
-│ │ └── Registration
-│ │ └── FinanceModuleRegistration.cs
-│ └── Workers
-│ └── DemoWorkflowWorker.cs
-│
-├── Persistence
-│ ├── DemoDatabaseInitializer.cs
-│ ├── Contracts
-│ │ ├── IFinanceDbContext.cs
-│ │ ├── IHrDbContext.cs
-│ │ ├── IItDbContext.cs
-│ │ └── IOperationsDbContext.cs
-│ ├── Database
-│ │ ├── Creator
-│ │ │ ├── FinanceDbCreator.cs
-│ │ │ ├── HrDbCreator.cs
-│ │ │ ├── ItDbCreator.cs
-│ │ │ └── OperationsDbCreator.cs
-│ │ ├── DbContexts
-│ │ │ ├── FinanceDbContext.cs
-│ │ │ ├── HrDbContext.cs
-│ │ │ ├── ItDbContext.cs
-│ │ │ └── OperationsDbContext.cs
-│ │ ├── DbModels
-│ │ │ ├── Employee.cs
-│ │ │ └── Employer.cs
-│ │ └── Seeder
-│ │ ├── HrDbSeeder.cs
-│ │ └── SeedDataGenerator.cs
-│ └── PersistenceRegistration.cs
-│
-└── Program.cs
-```
-
-The application project owns the worker and use-case services.
-
-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.
-
-```text
-DataArc.EntityFrameworkCore.Demo
- -> DataArc.EntityFrameworkCore.Demo.Persistence
-
-DataArc.EntityFrameworkCore.Demo.Benchmark
- -> DataArc.EntityFrameworkCore.Demo.Persistence
-```
-
-The benchmark references the persistence project directly so it measures the DataArc EF Core execution path without depending on the demo application shell.
-
----
-
## Running The Demo
### 1. Configure Connection Strings
@@ -679,10 +837,10 @@ Update the SQL Server connection strings in `appsettings.json`.
```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;"
+ "GoogleDb": "Server=YOUR_SERVER;Database=SAS_GoogleDb;Integrated Security=true;TrustServerCertificate=True;",
+ "MicrosoftDb": "Server=YOUR_SERVER;Database=SAS_MicrosoftDb;Integrated Security=true;TrustServerCertificate=True;",
+ "OpenAiDb": "Server=YOUR_SERVER;Database=SAS_OpenAiDb;Integrated Security=true;TrustServerCertificate=True;",
+ "SolidArcDb": "Server=YOUR_SERVER;Database=SAS_Db;Integrated Security=true;TrustServerCertificate=True;"
}
}
```
@@ -702,13 +860,14 @@ The application will:
1. Delete existing demo databases.
2. Create demo databases.
3. Generate SQL scripts under the app output `Scripts` folder.
-4. Seed HR employee data.
+4. Seed company data.
5. Start the hosted worker.
6. Run the salary adjustment workflow.
-7. Bulk distribute adjusted employee data into Finance, IT, and Operations.
-8. Query top-rated employee details.
-9. Print a summary.
-10. Stop the host after the workflow completes.
+7. Bulk process employee records.
+8. Query top-rated employees from each company database.
+9. Consolidate the top-rated employees in application code.
+10. Print a summary.
+11. Stop the host after the workflow completes.
Expected summary shape:
@@ -718,7 +877,7 @@ Databases created successfully.
Databases seeded successfully.
Processed salary adjustment records: 300,000
-Top Rated Employee: Name12345 Surname12345, Salary: 151,234.56, Number of top rated employees: 1,234
+Top Rated Employee: Name4040 Surname4040, Salary: 157,476.36, Number of top rated employees: 49,812
Demo workflow completed.
@@ -727,6 +886,54 @@ Press any key to exit.
---
+## Example Run Output
+
+A successful run creates, seeds, processes, and summarizes the demo workflow.
+
+```text
+Databases deleted successfully.
+Databases created successfully.
+Databases seeded successfully.
+Processed salary adjustment records: 300,000
+
+Top Rated Employee: Name4040 Surname4040, Salary: 157,476.36, Number of top rated employees: 49,812
+
+Demo workflow completed.
+```
+
+The numbers are sample output from one local run. Your result can differ because seed data, runtime environment, SQL Server configuration, and machine performance affect the final result.
+
+---
+
+## Benchmark Snapshot
+
+The benchmark inserts employee records into four EF Core contexts:
+
+```text
+GoogleDbContext
+MicrosoftDbContext
+OpenAiDbContext
+SolidArcDbContext
+```
+
+Each benchmark case inserts `RecordCount` employees into each participating context.
+
+```text
+Total inserted records = RecordCount x 4
+```
+
+| Method | Record Count Per Context | Total Inserted Records | Mean | Error | StdDev | Completed Work Items | Lock Contentions | Gen0 | Allocated |
+|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
+| ExecuteParallelBulkInsertAsync | 62,500 | 250,000 | 560.5 ms | 101.2 ms | 60.22 ms | 19,084 | - | 5,000 | 60.72 MB |
+| ExecuteParallelBulkInsertAsync | 125,000 | 500,000 | 1,077.9 ms | 190.5 ms | 126.01 ms | 38,123 | - | 10,000 | 120.97 MB |
+| ExecuteParallelBulkInsertAsync | 250,000 | 1,000,000 | 1,964.6 ms | 379.9 ms | 251.26 ms | 77,777 | - | 20,000 | 242.2 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.
+
+The useful signal is the execution shape: one explicit command pipeline, four EF Core contexts, parallel execution, structured result handling, and no reported lock contentions in this run.
+
+---
+
## Running The Benchmark
From the benchmark project:
@@ -756,8 +963,8 @@ BenchmarkDotNet v0.15.8
OS: Windows 11 25H2 / 2025 Update
CPU: 13th Gen Intel Core i7-13700H 2.40GHz
Cores: 14 physical, 20 logical
-.NET SDK: 10.0.300
-Runtime: .NET 8.0.27
+.NET SDK: 10.0.301
+Runtime: .NET 8.0.28
JIT: RyuJIT x64
InvocationCount: 1
IterationCount: 10
@@ -772,8 +979,8 @@ Diagnosers: MemoryDiagnoser, ThreadingDiagnoser
xychart-beta
title "Parallel bulk insert mean time"
x-axis ["250k", "500k", "1M"]
- y-axis "Mean time in ms" 0 --> 2000
- bar [543.5, 793.7, 1879.3]
+ y-axis "Mean time in ms" 0 --> 2200
+ bar [560.5, 1077.9, 1964.6]
```
```mermaid
@@ -781,7 +988,7 @@ xychart-beta
title "Managed memory allocated per operation"
x-axis ["250k", "500k", "1M"]
y-axis "Allocated MB" 0 --> 260
- bar [60.73, 121.25, 242.11]
+ bar [60.72, 120.97, 242.2]
```
---
@@ -792,11 +999,13 @@ DataArc.EntityFrameworkCore supports a 14-day trial so teams can test real workl
Use the trial to validate:
-- cross-context queries
- command pipelines
+- query pipelines
- bulk operations
+- parallel execution
- scheduled workload behavior
- logging and execution reporting
+- database/script generation in the demo path
---
@@ -812,18 +1021,20 @@ The goal is explicit execution, not hidden coupling.
## Summary
-DataArc.EntityFrameworkCore gives EF Core an explicit execution layer for high-throughput workflows that cross `DbContext` boundaries.
+DataArc.EntityFrameworkCore gives EF Core an explicit execution layer for high-throughput workflows that cross `DbContext` and database boundaries.
It helps teams:
- define public execution contracts while keeping concrete `DbContext` implementations internal
-- coordinate work across multiple EF Core contexts
-- execute bulk command pipelines
+- reduce repository-per-table sprawl
+- avoid command/query class explosion for every table and database permutation
+- use CQRS-style command/query factories
+- coordinate bulk command pipelines
- run parallel operations
-- compose cross-context read models
- create databases and generate scripts without EF Core migration files in this demo path
- return structured execution results
- log and measure workflow execution
+- keep application code focused on workflow intent rather than EF Core plumbing
**EF Core owns data access.**