Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: PR Build

on:
pull_request:
branches:
- main

workflow_dispatch:

permissions:
contents: read

jobs:
build:
name: Restore and Build
runs-on: windows-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup .NET SDKs
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
9.0.x
10.0.x

- name: Show .NET info
run: dotnet --info

- name: Show NuGet sources
run: dotnet nuget list source

- name: Restore from nuget.org
run: dotnet restore --configfile nuget.config

- name: Build
run: dotnet build --configuration Release --no-restore
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,25 @@ namespace DataArc.EntityFrameworkCore.Demo.Benchmark
[ThreadingDiagnoser]
[SimpleJob]
[WarmupCount(1)]
[IterationCount(4)]
[IterationCount(10)]
[InvocationCount(1)]
[Config(typeof(BenchmarkConfig))]
public class RawEmployeeBulkDataBenchmark
{
private const int MaxRecordCount = 1_000_000;

private IServiceProvider? _serviceProvider;
private IDatabaseCreator? _databaseCreator;
private ICommandFactory? _commandFactory;
private IReadOnlyList<Employee>? _employees;

[Params(10_000, 100_000, 250_000)]
private List<Employee> _allEmployees = [];
private List<Employee> _benchmarkEmployees = [];

[Params(250_000, 500_000, 1_000_000)]
public int RecordCount { get; set; }
public int BulkBatchSize => RecordCount;

[Params(250_000)]
public int BulkBatchSize { get; set; }

[GlobalSetup]
public void GlobalSetup()
Expand All @@ -42,6 +49,10 @@ public void GlobalSetup()

_databaseCreator = _serviceProvider.GetRequiredService<IDatabaseCreator>();
_commandFactory = _serviceProvider.GetRequiredService<ICommandFactory>();

_allEmployees = SeedDataGenerator
.GenerateHrSeedData(MaxRecordCount)
.ToList();
}

[IterationSetup]
Expand All @@ -56,34 +67,40 @@ public void IterationSetup()
if (!_databaseCreator.EnsureCreated())
throw new InvalidOperationException("Failed to create benchmark databases.");

_employees = SeedDataGenerator.GenerateHrSeedData(RecordCount);
_benchmarkEmployees = _allEmployees
.Take(RecordCount)
.ToList();
}

[Benchmark]
public async Task<int> ExecuteParallelBulkInsertAsync()
{
var commandBuilder = await _commandFactory!.CreateCommandBuilderAsync();
if (_commandFactory == null)
throw new InvalidOperationException($"{nameof(ICommandFactory)} was not resolved.");

if (_benchmarkEmployees.Count == 0)
throw new InvalidOperationException("Benchmark employee data was not generated.");

var commandBuilder = await _commandFactory.CreateCommandBuilderAsync();

if (commandBuilder == null)
throw new InvalidOperationException($"{nameof(commandBuilder)} was not resolved.");

if (_employees == null)
throw new InvalidOperationException("Benchmark employee data was not generated.");

commandBuilder
.UseDbExecutionContext<IHrDbContext>()
.AddBulk(_employees, BulkBatchSize);
.AddBulk(_benchmarkEmployees, BulkBatchSize);

commandBuilder
.UseDbExecutionContext<IFinanceDbContext>()
.AddBulk(_employees, BulkBatchSize);
.AddBulk(_benchmarkEmployees, BulkBatchSize);

commandBuilder.UseDbExecutionContext<IItDbContext>()
.AddBulk(_employees, BulkBatchSize);
commandBuilder
.UseDbExecutionContext<IItDbContext>()
.AddBulk(_benchmarkEmployees, BulkBatchSize);

commandBuilder
.UseDbExecutionContext<IOperationsDbContext>()
.AddBulk(_employees, BulkBatchSize);
.AddBulk(_benchmarkEmployees, BulkBatchSize);

var command = await commandBuilder.BuildAsync();
var commandResult = await command.ExecuteParallelAsync();
Expand All @@ -100,8 +117,7 @@ public async Task<int> ExecuteParallelBulkInsertAsync()
[GlobalCleanup]
public void GlobalCleanup()
{
//_databaseCreator?.EnsureDeleted();
// Cleanup if needed after all iterations are complete
//_databaseCreator?.EnsureDeleted();
}
}

Expand Down Expand Up @@ -144,7 +160,6 @@ public string GetValue(Summary summary, BenchmarkCase benchmarkCase)
.Single(parameter => parameter.Name == "RecordCount");

var recordCount = Convert.ToInt32(recordCountParameter.Value);

var totalInsertedRecords = recordCount * _destinationContextCount;

return totalInsertedRecords.ToString("N0");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public bool EnsureCreated()
.IncludeDbContext<HrDbContext>()
.IncludeDbContext<ItDbContext>()
.IncludeDbContext<OperationsDbContext>()
.Build(applyChanges: true, generateScripts: true); //Generates SQL scripts for the database changes and applies them to the databases. This is useful for debugging and auditing purposes.
.Build(applyChanges:true);

// Drop and create databases
db.ExecuteCreate();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using DataArc.Core;

