Skip to content

Commit d547e2f

Browse files
committed
Add EFCore support and refactor database initialization
- Updated project references to include Genocs.Persistence.EFCore. - Added `InitializeDatabasesAsync` method for async database initialization. - Modified `Program.cs` to call the new initialization method. - Updated `appsettings.json` to include database settings for MSSQL. - Refactored `ApplicationDbContext` and related classes for clarity and simplicity. - Introduced `IDapperRepository` for executing raw SQL queries. - Created `EFCoreExtensions` for EF Core related service registration. - Cleaned up `Startup.cs` by removing commented-out code and streamlining service registration.
1 parent 712a4ea commit d547e2f

File tree

15 files changed

+204
-56
lines changed

15 files changed

+204
-56
lines changed

src/Genocs.Core.Demo.Domain/Genocs.Core.Demo.Domain.csproj

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

99
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
1010
<ProjectReference Include="..\Genocs.Persistence.MongoDb\Genocs.Persistence.MongoDb.csproj" />
11+
<ProjectReference Include="..\Genocs.Persistence.EFCore\Genocs.Persistence.EFCore.csproj" />
1112
</ItemGroup>
1213

1314
<ItemGroup Condition="'$(Configuration)' != 'Debug'">

src/Genocs.Core.Demo.WebApi/Infrastructure/Extensions/ServiceCollectionExtensions.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Genocs.Core.Demo.WebApi.Configurations;
1+
using Genocs.Common.Persistence.Initialization;
2+
using Genocs.Core.Demo.WebApi.Configurations;
23
using Genocs.ServiceBusAzure.Configurations;
34
using Genocs.ServiceBusAzure.Queues;
45
using Genocs.ServiceBusAzure.Queues.Interfaces;
@@ -85,4 +86,19 @@ public static IApplicationBuilder UseFirebaseAuthentication(this IApplicationBui
8586
{
8687
return builder.UseMiddleware<FirebaseAuthenticationMiddleware>();
8788
}
89+
90+
/// <summary>
91+
/// Initialize the databases.
92+
/// </summary>
93+
/// <param name="services">The Service Provider.</param>
94+
/// <param name="cancellationToken">The cancellation Token.</param>
95+
/// <returns></returns>
96+
public static async Task InitializeDatabasesAsync(this IServiceProvider services, CancellationToken cancellationToken = default)
97+
{
98+
// Create a new scope to retrieve scoped services
99+
using var scope = services.CreateScope();
100+
101+
await scope.ServiceProvider.GetRequiredService<IDatabaseInitializer>()
102+
.InitializeDatabasesAsync(cancellationToken);
103+
}
88104
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Genocs.Auth;
1212
using System.Reflection;
1313
using System.Text.Json.Serialization;
14+
using Genocs.Persistence.EFCore.Extensions;
1415

1516
StaticLogger.EnsureInitialized();
1617

@@ -24,9 +25,10 @@
2425
.AddGenocs()
2526
.AddJwt()
2627
.AddOpenTelemetry()
27-
.AddMongo()
28-
.AddMongoFast()
29-
.RegisterMongoRepositories(Assembly.GetExecutingAssembly())
28+
//.AddMongo()
29+
//.AddMongoFast()
30+
//.RegisterMongoRepositories(Assembly.GetExecutingAssembly())
31+
.AddEFCorePersistence()
3032
.AddApplicationServices()
3133
.Build();
3234

@@ -63,6 +65,8 @@
6365

6466
app.UseGenocs();
6567

68+
await app.Services.InitializeDatabasesAsync();
69+
6670
// Configure the HTTP request pipeline.
6771
if (app.Environment.IsDevelopment())
6872
{

src/Genocs.Core.Demo.WebApi/appsettings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
"database": "demo",
6363
"enableTracing": true
6464
},
65+
"databaseSettings": {
66+
"DBProvider": "mssql",
67+
"ConnectionString": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=Genocs.Microservice.Template;Integrated Security=True;MultipleActiveResultSets=True"
68+
},
6569
"azureKeyVault": {
6670
"enabled": false,
6771
"name": "kv-genocs"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System.Data;
2+
using Genocs.Common.Interfaces;
3+
using Genocs.Core.Domain.Entities;
4+
5+
namespace Genocs.Core.Domain.Repositories;
6+
7+
public interface IDapperRepository : ITransientService
8+
{
9+
/// <summary>
10+
/// Get an <see cref="IReadOnlyList{T}"/> using raw sql string with parameters.
11+
/// </summary>
12+
/// <typeparam name="T">The type of the entity.</typeparam>
13+
/// <param name="sql">The sql string.</param>
14+
/// <param name="param">The parameters in the sql string.</param>
15+
/// <param name="transaction">The transaction to be performed.</param>
16+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
17+
/// <returns>Returns <see cref="Task"/> of <see cref="IReadOnlyCollection{T}"/>.</returns>
18+
Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object? param = null, IDbTransaction? transaction = null, CancellationToken cancellationToken = default)
19+
where T : class, IEntity;
20+
21+
/// <summary>
22+
/// Get a <typeparamref name="T"/> using raw sql string with parameters.
23+
/// </summary>
24+
/// <typeparam name="T">The type of the entity.</typeparam>
25+
/// <param name="sql">The sql string.</param>
26+
/// <param name="param">The parameters in the sql string.</param>
27+
/// <param name="transaction">The transaction to be performed.</param>
28+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
29+
/// <returns>Returns <see cref="Task"/> of <typeparamref name="T"/>.</returns>
30+
Task<T?> QueryFirstOrDefaultAsync<T>(string sql, object? param = null, IDbTransaction? transaction = null, CancellationToken cancellationToken = default)
31+
where T : class, IEntity;
32+
33+
/// <summary>
34+
/// Get a <typeparamref name="T"/> using raw sql string with parameters.
35+
/// </summary>
36+
/// <typeparam name="T">The type of the entity.</typeparam>
37+
/// <param name="sql">The sql string.</param>
38+
/// <param name="param">The parameters in the sql string.</param>
39+
/// <param name="transaction">The transaction to be performed.</param>
40+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
41+
/// <returns>Returns <see cref="Task"/> of <typeparamref name="T"/>.</returns>
42+
Task<T> QuerySingleAsync<T>(string sql, object? param = null, IDbTransaction? transaction = null, CancellationToken cancellationToken = default)
43+
where T : class, IEntity;
44+
}

src/Genocs.Core/Domain/Repositories/IRepository.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ namespace Genocs.Core.Domain.Repositories;
99
public interface IRepository<TEntity, in TKey>
1010
where TEntity : IEntity<TKey>;
1111

12-
13-
1412
// The Repository for the Application Db
1513
// I(Read)RepositoryBase<T> is from Ardalis.Specification
1614

src/Genocs.Persistence.EFCore/Common/DbProviderKeys.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/// </summary>
66
internal class DbProviderKeys
77
{
8-
public const string MongoDB = "MongoDB";
8+
public const string MongoDB = "mongodb";
99
public const string MySql = "mysql";
1010
public const string Npgsql = "postgresql";
1111
public const string Oracle = "oracle";

src/Genocs.Persistence.EFCore/Context/ApplicationDbContext.cs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,4 @@
22

33
namespace Genocs.Persistence.EFCore.Context;
44

5-
public class ApplicationDbContext : DbContext
6-
{
7-
public ApplicationDbContext(DbContextOptions options)
8-
: base(options)
9-
{
10-
}
11-
12-
protected override void OnModelCreating(ModelBuilder modelBuilder)
13-
{
14-
base.OnModelCreating(modelBuilder);
15-
}
16-
}
5+
public class ApplicationDbContext(DbContextOptions options) : DbContext(options);

src/Genocs.Persistence.EFCore/Startup.cs renamed to src/Genocs.Persistence.EFCore/Extensions/EFCoreExtensions.cs

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
1+
using Genocs.Common.Interfaces;
2+
using Genocs.Common.Persistence.Initialization;
3+
using Genocs.Core.Builders;
4+
using Genocs.Core.Domain.ConnectionString;
5+
using Genocs.Core.Domain.Entities;
6+
using Genocs.Core.Domain.Repositories;
17
using Genocs.Persistence.EFCore.Common;
8+
using Genocs.Persistence.EFCore.Configurations;
9+
using Genocs.Persistence.EFCore.Context;
10+
using Genocs.Persistence.EFCore.Initialization;
11+
using Genocs.Persistence.EFCore.Repositories;
212
using Microsoft.EntityFrameworkCore;
313
using Microsoft.Extensions.DependencyInjection;
14+
using Microsoft.Extensions.Options;
415
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
516
using Serilog;
617

7-
using Genocs.Core.Domain.Repositories;
8-
using Genocs.Persistence.EFCore.Repositories;
9-
10-
namespace Genocs.Persistence.EFCore;
18+
namespace Genocs.Persistence.EFCore.Extensions;
1119

12-
internal static class Startup
20+
public static class EFCoreExtensions
1321
{
14-
private static readonly ILogger _logger = Log.ForContext(typeof(Startup));
22+
private static readonly ILogger _logger = Log.ForContext(typeof(EFCoreExtensions));
1523

16-
internal static IServiceCollection AddPersistence(this IServiceCollection services)
24+
public static IGenocsBuilder AddEFCorePersistence(this IGenocsBuilder builder)
1725
{
18-
19-
services
26+
// Bind the configuration section to the DatabaseSettings class
27+
// and validate it
28+
builder.Services
2029
.AddOptions<DatabaseSettings>()
2130
.BindConfiguration(nameof(DatabaseSettings))
2231
.PostConfigure(databaseSettings =>
@@ -25,9 +34,10 @@ internal static IServiceCollection AddPersistence(this IServiceCollection servic
2534
})
2635
.ValidateDataAnnotations()
2736
.ValidateOnStart();
28-
/*
29-
return services
30-
.AddDbContext<MultitenantApplicationDbContext>((p, m) =>
37+
38+
// Add the DbContext and other services
39+
builder.Services
40+
.AddDbContext<ApplicationDbContext>((p, m) =>
3141
{
3242
var databaseSettings = p.GetRequiredService<IOptions<DatabaseSettings>>().Value;
3343
m.UseDatabase(databaseSettings.DBProvider, databaseSettings.ConnectionString);
@@ -41,11 +51,46 @@ internal static IServiceCollection AddPersistence(this IServiceCollection servic
4151
.AddTransient<IConnectionStringValidator, ConnectionStringValidator>()
4252
.AddRepositories();
4353

44-
*/
54+
return builder;
55+
}
56+
57+
internal static IServiceCollection AddServices(this IServiceCollection services) =>
58+
services
59+
.AddServices(typeof(ITransientService), ServiceLifetime.Transient)
60+
.AddServices(typeof(IScopedService), ServiceLifetime.Scoped);
61+
62+
internal static IServiceCollection AddServices(this IServiceCollection services, Type interfaceType, ServiceLifetime lifetime)
63+
{
64+
var interfaceTypes =
65+
AppDomain.CurrentDomain.GetAssemblies()
66+
.SelectMany(s => s.GetTypes())
67+
.Where(t => interfaceType.IsAssignableFrom(t)
68+
&& t.IsClass && !t.IsAbstract)
69+
.Select(t => new
70+
{
71+
Service = t.GetInterfaces().FirstOrDefault(),
72+
Implementation = t
73+
})
74+
.Where(t => t.Service is not null
75+
&& interfaceType.IsAssignableFrom(t.Service));
76+
77+
foreach (var type in interfaceTypes)
78+
{
79+
services.AddService(type.Service!, type.Implementation, lifetime);
80+
}
4581

4682
return services;
4783
}
4884

85+
internal static IServiceCollection AddService(this IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime lifetime) =>
86+
lifetime switch
87+
{
88+
ServiceLifetime.Transient => services.AddTransient(serviceType, implementationType),
89+
ServiceLifetime.Scoped => services.AddScoped(serviceType, implementationType),
90+
ServiceLifetime.Singleton => services.AddSingleton(serviceType, implementationType),
91+
_ => throw new ArgumentException("Invalid lifeTime", nameof(lifetime))
92+
};
93+
4994
internal static DbContextOptionsBuilder UseDatabase(this DbContextOptionsBuilder builder, string dbProvider, string connectionString)
5095
{
5196
return dbProvider.ToLowerInvariant() switch
@@ -69,11 +114,9 @@ internal static DbContextOptionsBuilder UseDatabase(this DbContextOptionsBuilder
69114

70115
private static IServiceCollection AddRepositories(this IServiceCollection services)
71116
{
72-
73117
// Add Repositories
74-
services.AddScoped(typeof(IRepository<,>), typeof(ApplicationDbRepository<>));
118+
services.AddScoped(typeof(IRepository<>), typeof(ApplicationDbRepository<>));
75119

76-
/*
77120
foreach (var aggregateRootType in
78121
typeof(IAggregateRoot).Assembly.GetExportedTypes()
79122
.Where(t => typeof(IAggregateRoot).IsAssignableFrom(t) && t.IsClass)
@@ -84,13 +127,12 @@ private static IServiceCollection AddRepositories(this IServiceCollection servic
84127
sp.GetRequiredService(typeof(IRepository<>).MakeGenericType(aggregateRootType)));
85128

86129
// Decorate the repositories with EventAddingRepositoryDecorators and expose them as IRepositoryWithEvents.
87-
services.AddScoped(typeof(IRepositoryWithEvents<>).MakeGenericType(aggregateRootType), sp =>
88-
Activator.CreateInstance(
89-
typeof(EventAddingRepositoryDecorator<>).MakeGenericType(aggregateRootType),
90-
sp.GetRequiredService(typeof(IRepository<>).MakeGenericType(aggregateRootType)))
91-
?? 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}"));
92135
}
93-
*/
94136

95137
return services;
96138
}

src/Genocs.Persistence.EFCore/Genocs.Persistence.EFCore.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,4 @@
5858
<PackageReference Include="MongoDB.EntityFrameworkCore" Version="9.0.0" />
5959
</ItemGroup>
6060

61-
<ItemGroup>
62-
<Folder Include="Extensions\" />
63-
</ItemGroup>
64-
6561
</Project>

0 commit comments

Comments
 (0)