Skip to content
This repository was archived by the owner on Dec 11, 2024. It is now read-only.

Commit b3c4350

Browse files
committed
Make a persistence module mandatory when adding the PersistentApplication
1 parent e073b13 commit b3c4350

File tree

77 files changed

+582
-746
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+582
-746
lines changed

Backend.Fx.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Backend.Fx.MicrosoftDepende
6060
EndProject
6161
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Backend.Fx.TestUtil", "tests\Backend.Fx.TestUtil\Backend.Fx.TestUtil.csproj", "{3AD4F223-DC1D-40B7-9DB9-DC88FDD0178D}"
6262
EndProject
63+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp.Domain", "tests\SampleApp.Domain\SampleApp.Domain.csproj", "{ADCBD99B-0C75-484C-9C7F-6E174455B3AE}"
64+
EndProject
6365
Global
6466
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6567
Debug|Any CPU = Debug|Any CPU
@@ -125,6 +127,10 @@ Global
125127
{3AD4F223-DC1D-40B7-9DB9-DC88FDD0178D}.Debug|Any CPU.Build.0 = Debug|Any CPU
126128
{3AD4F223-DC1D-40B7-9DB9-DC88FDD0178D}.Release|Any CPU.ActiveCfg = Release|Any CPU
127129
{3AD4F223-DC1D-40B7-9DB9-DC88FDD0178D}.Release|Any CPU.Build.0 = Release|Any CPU
130+
{ADCBD99B-0C75-484C-9C7F-6E174455B3AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
131+
{ADCBD99B-0C75-484C-9C7F-6E174455B3AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
132+
{ADCBD99B-0C75-484C-9C7F-6E174455B3AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
133+
{ADCBD99B-0C75-484C-9C7F-6E174455B3AE}.Release|Any CPU.Build.0 = Release|Any CPU
128134
EndGlobalSection
129135
GlobalSection(SolutionProperties) = preSolution
130136
HideSolutionNode = FALSE
@@ -152,6 +158,7 @@ Global
152158
{E50D7E8D-D012-4683-BA05-C877BAA25230} = {C7885592-A4B8-4BA8-8D3A-1EDA4025D17A}
153159
{B4791DB0-F8DD-4248-86CB-407E46F55B13} = {22E4DE95-C3E5-49E6-83BF-BF30905A746B}
154160
{3AD4F223-DC1D-40B7-9DB9-DC88FDD0178D} = {C7885592-A4B8-4BA8-8D3A-1EDA4025D17A}
161+
{ADCBD99B-0C75-484C-9C7F-6E174455B3AE} = {C7885592-A4B8-4BA8-8D3A-1EDA4025D17A}
155162
EndGlobalSection
156163
GlobalSection(ExtensibilityGlobals) = postSolution
157164
SolutionGuid = {45648557-C751-44AD-9C87-0F12EB673969}

src/abstractions/Backend.Fx/Environment/Persistence/PersistentApplication.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ public class PersistentApplication : BackendFxApplicationDecorator
1616

1717
public PersistentApplication(IDatabaseBootstrapper databaseBootstrapper,
1818
IDatabaseAvailabilityAwaiter databaseAvailabilityAwaiter,
19+
IModule persistenceModule,
1920
IBackendFxApplication application) : base(application)
2021
{
2122
_databaseBootstrapper = databaseBootstrapper;
2223
_databaseAvailabilityAwaiter = databaseAvailabilityAwaiter;
24+
application.CompositionRoot.RegisterModules(persistenceModule);
2325
}
2426

2527

src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/MessageBusApplication.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,19 @@ public class MessageBusApplication : BackendFxApplicationDecorator
1111
public MessageBusApplication(IMessageBus messageBus, IBackendFxApplication application)
1212
: base(application)
1313
{
14+
Invoker = new RaiseIntegrationEventsInvokerDecorator(application.CompositionRoot, base.Invoker);
15+
AsyncInvoker = new RaiseIntegrationEventsAsyncInvokerDecorator(application.CompositionRoot, base.AsyncInvoker);
16+
1417
application.CompositionRoot.RegisterModules(new MessageBusModule(messageBus, application.Assemblies));
1518
_messageBus = messageBus;
16-
var invoker = new RaiseIntegrationEventsInvokerDecorator(application.CompositionRoot, base.Invoker);
17-
_messageBus.ProvideInvoker(invoker);
18-
Invoker = invoker;
19-
AsyncInvoker = new RaiseIntegrationEventsAsyncInvokerDecorator(application.CompositionRoot, base.AsyncInvoker);
20-
19+
_messageBus.ProvideInvoker(
20+
new SequentializingBackendFxApplicationInvoker(
21+
new ExceptionLoggingAndHandlingInvoker(application.ExceptionLogger, application.Invoker)));
2122
}
2223

2324
public override async Task BootAsync(CancellationToken cancellationToken = default)
2425
{
2526
await base.BootAsync(cancellationToken).ConfigureAwait(false);
26-
_messageBus.ProvideInvoker(
27-
new SequentializingBackendFxApplicationInvoker(
28-
new ExceptionLoggingAndHandlingInvoker(ExceptionLogger, Invoker)));
2927
_messageBus.Connect();
3028
}
3129

src/implementations/Backend.Fx.EfCore5Persistence/Bootstrapping/EfCorePersistenceModule.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,32 @@
66
using Backend.Fx.BuildingBlocks;
77
using Backend.Fx.Environment.Persistence;
88
using Backend.Fx.Patterns.DependencyInjection;
9+
using Backend.Fx.Patterns.EventAggregation.Domain;
910
using Backend.Fx.Patterns.IdGeneration;
1011
using Microsoft.EntityFrameworkCore;
1112
using Microsoft.Extensions.DependencyInjection;
1213
using Microsoft.Extensions.Logging;
1314

1415
namespace Backend.Fx.EfCore5Persistence.Bootstrapping
1516
{
16-
public class EfCorePersistenceModule<TDbContext> : IModule
17+
public class EfCorePersistenceModule<TDbContext, TIdGenerator> : IModule
1718
where TDbContext : DbContext
19+
where TIdGenerator : IEntityIdGenerator
1820
{
1921
private readonly ILoggerFactory _loggerFactory;
2022
private readonly Action<DbContextOptionsBuilder<TDbContext>, IDbConnection> _configure;
2123
private readonly IDbConnectionFactory _dbConnectionFactory;
22-
private readonly IEntityIdGenerator _entityIdGenerator;
2324
private readonly Type[] _aggregateRootTypes;
2425
private readonly Type[] _entityTypes;
2526
private readonly Dictionary<Type, Type> _aggregateMappingTypes;
2627

2728
public EfCorePersistenceModule(
2829
IDbConnectionFactory dbConnectionFactory,
29-
IEntityIdGenerator entityIdGenerator,
3030
ILoggerFactory loggerFactory,
3131
Action<DbContextOptionsBuilder<TDbContext>, IDbConnection> configure,
3232
params Assembly[] assemblies)
3333
{
3434
_dbConnectionFactory = dbConnectionFactory;
35-
_entityIdGenerator = entityIdGenerator;
3635
_loggerFactory = loggerFactory;
3736
_configure = configure;
3837

@@ -70,13 +69,14 @@ public void Register(ICompositionRoot compositionRoot)
7069
compositionRoot.Register(
7170
new ServiceDescriptor(
7271
typeof(IEntityIdGenerator),
73-
_entityIdGenerator));
72+
typeof(TIdGenerator),
73+
ServiceLifetime.Singleton));
7474

7575
// by letting the container create the connection we can be sure, that only one connection per scope is used, and disposing is done accordingly
7676
compositionRoot.Register(
7777
new ServiceDescriptor(
7878
typeof(IDbConnection),
79-
sp => _dbConnectionFactory.Create(),
79+
_ => _dbConnectionFactory.Create(),
8080
ServiceLifetime.Scoped));
8181

8282
// EF core requires us to flush frequently, because of a missing identity map
@@ -163,12 +163,12 @@ public void Register(ICompositionRoot compositionRoot)
163163
typeof(DbConnectionOperationDecorator),
164164
ServiceLifetime.Scoped));
165165

166-
// // ensure everything dirty is flushed to the db before handling domain events
167-
// compositionRoot.Register(
168-
// new ServiceDescriptor(
169-
// typeof(IDomainEventAggregator),
170-
// typeof(FlushDomainEventAggregatorDecorator),
171-
// ServiceLifetime.Scoped));
166+
// ensure everything dirty is flushed to the db before handling domain events
167+
compositionRoot.RegisterDecorator(
168+
new ServiceDescriptor(
169+
typeof(IDomainEventAggregator),
170+
typeof(FlushDomainEventAggregatorDecorator),
171+
ServiceLifetime.Scoped));
172172
}
173173
}
174174
}

