Skip to content

Commit 5a660d1

Browse files
authored
migrate to using db context factories (#43)
* ensure we clear the db context change tracker before adding changes
1 parent 985f8bc commit 5a660d1

14 files changed

+335
-129
lines changed

src/SIL.Harmony.Tests/Adapter/CustomObjectAdapterTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,6 @@ await dataModel.AddChange(Guid.NewGuid(),
194194
myClass2.MyNumber.Should().Be(123.45m);
195195
myClass2.DeletedTime.Should().BeNull();
196196

197-
dataModel.QueryLatest<MyClass>().Should().NotBeEmpty();
197+
dataModel.QueryLatest<MyClass>().ToBlockingEnumerable().Should().NotBeEmpty();
198198
}
199199
}

src/SIL.Harmony.Tests/DataModelReferenceTests.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
using SIL.Harmony.Changes;
33
using SIL.Harmony.Sample.Changes;
44
using SIL.Harmony.Sample.Models;
5-
using SIL.Harmony.Tests;
65

7-
namespace Tests;
6+
namespace SIL.Harmony.Tests;
87

98
public class DataModelReferenceTests : DataModelTestBase
109
{
@@ -158,7 +157,7 @@ public async Task CanCreate2TagsWithTheSameNameOutOfOrder()
158157
var commitA = await WriteNextChange(SetTag(Guid.NewGuid(), tagText));
159158
//represents someone syncing in a tag with the same name
160159
await WriteChangeBefore(commitA, SetTag(Guid.NewGuid(), tagText));
161-
DataModel.QueryLatest<Tag>().Where(t => t.Text == tagText).Should().ContainSingle();
160+
DataModel.QueryLatest<Tag>().ToBlockingEnumerable().Where(t => t.Text == tagText).Should().ContainSingle();
162161
}
163162

164163
[Fact]
@@ -170,6 +169,6 @@ public async Task CanUpdateTagWithTheSameNameOutOfOrder()
170169
var commitA = await WriteNextChange(SetTag(Guid.NewGuid(), tagText));
171170
//represents someone syncing in a tag with the same name
172171
await WriteNextChange(SetTag(renameTagId, tagText));
173-
DataModel.QueryLatest<Tag>().Where(t => t.Text == tagText).Should().ContainSingle();
172+
DataModel.QueryLatest<Tag>().ToBlockingEnumerable().Where(t => t.Text == tagText).Should().ContainSingle();
174173
}
175174
}

src/SIL.Harmony.Tests/DataModelSimpleChanges.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
using SIL.Harmony;
21
using SIL.Harmony.Changes;
32
using SIL.Harmony.Db;
43
using SIL.Harmony.Sample.Changes;
54
using SIL.Harmony.Sample.Models;
6-
using SIL.Harmony.Tests;
75
using Microsoft.EntityFrameworkCore;
86

9-
namespace Tests;
7+
namespace SIL.Harmony.Tests;
108

119
public class DataModelSimpleChanges : DataModelTestBase
1210
{
@@ -79,7 +77,7 @@ public async Task WriteMultipleCommits()
7977

8078
await WriteNextChange(SetWord(Guid.NewGuid(), "change 3"));
8179
DbContext.Snapshots.Should().HaveCount(3);
82-
DataModel.QueryLatest<Word>().Should().HaveCount(3);
80+
DataModel.QueryLatest<Word>().ToBlockingEnumerable().Should().HaveCount(3);
8381
}
8482

8583
[Fact]

src/SIL.Harmony.Tests/DataModelTestBase.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ public class DataModelTestBase : IAsyncLifetime
1818
private readonly bool _performanceTest;
1919
public readonly DataModel DataModel;
2020
public readonly SampleDbContext DbContext;
21-
internal readonly CrdtRepository CrdtRepository;
2221
protected readonly MockTimeProvider MockTimeProvider = new();
2322

