168 lines
6.0 KiB
C#
168 lines
6.0 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace DataModel.Database.Common;
|
|
|
|
public sealed class DataContext : DbContext, IDataContext
|
|
{
|
|
public DbSet<Setting> Settings { get; set; }
|
|
|
|
public DbSet<Section> Sections { get; set; }
|
|
|
|
public DbSet<TextElement> TextElements { get; set; }
|
|
|
|
public DbSet<Translation> Translations { get; set; }
|
|
|
|
public DataContext(DbContextOptions<DataContext> contextOptions) : base(contextOptions)
|
|
{
|
|
}
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
base.OnModelCreating(modelBuilder);
|
|
|
|
#region Settings
|
|
|
|
modelBuilder.Entity<Setting>().HasIndex(n => n.Id);
|
|
modelBuilder.Entity<Setting>().HasIndex(n => n.Code).IsUnique();
|
|
modelBuilder.Entity<Setting>().HasIndex(n => n.BoolValue);
|
|
modelBuilder.Entity<Setting>().HasIndex(n => n.GuidValue);
|
|
modelBuilder.Entity<Setting>().HasIndex(n => n.IntegerValue);
|
|
modelBuilder.Entity<Setting>().HasIndex(n => n.TextValue);
|
|
|
|
#endregion
|
|
|
|
#region Sections
|
|
|
|
modelBuilder.Entity<Section>().HasIndex(n => n.Id);
|
|
modelBuilder.Entity<Section>().HasIndex(n => n.Name);
|
|
modelBuilder.Entity<Section>().HasIndex(n => n.Depth);
|
|
modelBuilder.Entity<Section>().HasIndex(n => n.DataKey);
|
|
// modelBuilder.Entity<Section>().Navigation(n => n.Parent).AutoInclude(); // Cycle-reference, does not work, though.
|
|
modelBuilder.Entity<Section>().Navigation(n => n.TextElements).AutoInclude();
|
|
|
|
#endregion
|
|
|
|
#region TextElements
|
|
|
|
modelBuilder.Entity<TextElement>().HasIndex(n => n.Id);
|
|
modelBuilder.Entity<TextElement>().HasIndex(n => n.Code);
|
|
modelBuilder.Entity<TextElement>().HasIndex(n => n.Name);
|
|
modelBuilder.Entity<TextElement>().HasIndex(n => n.IsMultiLine);
|
|
modelBuilder.Entity<TextElement>().Navigation(n => n.Section).AutoInclude();
|
|
modelBuilder.Entity<TextElement>().Navigation(n => n.Translations).AutoInclude();
|
|
|
|
#endregion
|
|
|
|
#region Translations
|
|
|
|
modelBuilder.Entity<Translation>().HasIndex(n => n.Id);
|
|
modelBuilder.Entity<Translation>().HasIndex(n => n.Culture);
|
|
modelBuilder.Entity<Translation>().HasIndex(n => n.Text);
|
|
modelBuilder.Entity<Translation>().HasIndex(n => n.TranslateManual);
|
|
modelBuilder.Entity<Translation>().Navigation(n => n.TextElement).AutoInclude();
|
|
|
|
#endregion
|
|
}
|
|
|
|
#region Export and import
|
|
|
|
private readonly record struct JsonData(
|
|
IEnumerable<JsonSetting> Settings,
|
|
IEnumerable<JsonSection> Sections,
|
|
IEnumerable<JsonTextElement> TextElements,
|
|
IEnumerable<JsonTranslation> 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<JsonUniqueId>
|
|
{
|
|
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<JsonUniqueId> TextElements
|
|
);
|
|
|
|
internal readonly record struct JsonTextElement(
|
|
JsonUniqueId UniqueId,
|
|
string Code,
|
|
string Name,
|
|
bool IsMultiLine,
|
|
JsonUniqueId SectionUniqueId,
|
|
List<JsonUniqueId> 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
|
|
} |