src/implementations/Backend.Fx.EfCore5Persistence/DbContextExtensions.cs

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
using System;
2-
using System.Globalization;
32
using System.Linq;
43
using System.Reflection;
54
using Backend.Fx.BuildingBlocks;
65
using Backend.Fx.Extensions;
76
using Backend.Fx.Logging;
87
using Microsoft.EntityFrameworkCore;
9-
using Microsoft.EntityFrameworkCore.ChangeTracking;
108
using Microsoft.Extensions.Logging;
119
using ILogger = Microsoft.Extensions.Logging.ILogger;
1210

@@ -54,33 +52,5 @@ public static void ApplyAggregateMappings(this DbContext dbContext, ModelBuilder
5452
aggregateMapping.ApplyEfMapping(modelBuilder);
5553
}
5654
}
57-
58-
59-
60-
public static void TraceChangeTrackerState(this DbContext dbContext)
61-
{
62-
if (Logger.IsEnabled(LogLevel.Trace))
63-
try
64-
{
65-
var changeTrackerState = new
66-
{
67-
added = dbContext.ChangeTracker.Entries().Where(entry => entry.State == EntityState.Added).ToArray(),
68-
modified = dbContext.ChangeTracker.Entries().Where(entry => entry.State == EntityState.Modified).ToArray(),
69-
deleted = dbContext.ChangeTracker.Entries().Where(entry => entry.State == EntityState.Deleted).ToArray(),
70-
unchanged = dbContext.ChangeTracker.Entries().Where(entry => entry.State == EntityState.Unchanged).ToArray()
71-
};
72-
73-
Logger.LogTrace("Change tracker state: {@ChangeTrackerState}", changeTrackerState);
74-
}
75-
catch (Exception ex)
76-
{
77-
Logger.LogWarning(ex, "Change tracker state could not be dumped");
78-
}
79-
}
80-
81-
private static string GetPrimaryKeyValue(EntityEntry entry)
82-
{
83-
return (entry.Entity as Entity)?.Id.ToString(CultureInfo.InvariantCulture) ?? "?";
84-
}
8555
}
8656
}