2423
public DataModelTestBase(bool saveToDisk = false, bool alwaysValidate = true,
@@ -47,7 +46,6 @@ public DataModelTestBase(SqliteConnection connection, bool alwaysValidate = true
4746
DbContext.Database.OpenConnection();
4847
DbContext.Database.EnsureCreated();
4948
DataModel = _services.GetRequiredService<DataModel>();
50-
CrdtRepository = _services.GetRequiredService<CrdtRepository>();
5149
}
5250

5351
public DataModelTestBase ForkDatabase(bool alwaysValidate = true)
@@ -170,6 +168,7 @@ public virtual Task InitializeAsync()
170168

171169
public async Task DisposeAsync()
172170
{
171+
173172
await _services.DisposeAsync();
174173
}
175174

src/SIL.Harmony.Tests/PersistExtraDataTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public async Task CanPersistExtraData()
7676
{
7777
var entityId = Guid.NewGuid();
7878
var commit = await _dataModelTestBase.WriteNextChange(new CreateExtraDataModelChange(entityId));
79-
var extraDataModel = _dataModelTestBase.DataModel.QueryLatest<ExtraDataModel>().Should().ContainSingle().Subject;
79+
var extraDataModel = _dataModelTestBase.DataModel.QueryLatest<ExtraDataModel>().ToBlockingEnumerable().Should().ContainSingle().Subject;
8080
extraDataModel.Id.Should().Be(entityId);
8181
extraDataModel.CommitId.Should().Be(commit.Id);
8282
extraDataModel.DateTime.Should().Be(commit.HybridDateTime.DateTime);

src/SIL.Harmony.Tests/RepositoryTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public RepositoryTests()
2121
.AddCrdtDataSample(":memory:")
2222
.BuildServiceProvider();
2323

24-
_repository = _services.GetRequiredService<CrdtRepository>();
24+
_repository = _services.GetRequiredService<CrdtRepositoryFactory>().CreateRepositorySync();
2525
_crdtDbContext = _services.GetRequiredService<SampleDbContext>();
2626
}
2727

@@ -34,6 +34,7 @@ public async Task InitializeAsync()
3434

3535
public async Task DisposeAsync()
3636
{
37+
await _repository.DisposeAsync();
3738
await _services.DisposeAsync();
3839
}
3940

src/SIL.Harmony.Tests/SnapshotTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ await WriteNextChange(
116116
TagWord(wordId, tagId),
117117
]);
118118

119-
var word = await DataModel.QueryLatest<Word>().Include(w => w.Tags)
120-
.Where(w => w.Id == wordId).FirstOrDefaultAsync();
119+
var word = await DataModel.QueryLatest<Word>(q => q.Include(w => w.Tags)
120+
.Where(w => w.Id == wordId)).FirstOrDefaultAsync();
121121
word.Should().NotBeNull();
122122
word.Tags.Should().BeEquivalentTo([new Tag { Id = tagId, Text = "tag-1" }]);
123123
}
@@ -135,8 +135,8 @@ await WriteNextChange(
135135
var tagCreation = await WriteNextChange(TagWord(wordId, tagId));
136136
await WriteChangeBefore(tagCreation, TagWord(wordId, tagId));
137137

138-
var word = await DataModel.QueryLatest<Word>().Include(w => w.Tags)
139-
.Where(w => w.Id == wordId).FirstOrDefaultAsync();
138+
var word = await DataModel.QueryLatest<Word>(q=> q.Include(w => w.Tags)
139+
.Where(w => w.Id == wordId)).FirstOrDefaultAsync();
140140
word.Should().NotBeNull();
141141
word.Tags.Should().BeEquivalentTo([new Tag { Id = tagId, Text = "tag-1" }]);
142142
}

src/SIL.Harmony.Tests/SyncTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public async Task CanSync_AddDependentWithMultipleChanges()
116116

117117
await _client2.DataModel.SyncWith(_client1.DataModel);
118118

119-
_client2.DataModel.QueryLatest<Definition>().Should()
120-
.BeEquivalentTo(_client1.DataModel.QueryLatest<Definition>());
119+
_client2.DataModel.QueryLatest<Definition>().ToBlockingEnumerable().Should()
120+
.BeEquivalentTo(_client1.DataModel.QueryLatest<Definition>().ToBlockingEnumerable());
121121
}
122122
}

src/SIL.Harmony/CrdtKernel.cs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Text.Json;
2+
using Microsoft.EntityFrameworkCore;
23
using Microsoft.Extensions.DependencyInjection;
34
using Microsoft.Extensions.Logging;
45
using Microsoft.Extensions.Options;
@@ -8,29 +9,42 @@ namespace SIL.Harmony;
89

