Skip to content

Commit 8a0960c

Browse files
committed
Enhance EF Core integration and event handling
- Added comments and improved logging in Program.cs, ApplicationDbInitializer.cs, and DatabaseInitializer.cs for better clarity. - Introduced CollectionExtensions class with IsNullOrEmpty and AddIfNotContains methods. - Updated ApplicationDbContext to include a Connection property for Dapper access. - Added DatabaseSettings class with properties for DBProvider and ConnectionString. - Enhanced repository event handling with EventAddingRepositoryDecorator. - Introduced new event classes: EntityCreatedEvent, EntityDeletedEvent, and EntityUpdatedEvent. - Added MultitenantDatabaseInitializer for multi-tenant database initialization. - Commented out DapperRepository class, indicating it may be under review.
1 parent d547e2f commit 8a0960c

File tree

14 files changed

+387
-94
lines changed

14 files changed

+387
-94
lines changed

src/Genocs.Core.Demo.WebApi/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565

6666
app.UseGenocs();
6767

68+
// Use it only in case you are using EF Core
6869
await app.Services.InitializeDatabasesAsync();
6970

7071
// Configure the HTTP request pipeline.
Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,35 @@
1-
namespace Genocs.Core.Collections.Extensions
1+
namespace Genocs.Core.Collections.Extensions;
2+
3+
/// <summary>
4+
/// Extension methods for Collections.
5+
/// </summary>
6+
public static class CollectionExtensions
27
{
8+
/// <summary>
9+
/// Checks whatever given collection object is null or has no item.
10+
/// </summary>
11+
public static bool IsNullOrEmpty<T>(this ICollection<T> source)
12+
{
13+
return source == null || source.Count <= 0;
14+
}
315

416
/// <summary>
5-
/// Extension methods for Collections.
17+
/// Adds an item to the collection if it's not already in the collection.
618
/// </summary>
7-
public static class CollectionExtensions
19+
/// <param name="source">Collection.</param>
20+
/// <param name="item">Item to check and add.</param>
21+
/// <typeparam name="T">Type of the items in the collection.</typeparam>
22+
/// <returns>Returns True if added, returns False if not.</returns>
23+
public static bool AddIfNotContains<T>(this ICollection<T> source, T item)
824
{
9-
/// <summary>
10-
/// Checks whatever given collection object is null or has no item.
11-
/// </summary>
12-
public static bool IsNullOrEmpty<T>(this ICollection<T> source)
13-
{
14-
return source == null || source.Count <= 0;
15-
}
25+
ArgumentNullException.ThrowIfNull(source);
1626

17-
/// <summary>
18-
/// Adds an item to the collection if it's not already in the collection.
19-
/// </summary>
20-
/// <param name="source">Collection.</param>
21-
/// <param name="item">Item to check and add.</param>
22-
/// <typeparam name="T">Type of the items in the collection.</typeparam>
23-
/// <returns>Returns True if added, returns False if not.</returns>
24-
public static bool AddIfNotContains<T>(this ICollection<T> source, T item)
27+
if (source.Contains(item))
2528
{
26-
if (source == null)
27-
{
28-
throw new ArgumentNullException("source");
29-
}
30-
31-
if (source.Contains(item))
32-
{
33-
return false;
34-
}
35-
36-
source.Add(item);
37-
return true;
29+
return false;
3830
}
31+
32+
source.Add(item);
33+
return true;
3934
}
4035
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Genocs.Core.Domain.Entities;
2+
3+
namespace Genocs.Core.Domain.Events;
4+
5+
public static class EntityCreatedEvent
6+
{
7+
public static EntityCreatedEvent<TEntity> WithEntity<TEntity>(TEntity entity)
8+
where TEntity : IEntity
9+
=> new(entity);
10+
}
11+
12+
public class EntityCreatedEvent<TEntity> : DomainEvent
13+
where TEntity : IEntity
14+
{
15+
internal EntityCreatedEvent(TEntity entity)
16+
=> Entity = entity;
17+
18+
public TEntity Entity { get; }
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Genocs.Core.Domain.Entities;
2+
3+
namespace Genocs.Core.Domain.Events;
4+
5+
public static class EntityDeletedEvent
6+
{
7+
public static EntityDeletedEvent<TEntity> WithEntity<TEntity>(TEntity entity)
8+
where TEntity : IEntity
9+
=> new(entity);
10+
}
11+
12+
public class EntityDeletedEvent<TEntity> : DomainEvent
13+
where TEntity : IEntity
14+
{
15+
internal EntityDeletedEvent(TEntity entity)
16+
=> Entity = entity;
17+
18+
public TEntity Entity { get; }
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Genocs.Core.Domain.Entities;
2+
3+
namespace Genocs.Core.Domain.Events;
4+
5+
public static class EntityUpdatedEvent
6+
{
7+
public static EntityUpdatedEvent<TEntity> WithEntity<TEntity>(TEntity entity)
8+
where TEntity : IEntity
9+
=> new(entity);
10+
}
11+
12+
public class EntityUpdatedEvent<TEntity> : DomainEvent
13+
where TEntity : IEntity
14+
{
15+
internal EntityUpdatedEvent(TEntity entity)
16+
=> Entity = entity;
17+
18+
public TEntity Entity { get; }
19+
}
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
using System.Data;
12
using Microsoft.EntityFrameworkCore;
23

34
namespace Genocs.Persistence.EFCore.Context;
45

5-
public class ApplicationDbContext(DbContextOptions options) : DbContext(options);
6+
public class ApplicationDbContext(DbContextOptions options) : DbContext(options)
7+
{
8+
// Used by Dapper
9+
public IDbConnection Connection => Database.GetDbConnection();
10+
}

src/Genocs.Persistence.EFCore/DatabaseSettings.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,20 @@
22

33
namespace Genocs.Persistence.EFCore;
44

5+
/// <summary>
6+
/// The DatabaseSettings class is used to configure the database connection settings.
7+
/// TODO: Rename to *Options and Move to *.Configuration namespace.
8+
/// </summary>
59
public class DatabaseSettings : IValidatableObject
610
{
11+
/// <summary>
12+
/// The database provider to use (e.g., SqlServer, MySql, PostgreSql).
13+
/// </summary>
714
public string DBProvider { get; set; } = string.Empty;
15+
16+
/// <summary>
17+
/// The connection string to the database.
18+
/// </summary>
819
public string ConnectionString { get; set; } = string.Empty;
920

1021
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)

src/Genocs.Persistence.EFCore/Extensions/EFCoreExtensions.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,11 @@ private static IServiceCollection AddRepositories(this IServiceCollection servic
127127
sp.GetRequiredService(typeof(IRepository<>).MakeGenericType(aggregateRootType)));
128128

129129
// Decorate the repositories with EventAddingRepositoryDecorators and expose them as IRepositoryWithEvents.
130-
//services.AddScoped(typeof(IRepositoryWithEvents<>).MakeGenericType(aggregateRootType), sp =>
131-
// Activator.CreateInstance(
132-
// typeof(EventAddingRepositoryDecorator<>).MakeGenericType(aggregateRootType),
133-
// sp.GetRequiredService(typeof(IRepository<>).MakeGenericType(aggregateRootType)))
134-
// ?? throw new InvalidOperationException($"Couldn't create EventAddingRepositoryDecorator for aggregateRootType {aggregateRootType.Name}"));
130+
services.AddScoped(typeof(IRepositoryWithEvents<>).MakeGenericType(aggregateRootType), sp =>
131+
Activator.CreateInstance(
132+
typeof(EventAddingRepositoryDecorator<>).MakeGenericType(aggregateRootType),
133+
sp.GetRequiredService(typeof(IRepository<>).MakeGenericType(aggregateRootType)))
134+
?? throw new InvalidOperationException($"Couldn't create EventAddingRepositoryDecorator for aggregateRootType {aggregateRootType.Name}"));
135135
}
136136

137137
return services;

src/Genocs.Persistence.EFCore/Initialization/ApplicationDbInitializer.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,22 @@
44

55
namespace Genocs.Persistence.EFCore.Initialization;
66

7+
/// <summary>
8+
/// ApplicationDbInitializer is responsible for initializing the database.
9+
/// </summary>
710
internal class ApplicationDbInitializer
811
{
912
private readonly ApplicationDbContext _dbContext;
1013
private readonly ApplicationDbSeeder _dbSeeder;
1114
private readonly ILogger<ApplicationDbInitializer> _logger;
1215

16+
/// <summary>
17+
/// Initializes a new instance of the <see cref="ApplicationDbInitializer"/> class.
18+
/// </summary>
19+
/// <param name="dbContext">The DB Context.</param>
20+
/// <param name="dbSeeder">the datebase seeder.</param>
21+
/// <param name="logger">The logger.</param>
22+
/// <exception cref="ArgumentNullException">The exception that is thrown when compulsory params are missing.</exception>
1323
public ApplicationDbInitializer(ApplicationDbContext dbContext, ApplicationDbSeeder dbSeeder, ILogger<ApplicationDbInitializer> logger)
1424
{
1525

@@ -18,11 +28,16 @@ public ApplicationDbInitializer(ApplicationDbContext dbContext, ApplicationDbSee
1828
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
1929
}
2030

21-
public async Task InitializeAsync(CancellationToken cancellationToken)
31+
/// <summary>
32+
/// Initializes the database.
33+
/// </summary>
34+
/// <param name="cancellationToken">The cancelation token.</param>
35+
/// <returns>The task.</returns>
36+
public virtual async Task InitializeAsync(CancellationToken cancellationToken)
2237
{
2338
if (_dbContext.Database.GetMigrations().Any())
2439
{
25-
_logger.LogInformation("Applying migrations to the database.");
40+
_logger.LogInformation("Find migrations that need to be apply. Applying migrations to the database.");
2641
}
2742

2843
await Task.CompletedTask;

src/Genocs.Persistence.EFCore/Initialization/CustomSeederRunner.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,23 @@
33

44
namespace Genocs.Persistence.EFCore.Initialization;
55

6+
/// <summary>
7+
/// CustomSeederRunner is responsible for running custom seeders.
8+
/// </summary>
9+
/// <param name="serviceProvider"></param>
610
internal class CustomSeederRunner(IServiceProvider serviceProvider)
711
{
8-
private readonly ICustomSeeder[] _seeders = [.. serviceProvider.GetServices<ICustomSeeder>()];
9-
12+
/// <summary>
13+
/// Runs the custom seeders.
14+
/// </summary>
15+
/// <param name="cancellationToken">The cancellation token.</param>
16+
/// <returns>The task.</returns>
1017
public async Task RunSeedersAsync(CancellationToken cancellationToken)
1118
{
12-
foreach (var seeder in _seeders)
19+
ICustomSeeder[] seeders = serviceProvider.GetServices<ICustomSeeder>().ToArray();
20+
foreach (var seeder in seeders)
1321
{
1422
await seeder.InitializeAsync(cancellationToken);
1523
}
1624
}
17-
}
25+
}

0 commit comments

Comments
 (0)