src/implementations/Backend.Fx.EfCore5Persistence/EfFlush.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public void Flush()
3737
{
3838
DetectChanges();
3939
UpdateTrackingProperties();
40-
DbContext.TraceChangeTrackerState();
40+
if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("Change tracker state: {@ChangeTrackerState}", DbContext.ChangeTracker.DebugView.LongView);
4141
CheckForMissingTenantIds();
4242
SaveChanges();
4343
}

src/implementations/Backend.Fx.EfCore6Persistence/Bootstrapping/EfCorePersistenceModule.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,32 @@
66
using Backend.Fx.BuildingBlocks;
77
using Backend.Fx.Environment.Persistence;
88
using Backend.Fx.Patterns.DependencyInjection;
9+
using Backend.Fx.Patterns.EventAggregation.Domain;
910
using Backend.Fx.Patterns.IdGeneration;
1011
using Microsoft.EntityFrameworkCore;
1112
using Microsoft.Extensions.DependencyInjection;
1213
using Microsoft.Extensions.Logging;
1314

1415
namespace Backend.Fx.EfCore6Persistence.Bootstrapping
1516
{
16-
public class EfCorePersistenceModule<TDbContext> : IModule
17+
public class EfCorePersistenceModule<TDbContext, TIdGenerator> : IModule
1718
where TDbContext : DbContext
19+
where TIdGenerator : IEntityIdGenerator
1820
{
1921
private readonly ILoggerFactory _loggerFactory;
2022
private readonly Action<DbContextOptionsBuilder<TDbContext>, IDbConnection> _configure;
2123
private readonly IDbConnectionFactory _dbConnectionFactory;
22-
private readonly IEntityIdGenerator _entityIdGenerator;
2324
private readonly Type[] _aggregateRootTypes;
2425
private readonly Type[] _entityTypes;
2526
private readonly Dictionary<Type, Type> _aggregateMappingTypes;
2627

2728
public EfCorePersistenceModule(
2829
IDbConnectionFactory dbConnectionFactory,
29-
IEntityIdGenerator entityIdGenerator,
3030
ILoggerFactory loggerFactory,
3131
Action<DbContextOptionsBuilder<TDbContext>, IDbConnection> configure,
3232
params Assembly[] assemblies)
3333
{
3434
_dbConnectionFactory = dbConnectionFactory;
35-
_entityIdGenerator = entityIdGenerator;
3635
_loggerFactory = loggerFactory;
3736
_configure = configure;
3837

@@ -70,13 +69,14 @@ public void Register(ICompositionRoot compositionRoot)
7069
compositionRoot.Register(
7170
new ServiceDescriptor(
7271
typeof(IEntityIdGenerator),
73-
_entityIdGenerator));
72+
typeof(TIdGenerator),
73+
ServiceLifetime.Singleton));
7474

7575
// by letting the container create the connection we can be sure, that only one connection per scope is used, and disposing is done accordingly
7676
compositionRoot.Register(
7777
new ServiceDescriptor(
7878
typeof(IDbConnection),
79-
sp => _dbConnectionFactory.Create(),
79+
_ => _dbConnectionFactory.Create(),
8080
ServiceLifetime.Scoped));
8181

8282
// EF core requires us to flush frequently, because of a missing identity map
@@ -163,12 +163,12 @@ public void Register(ICompositionRoot compositionRoot)
163163
typeof(DbConnectionOperationDecorator),
164164
ServiceLifetime.Scoped));
165165