using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Dtos;
using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts;
using DataArc.EntityFrameworkCore.Demo.Persistence.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<List<EmployeeDto>> GetTopRatedEmployeesAsync(double rating)
{
var topRatedEmployeesQuery = await _queryFactory.CreateQueryAsync();

var topRatedEmployees = await topRatedEmployeesQuery
.UseDbExecutionContext<IHrDbContext, Employee>(e => e.Rating > rating)
.Join<IFinanceDbContext, Employee>
(bag => bag.Get<Employee>()!.Id, f => f.Id)
.Join<IItDbContext, Employee>
(bag => bag.Get<Employee>()!.Id, i => i.Id)
.Join<IOperationsDbContext, Employee>(bag => bag.Get<Employee>()!.Id, o => o.Id)
.Select(bag => new EmployeeDto()
{
Id = bag.Get<Employee>()!.Id,
Name = bag.Get<Employee>()!.Name!,
Surname = bag.Get<Employee>()!.Surname!,
Salary = bag.Get<Employee>()!.Salary!

}).ToListAsync();

return topRatedEmployees;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Dtos;

namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Services
namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services
{
public interface IFinanceService
public interface IEmployeePerformanceService
{
Task<int> ProcessEmployeeFinanceDataAsync(decimal salaryAdjustmentBaseRate, decimal salaryThreshold, int batchSize);
Task<List<EmployeeDto>> GetTopRatedEmployeesAsync(double rating);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Dtos;

namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services
{
public interface ISalaryAdjustmentService
{
Task<int> ProcessEmployeeSalaryAdjustmentsAsync(decimal salaryAdjustmentBaseRate, decimal salaryThreshold, int batchSize);
}
}
Original file line number Diff line number Diff line change
@@ -1,45 +1,22 @@
using DataArc.Core;
using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Dtos;

using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBContexts;
using DataArc.EntityFrameworkCore.Demo.Persistence.Database.DBModels;

namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Services
namespace DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services
{
public class FinanceService : IFinanceService
internal class SalaryAdjustmentService : ISalaryAdjustmentService
{
private readonly ICommandFactory _commandFactory;
private readonly IQueryFactory _queryFactory;

public FinanceService(IQueryFactory queryFactory, ICommandFactory commandFactory)
public SalaryAdjustmentService(ICommandFactory commandFactory, IQueryFactory queryFactory)
{
_queryFactory = queryFactory;
_commandFactory = commandFactory;
_queryFactory = queryFactory;
}

public async Task<List<EmployeeDto>> GetTopRatedEmployeesAsync(double rating)
{
var topRatedEmployeesQuery = await _queryFactory.CreateQueryAsync();

var topRatedEmployees = await topRatedEmployeesQuery
.UseDbExecutionContext<IHrDbContext, Employee>(e => e.Rating > rating)
.Join<IFinanceDbContext, Employee>
(bag => bag.Get<Employee>()!.Id, f => f.Id)
.Join<IItDbContext, Employee>
(bag => bag.Get<Employee>()!.Id, i => i.Id)
.Join<IOperationsDbContext, Employee>(bag => bag.Get<Employee>()!.Id, o => o.Id)
.Select(bag => new EmployeeDto()
{
Id = bag.Get<Employee>()!.Id,
Name = bag.Get<Employee>()!.Name!,
Surname = bag.Get<Employee>()!.Surname!,
Salary = bag.Get<Employee>()!.Salary!

}).ToListAsync();

return topRatedEmployees;
}

public async Task<int> ProcessEmployeeFinanceDataAsync(
public async Task<int> ProcessEmployeeSalaryAdjustmentsAsync(
decimal salaryAdjustmentBaseRate,
decimal salaryThreshold,
int batchSize)
Expand All @@ -56,7 +33,8 @@ public async Task<int> ProcessEmployeeFinanceDataAsync(
employee.Salary += employee.Salary * salaryAdjustmentBaseRate;
}

var commandBuilder = await _commandFactory.CreateCommandBuilderAsync();
var commandBuilder = await _commandFactory
.CreateCommandBuilderAsync();

commandBuilder
.UseDbExecutionContext<IFinanceDbContext>()
Expand All @@ -72,7 +50,7 @@ public async Task<int> ProcessEmployeeFinanceDataAsync(

// Build the command builder pipeline and execute the command in parallel across the different contexts
var command = await commandBuilder.BuildAsync();
var commandResult = await command.ExecuteParallelAsync();
var commandResult = await command.ExecuteAsync();

// Check the command result for success and handle any errors or exceptions
if (!commandResult.Success) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Services;
using DataArc.EntityFrameworkCore.Demo.Application.Modules.Finance.Features.SalaryAdjustments.Services;
using DataArc.EntityFrameworkCore.Demo.Persistence;
using Microsoft.Extensions.DependencyInjection;

Expand All @@ -10,8 +10,9 @@ public static IServiceCollection AddFinanceModule(this IServiceCollection servic
{
// Register persistence layer for Finance module
services.AddPersistence();
// Register FinanceService and its dependencies
services.AddScoped<IFinanceService, FinanceService>();
// Register Services
services.AddScoped<ISalaryAdjustmentService, SalaryAdjustmentService>();
services.AddScoped<IEmployeePerformanceService, EmployeePerformanceService>();
return services;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<ItemGroup>
<None Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
Expand Down
Loading
Loading