From 9989f13625abc86def7f330b27ed3192b7ed3ae5 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 9 Jul 2024 14:40:38 +0700 Subject: [PATCH] migrate to using a db context defined in LcmCrdt instead of one provided by Harmony --- backend/LcmCrdt.Tests/LexboxApiTests.cs | 4 +-- backend/LcmCrdt/CurrentProjectService.cs | 4 +-- backend/LcmCrdt/LcmCrdtDbContext.cs | 37 ++++++++++++++++++++ backend/LcmCrdt/LcmCrdtKernel.cs | 27 ++------------ backend/LcmCrdt/ProjectsService.cs | 6 ++-- backend/LocalWebApp/Routes/ActivityRoutes.cs | 3 +- backend/LocalWebApp/Routes/HistoryRoutes.cs | 6 ++-- backend/LocalWebApp/Routes/TestRoutes.cs | 3 +- 8 files changed, 53 insertions(+), 37 deletions(-) create mode 100644 backend/LcmCrdt/LcmCrdtDbContext.cs diff --git a/backend/LcmCrdt.Tests/LexboxApiTests.cs b/backend/LcmCrdt.Tests/LexboxApiTests.cs index 69eb7c05e..bc077b51e 100644 --- a/backend/LcmCrdt.Tests/LexboxApiTests.cs +++ b/backend/LcmCrdt.Tests/LexboxApiTests.cs @@ -20,7 +20,7 @@ public class BasicApiTests : IAsyncLifetime protected readonly AsyncServiceScope _services; public DataModel DataModel = null!; - private readonly CrdtDbContext _crdtDbContext; + private readonly LcmCrdtDbContext _crdtDbContext; public BasicApiTests() { @@ -30,7 +30,7 @@ public BasicApiTests() .AddSingleton(new MockProjectContext(new CrdtProject("sena-3", ":memory:"))) .BuildServiceProvider(); _services = services.CreateAsyncScope(); - _crdtDbContext = _services.ServiceProvider.GetRequiredService(); + _crdtDbContext = _services.ServiceProvider.GetRequiredService(); } public virtual async Task InitializeAsync() diff --git a/backend/LcmCrdt/CurrentProjectService.cs b/backend/LcmCrdt/CurrentProjectService.cs index 1828a1601..0be96f434 100644 --- a/backend/LcmCrdt/CurrentProjectService.cs +++ b/backend/LcmCrdt/CurrentProjectService.cs @@ -4,7 +4,7 @@ namespace LcmCrdt; -public class CurrentProjectService(CrdtDbContext dbContext, ProjectContext projectContext, IMemoryCache memoryCache) +public class CurrentProjectService(LcmCrdtDbContext dbContext, ProjectContext projectContext, IMemoryCache memoryCache) { public CrdtProject Project => projectContext.Project ?? throw new NullReferenceException("Not in the context of a project"); @@ -19,7 +19,7 @@ public async ValueTask GetProjectData() { using var entry = memoryCache.CreateEntry(key); entry.SlidingExpiration = TimeSpan.FromMinutes(10); - result = await dbContext.Set().AsNoTracking().FirstAsync(); + result = await dbContext.ProjectData.AsNoTracking().FirstAsync(); entry.Value = result; } if (result is null) throw new InvalidOperationException("Project data not found"); diff --git a/backend/LcmCrdt/LcmCrdtDbContext.cs b/backend/LcmCrdt/LcmCrdtDbContext.cs new file mode 100644 index 000000000..5a26ca940 --- /dev/null +++ b/backend/LcmCrdt/LcmCrdtDbContext.cs @@ -0,0 +1,37 @@ +using System.Text.Json; +using Crdt; +using Crdt.Db; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Microsoft.Extensions.Options; + +namespace LcmCrdt; + +public class LcmCrdtDbContext(IOptions options): DbContext, ICrdtDbContext +{ + public DbSet ProjectData => Set(); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.UseCrdt(options.Value); + + modelBuilder.Entity().HasKey(p => p.Id); + } + + protected override void ConfigureConventions(ModelConfigurationBuilder builder) + { + builder.Properties() + .HaveColumnType("jsonb") + .HaveConversion(); + builder.Properties() + .HaveConversion(); + } + + private class MultiStringDbConverter() : ValueConverter( + mul => JsonSerializer.Serialize(mul, (JsonSerializerOptions?)null), + json => JsonSerializer.Deserialize(json, (JsonSerializerOptions?)null) ?? new()); + + private class WritingSystemIdConverter() : ValueConverter( + id => id.Code, + code => new MiniLcm.WritingSystemId(code)); +} diff --git a/backend/LcmCrdt/LcmCrdtKernel.cs b/backend/LcmCrdt/LcmCrdtKernel.cs index d8b5e670c..f61aa16cb 100644 --- a/backend/LcmCrdt/LcmCrdtKernel.cs +++ b/backend/LcmCrdt/LcmCrdtKernel.cs @@ -24,9 +24,9 @@ public static IServiceCollection AddLcmCrdtClient(this IServiceCollection servic { LinqToDBForEFTools.Initialize(); services.AddMemoryCache(); + services.AddDbContext(ConfigureDbOptions); - services.AddCrdtData( - ConfigureDbOptions, + services.AddCrdtData( ConfigureCrdt ); services.AddScoped(); @@ -61,19 +61,7 @@ private static void ConfigureDbOptions(IServiceProvider provider, DbContextOptio private static void ConfigureCrdt(CrdtConfig config) { config.EnableProjectedTables = true; - config.ObjectTypeListBuilder.AddDbModelConfig(builder => - { - builder.Entity().HasKey(p => p.Id); - // builder.Owned(); - }) - .AddDbModelConvention(builder => - { - builder.Properties() - .HaveColumnType("jsonb") - .HaveConversion(); - builder.Properties() - .HaveConversion(); - }) + config.ObjectTypeListBuilder .Add(builder => { // builder.OwnsOne(e => e.Note, n => n.ToJson()); @@ -127,13 +115,4 @@ private static void ConfigureCrdt(CrdtConfig config) .Add() .Add(); } - - - private class MultiStringDbConverter() : ValueConverter( - mul => JsonSerializer.Serialize(mul, (JsonSerializerOptions?)null), - json => JsonSerializer.Deserialize(json, (JsonSerializerOptions?)null) ?? new()); - - private class WritingSystemIdConverter() : ValueConverter( - id => id.Code, - code => new MiniLcm.WritingSystemId(code)); } diff --git a/backend/LcmCrdt/ProjectsService.cs b/backend/LcmCrdt/ProjectsService.cs index 315328022..80d9df4ce 100644 --- a/backend/LcmCrdt/ProjectsService.cs +++ b/backend/LcmCrdt/ProjectsService.cs @@ -38,7 +38,7 @@ public async Task CreateProject(string name, if (File.Exists(sqliteFile)) throw new InvalidOperationException("Project already exists"); var crdtProject = new CrdtProject(name, sqliteFile); await using var serviceScope = CreateProjectScope(crdtProject); - var db = serviceScope.ServiceProvider.GetRequiredService(); + var db = serviceScope.ServiceProvider.GetRequiredService(); var projectData = new ProjectData(name, id ?? Guid.NewGuid(), ProjectData.GetOriginDomain(domain), Guid.NewGuid()); await InitProjectDb(db, projectData); await serviceScope.ServiceProvider.GetRequiredService().PopulateProjectDataCache(); @@ -47,10 +47,10 @@ public async Task CreateProject(string name, return crdtProject; } - internal static async Task InitProjectDb(CrdtDbContext db, ProjectData data) + internal static async Task InitProjectDb(LcmCrdtDbContext db, ProjectData data) { await db.Database.EnsureCreatedAsync(); - db.Set().Add(data); + db.ProjectData.Add(data); await db.SaveChangesAsync(); } diff --git a/backend/LocalWebApp/Routes/ActivityRoutes.cs b/backend/LocalWebApp/Routes/ActivityRoutes.cs index f1526a30e..3d149ee00 100644 --- a/backend/LocalWebApp/Routes/ActivityRoutes.cs +++ b/backend/LocalWebApp/Routes/ActivityRoutes.cs @@ -1,6 +1,7 @@ using Crdt.Changes; using Crdt.Core; using Crdt.Db; +using LcmCrdt; using LocalWebApp.Hubs; using Microsoft.EntityFrameworkCore; using Microsoft.OpenApi.Models; @@ -22,7 +23,7 @@ public static IEndpointConventionBuilder MapActivities(this WebApplication app) return operation; }); group.MapGet("/", - (CrdtDbContext dbcontext) => + (ICrdtDbContext dbcontext) => { return dbcontext.Commits.DefaultOrder().Take(10).Select(c => new Activity(c.Id, c.HybridDateTime.DateTime, ChangeName(c.ChangeEntities), c.ChangeEntities)).AsAsyncEnumerable(); }); diff --git a/backend/LocalWebApp/Routes/HistoryRoutes.cs b/backend/LocalWebApp/Routes/HistoryRoutes.cs index 09f6236b9..90b6d9890 100644 --- a/backend/LocalWebApp/Routes/HistoryRoutes.cs +++ b/backend/LocalWebApp/Routes/HistoryRoutes.cs @@ -23,7 +23,7 @@ public static IEndpointConventionBuilder MapHistoryRoutes(this WebApplication ap return operation; }); group.MapGet("/snapshot/{snapshotId:guid}", - async (Guid snapshotId, CrdtDbContext dbcontext) => + async (Guid snapshotId, ICrdtDbContext dbcontext) => { return await dbcontext.Snapshots.Where(s => s.Id == snapshotId).SingleOrDefaultAsync(); }); @@ -35,12 +35,12 @@ public static IEndpointConventionBuilder MapHistoryRoutes(this WebApplication ap return await dataModel.GetEntitySnapshotAtTime(new DateTimeOffset(timestamp), entityId); }); group.MapGet("/{entityId}", - (Guid entityId, CrdtDbContext dbcontext) => + (Guid entityId, ICrdtDbContext dbcontext) => { var query = from commit in dbcontext.Commits.DefaultOrder() from snapshot in dbcontext.Snapshots.LeftJoin( s => s.CommitId == commit.Id && s.EntityId == entityId) - from change in dbcontext.ChangeEntities.LeftJoin(c => + from change in dbcontext.Set>().LeftJoin(c => c.CommitId == commit.Id && c.EntityId == entityId) where snapshot.Id != null || change.EntityId != null select new HistoryLineItem(commit.Id, diff --git a/backend/LocalWebApp/Routes/TestRoutes.cs b/backend/LocalWebApp/Routes/TestRoutes.cs index 70383654c..cc6e97a8c 100644 --- a/backend/LocalWebApp/Routes/TestRoutes.cs +++ b/backend/LocalWebApp/Routes/TestRoutes.cs @@ -23,10 +23,9 @@ public static IEndpointConventionBuilder MapTest(this WebApplication app) return operation; }); group.MapGet("/entries", - (CrdtDbContext dbContext, ILexboxApi api) => + (ILexboxApi api) => { return api.GetEntries(); - return dbContext.Set().Take(1000).AsAsyncEnumerable(); }); return group; }