166-
// // ensure everything dirty is flushed to the db before handling domain events
167-
// compositionRoot.Register(
168-
// new ServiceDescriptor(
169-
// typeof(IDomainEventAggregator),
170-
// typeof(FlushDomainEventAggregatorDecorator),
171-
// ServiceLifetime.Scoped));
166+
// ensure everything dirty is flushed to the db before handling domain events
167+
compositionRoot.RegisterDecorator(
168+
new ServiceDescriptor(
169+
typeof(IDomainEventAggregator),
170+
typeof(FlushDomainEventAggregatorDecorator),
171+
ServiceLifetime.Scoped));
172172
}
173173
}
174174
}

src/implementations/Backend.Fx.EfCore6Persistence/DbContextExtensions.cs

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
using System;
2-
using System.Globalization;
32
using System.Linq;
43
using System.Reflection;
54
using Backend.Fx.BuildingBlocks;
65
using Backend.Fx.Extensions;
76
using Backend.Fx.Logging;
87
using Microsoft.EntityFrameworkCore;
9-
using Microsoft.EntityFrameworkCore.ChangeTracking;
108
using Microsoft.Extensions.Logging;
119
using ILogger = Microsoft.Extensions.Logging.ILogger;
1210

@@ -54,33 +52,5 @@ public static void ApplyAggregateMappings(this DbContext dbContext, ModelBuilder
5452
aggregateMapping.ApplyEfMapping(modelBuilder);
5553
}
5654
}
57-
58-
59-
60-
public static void TraceChangeTrackerState(this DbContext dbContext)
61-
{
62-
if (Logger.IsEnabled(LogLevel.Trace))
63-
try
64-
{
65-
var changeTrackerState = new
66-
{
67-
added = dbContext.ChangeTracker.Entries().Where(entry => entry.State == EntityState.Added).ToArray(),
68-
modified = dbContext.ChangeTracker.Entries().Where(entry => entry.State == EntityState.Modified).ToArray(),
69-
deleted = dbContext.ChangeTracker.Entries().Where(entry => entry.State == EntityState.Deleted).ToArray(),
70-
unchanged = dbContext.ChangeTracker.Entries().Where(entry => entry.State == EntityState.Unchanged).ToArray()
71-
};
72-
73-
Logger.LogTrace("Change tracker state: {@ChangeTrackerState}", changeTrackerState);
74-
}
75-
catch (Exception ex)
76-
{
77-
Logger.LogWarning(ex, "Change tracker state could not be dumped");
78-
}
79-
}
80-
81-
private static string GetPrimaryKeyValue(EntityEntry entry)
82-
{
83-
return (entry.Entity as Entity)?.Id.ToString(CultureInfo.InvariantCulture) ?? "?";
84-
}
8555
}
8656
}

src/implementations/Backend.Fx.EfCore6Persistence/EfFlush.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public void Flush()
3737
{
3838
DetectChanges();
3939
UpdateTrackingProperties();
40-
DbContext.TraceChangeTrackerState();
40+
if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("Change tracker state: {@ChangeTrackerState}", DbContext.ChangeTracker.DebugView.LongView);
4141
CheckForMissingTenantIds();
4242
SaveChanges();
4343
}
@@ -186,7 +186,7 @@ private static EntityEntry GetAggregateRootEntry(ChangeTracker changeTracker, En
186186
Logger.LogDebug("Recursing...");
187187
return GetAggregateRootEntry(changeTracker, navigationTargetEntry);
188188
}
189-
189+
190190
throw new InvalidOperationException($"Could not find aggregate root of {entry.Entity.GetType().Name}[{(entry.Entity as Identified)?.Id}]");
191191
}
192192
}

tests/Backend.Fx.Tests/Patterns/IdGeneration/InMemorySequence.cs renamed to src/implementations/Backend.Fx.InMemoryPersistence/InMemorySequence.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using Backend.Fx.Patterns.IdGeneration;
22

3-
namespace Backend.Fx.Tests.Patterns.IdGeneration
3+
namespace Backend.Fx.InMemoryPersistence
44
{
55
public class InMemorySequence : ISequence
66
{

0 commit comments

Comments
 (0)