using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.EntityFrameworkCore; namespace DataModel.Database.Common; public sealed class DataContext : DbContext, IDataContext { public DbSet Settings { get; set; } public DbSet
Sections { get; set; } public DbSet TextElements { get; set; } public DbSet Translations { get; set; } public DataContext(DbContextOptions contextOptions) : base(contextOptions) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); #region Settings modelBuilder.Entity().HasIndex(n => n.Id); modelBuilder.Entity().HasIndex(n => n.Code).IsUnique(); modelBuilder.Entity().HasIndex(n => n.BoolValue); modelBuilder.Entity().HasIndex(n => n.GuidValue); modelBuilder.Entity().HasIndex(n => n.IntegerValue); modelBuilder.Entity().HasIndex(n => n.TextValue); #endregion #region Sections modelBuilder.Entity
().HasIndex(n => n.Id); modelBuilder.Entity
().HasIndex(n => n.Name); modelBuilder.Entity
().HasIndex(n => n.Depth); modelBuilder.Entity
().HasIndex(n => n.DataKey); // modelBuilder.Entity
().Navigation(n => n.Parent).AutoInclude(); // Cycle-reference, does not work, though. modelBuilder.Entity
().Navigation(n => n.TextElements).AutoInclude(); #endregion #region TextElements modelBuilder.Entity().HasIndex(n => n.Id); modelBuilder.Entity().HasIndex(n => n.Code); modelBuilder.Entity().HasIndex(n => n.Name); modelBuilder.Entity().HasIndex(n => n.IsMultiLine); modelBuilder.Entity().Navigation(n => n.Section).AutoInclude(); modelBuilder.Entity().Navigation(n => n.Translations).AutoInclude(); #endregion #region Translations modelBuilder.Entity().HasIndex(n => n.Id); modelBuilder.Entity().HasIndex(n => n.Culture); modelBuilder.Entity().HasIndex(n => n.Text); modelBuilder.Entity().HasIndex(n => n.TranslateManual); modelBuilder.Entity().Navigation(n => n.TextElement).AutoInclude(); #endregion } #region Export and import private readonly record struct JsonData( IEnumerable Settings, IEnumerable Sections, IEnumerable TextElements, IEnumerable Translations ); internal readonly record struct JsonUniqueId(string Code, Guid UniqueId, string Prefix = "") { public override string ToString() => string.IsNullOrWhiteSpace(this.Prefix) ? $"{this.Code}::{this.UniqueId}" : $"{this.Prefix}::{this.Code}::{this.UniqueId}"; public static implicit operator string(JsonUniqueId id) => id.ToString(); } private sealed class JsonUniqueIdConverter : JsonConverter { public override JsonUniqueId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var json = reader.GetString(); var parts = json?.Split("::"); return parts?.Length switch { 2 => new JsonUniqueId(parts[0], Guid.Parse(parts[1])), 3 => new JsonUniqueId(parts[1], Guid.Parse(parts[2]), parts[0]), _ => throw new JsonException($"Invalid format of JsonUniqueId: {json}") }; } public override void Write(Utf8JsonWriter writer, JsonUniqueId value, JsonSerializerOptions options) { writer.WriteStringValue(value); } } internal readonly record struct JsonSetting( JsonUniqueId UniqueId, string Code, string TextValue, int IntegerValue, bool BoolValue, Guid GuidValue ); internal readonly record struct JsonSection( JsonUniqueId UniqueId, string Name, string DataKey, int Depth, JsonUniqueId ParentUniqueId, List TextElements ); internal readonly record struct JsonTextElement( JsonUniqueId UniqueId, string Code, string Name, bool IsMultiLine, JsonUniqueId SectionUniqueId, List Translations ); internal readonly record struct JsonTranslation( JsonUniqueId UniqueId, string Culture, string Text, bool TranslateManual, JsonUniqueId TextElementUniqueId ); public async Task ExportAsync(string path) { var jsonSettings = new JsonSerializerOptions { WriteIndented = true, Converters = { new JsonUniqueIdConverter() }, }; await using var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None); await JsonSerializer.SerializeAsync(fileStream, new JsonData { Settings = this.Settings.Select(n => n.ToJsonSetting()), Sections = this.Sections.Select(n => n.ToJsonSection()), TextElements = this.TextElements.Select(n => n.ToJsonTextElement()), Translations = this.Translations.Select(n => n.ToJsonTranslation()), }, jsonSettings); } public static async Task ImportAndLoadAsync(string path) { // We import that JSON data file into an new, empty database file // at a temporary location. Next, we enable the auto export feature // to keep the source file up-to-date. } #endregion }