910
public static class CrdtKernel
1011
{
12+
public static IServiceCollection AddCrdtDataDbFactory<TContext>(this IServiceCollection services,
13+
Action<CrdtConfig> configureCrdt) where TContext : DbContext, ICrdtDbContext
14+
{
15+
16+
services.AddCrdtDataCore(configureCrdt);
17+
services.AddScoped<ICrdtDbContextFactory, CrdtDbContextFactory<TContext>>();
18+
return services;
19+
}
20+
1121
public static IServiceCollection AddCrdtData<TContext>(this IServiceCollection services,
12-
Action<CrdtConfig> configureCrdt) where TContext: ICrdtDbContext
22+
Action<CrdtConfig> configureCrdt) where TContext : DbContext, ICrdtDbContext
23+
{
24+
services.AddCrdtDataCore(configureCrdt);
25+
services.AddScoped<ICrdtDbContextFactory, CrdtDbContextNoDisposeFactory<TContext>>();
26+
return services;
27+
}
28+
public static IServiceCollection AddCrdtDataCore(this IServiceCollection services, Action<CrdtConfig> configureCrdt)
1329
{
1430
services.AddLogging();
15-
services.AddOptions<CrdtConfig>().Configure(configureCrdt).PostConfigure(crdtConfig => crdtConfig.ObjectTypeListBuilder.Freeze());
31+
services.AddOptions<CrdtConfig>().Configure(configureCrdt)
32+
.PostConfigure(crdtConfig => crdtConfig.ObjectTypeListBuilder.Freeze());
1633
services.AddSingleton(sp => sp.GetRequiredService<IOptions<CrdtConfig>>().Value.JsonSerializerOptions);
1734
services.AddSingleton(TimeProvider.System);
1835
services.AddScoped<IHybridDateTimeProvider>(NewTimeProvider);
19-
//must use factory, otherwise one context will be created for this registration, and one for the application.
20-
//we want to have one context per application
21-
services.AddScoped<ICrdtDbContext>(p => p.GetRequiredService<TContext>());
22-
services.AddScoped<CrdtRepository>();
36+
services.AddScoped<CrdtRepositoryFactory>();
2337
//must use factory method because DataModel constructor is internal
2438
services.AddScoped<DataModel>(provider => new DataModel(
25-
provider.GetRequiredService<CrdtRepository>(),
39+
provider.GetRequiredService<CrdtRepositoryFactory>(),
2640
provider.GetRequiredService<JsonSerializerOptions>(),
2741
provider.GetRequiredService<IHybridDateTimeProvider>(),
2842
provider.GetRequiredService<IOptions<CrdtConfig>>(),
2943
provider.GetRequiredService<ILogger<DataModel>>()
3044
));
3145
//must use factory method because ResourceService constructor is internal
3246
services.AddScoped<ResourceService>(provider => new ResourceService(
33-
provider.GetRequiredService<CrdtRepository>(),
47+
provider.GetRequiredService<CrdtRepositoryFactory>(),
3448
provider.GetRequiredService<IOptions<CrdtConfig>>(),
3549
provider.GetRequiredService<DataModel>(),
3650
provider.GetRequiredService<ILogger<ResourceService>>()
@@ -43,8 +57,11 @@ public static HybridDateTimeProvider NewTimeProvider(IServiceProvider servicePro
4357
//todo, if this causes issues getting the order correct, we can update the last date time after the db is created
4458
//as long as it's before we get a date time from the provider
4559
//todo use IMemoryCache to store the last date time, possibly based on the current project
46-
var hybridDateTime = serviceProvider.GetRequiredService<CrdtRepository>().GetLatestDateTime();
60+
using var repo = serviceProvider.GetRequiredService<CrdtRepositoryFactory>().CreateRepositorySync();
61+
var hybridDateTime = repo.GetLatestDateTime();
4762
hybridDateTime ??= HybridDateTimeProvider.DefaultLastDateTime;
4863
return ActivatorUtilities.CreateInstance<HybridDateTimeProvider>(serviceProvider, hybridDateTime);
4964
}
5065
}
66+
67+

0 commit comments

Comments
 (0)