Add in-memory DB support for .NET 4.8 and .NET 8 testing

Introduced a `DbConnection` constructor in `JobRunnerDbContext`
for Entity Framework 6 to enable in-memory testing with the
Effort library. Added conditional compilation to support both
.NET Framework 4.8 and .NET 8.

Implemented `AddInfrastructureInMemory` in `DependencyExtension`
to register services with in-memory databases:
- For .NET 4.8, uses Effort's transient connection.
- For .NET 8, uses EF Core's in-memory provider.

Registered AutoMapper in both methods for object mapping. Added
`EntityMappingProfile` to define AutoMapper configurations,
enforcing explicit DTO-to-entity mappings. These changes improve
testability and maintainability across .NET versions.
This commit is contained in:
2026-07-09 15:45:18 +02:00
parent 5eca5f1d4b
commit 574e5ed209
3 changed files with 84 additions and 0 deletions

View File

@@ -1,5 +1,6 @@
#if NET48 #if NET48
using System.Data.Entity; using System.Data.Entity;
using System.Data.Common;
#else #else
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
#endif #endif
@@ -41,6 +42,13 @@ namespace ECMJobRunner.Infrastructure.Data
public JobRunnerDbContext(string connectionString) : base(connectionString) public JobRunnerDbContext(string connectionString) : base(connectionString)
{ {
} }
/// <summary>
/// Constructor with DbConnection for Entity Framework 6 (used for InMemory testing with Effort)
/// </summary>
public JobRunnerDbContext(DbConnection connection) : base(connection, contextOwnsConnection: true)
{
}
#else #else
/// <summary> /// <summary>
/// Constructor for Entity Framework Core (.NET 8) /// Constructor for Entity Framework Core (.NET 8)

View File

@@ -45,5 +45,59 @@ namespace ECMJobRunner.Infrastructure
return services; return services;
} }
#if NET48
/// <summary>
/// Add Infrastructure services with InMemory database for testing (.NET Framework 4.8 - uses Effort)
/// </summary>
/// <param name="services">Service collection</param>
/// <returns>Service collection for chaining</returns>
public static IServiceCollection AddInfrastructureInMemory(this IServiceCollection services)
{
// Register DbContext as scoped with Effort InMemory connection
services.AddScoped(provider =>
{
var connection = Effort.DbConnectionFactory.CreateTransient();
return new JobRunnerDbContext(connection);
});
// Register AutoMapper
services.AddAutoMapper(typeof(DependencyExtension).Assembly);
// Register repositories as scoped (same lifetime as DbContext)
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
services.AddScoped<ICfgProfileRepository, CfgProfileRepository>();
services.AddScoped<IProfileSqlJobRepository, ProfileSqlJobRepository>();
services.AddScoped<IProfileHistoryRepository, ProfileHistoryRepository>();
return services;
}
#else
/// <summary>
/// Add Infrastructure services with InMemory database for testing (.NET 8 - uses EF Core InMemory)
/// </summary>
/// <param name="services">Service collection</param>
/// <param name="databaseName">Optional database name (default: random GUID)</param>
/// <returns>Service collection for chaining</returns>
public static IServiceCollection AddInfrastructureInMemory(this IServiceCollection services, string? databaseName = null)
{
var dbName = databaseName ?? $"TestDb_{System.Guid.NewGuid()}";
// Register DbContext as scoped with InMemory database
services.AddDbContext<JobRunnerDbContext>(options =>
options.UseInMemoryDatabase(dbName));
// Register AutoMapper
services.AddAutoMapper(typeof(DependencyExtension).Assembly);
// Register repositories as scoped (same lifetime as DbContext)
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
services.AddScoped<ICfgProfileRepository, CfgProfileRepository>();
services.AddScoped<IProfileSqlJobRepository, ProfileSqlJobRepository>();
services.AddScoped<IProfileHistoryRepository, ProfileHistoryRepository>();
return services;
}
#endif
} }
} }

View File

@@ -0,0 +1,22 @@
using AutoMapper;
using ECMJobRunner.Domain.Entities;
namespace ECMJobRunner.Infrastructure.Mapping
{
/// <summary>
/// AutoMapper profile for entity mappings
/// </summary>
public class EntityMappingProfile : Profile
{
/// <summary>
/// Constructor configuring AutoMapper mappings for all entities
/// </summary>
public EntityMappingProfile()
{
// Note: object -> Entity mappings are intentionally removed
// AutoMapper cannot map from object type due to reflection limitations
// Callers should use explicit DTO types and create specific mappings
// Example: CreateMap<YourDto, CfgProfile>() in your own AutoMapper profile
}
}
}