Added Git Export Feature

- Added import algorithm
- Added FromJsonX() methods to data models
- Added possibility to work with temp. database file
- Added export processor to handle export process & triggers
- Added something changed event e.g. as export trigger
- Added possibility to import a JSON export
- Updated Git icon
This commit is contained in:
tsommer committed 2023-01-22 19:35:57 +01:00
1 parent 7a742a18e7
commit 0587af18f2
20 files changed
+411 -62

No files matched your search

@@ -167,11 +167,145 @@ public sealed class DataContext : DbContext, IDataContext
}, jsonSettings);
}
public static async Task ImportAndLoadAsync(string path)
/// <summary>
/// Stores data needed to resolve a parent-child relationship.
/// </summary>
/// <param name="ParentId">The parent id we want to resolve.</param>
/// <param name="Entity">The entity for which we want to resolve the parent.</param>
/// <typeparam name="T">The type of the entity.</typeparam>
private readonly record struct TreeResolver<T>(Guid ParentId, T Entity);
/// <summary>
/// Imports data from a JSON file into an empty database.
/// </summary>
/// <param name="path">The path to the JSON export.</param>
/// <exception cref="InvalidOperationException">When the database is not empty.</exception>
public async Task ImportAsync(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.
if(await this.Settings.AnyAsync() ||
await this.Sections.AnyAsync() ||
await this.TextElements.AnyAsync() ||
await this.Translations.AnyAsync())
throw new InvalidOperationException("The database is not empty. In order to import data, the database must be empty.");
// Start a transaction:
await using var transaction = await this.Database.BeginTransactionAsync();
// Configure the JSON serializer:
var jsonSettings = new JsonSerializerOptions
{
Converters = { new JsonUniqueIdConverter() },
};
await using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
var jsonData = await JsonSerializer.DeserializeAsync<JsonData>(fileStream, jsonSettings);
// --------------------
// Import the settings:
// --------------------
foreach (var setting in jsonData.Settings)
this.Settings.Add(Setting.FromJsonSetting(setting));
// --------------------
// Import the sections:
// --------------------
// We must store the intermediate data in a list, because we need to resolve
// the parent-child relationships in a second step.
var allSections = new Dictionary<Guid, TreeResolver<Section>>();
var sectionToTextElements = new Dictionary<Guid, List<Guid>>();
// Read the data from the JSON file:
foreach (var section in jsonData.Sections)
{
// Convert the next element:
var nextSection = Section.FromJsonSection(section);
// Store the element:
allSections.Add(nextSection.UniqueId, new (section.ParentUniqueId.UniqueId, nextSection));
sectionToTextElements.Add(nextSection.UniqueId, section.TextElements.Select(n => n.UniqueId).ToList());
}
// Now, resolve the parent-child relationships for the sections:
foreach (var (uniqueId, (parentId, section)) in allSections)
section.Parent = parentId == Guid.Empty ? null : allSections[parentId].Entity;
// -------------------------
// Import the text elements:
// -------------------------
// We must store the intermediate data in a list, because we need to resolve
// the parent-child relationships in a second step.
var allTextElements = new Dictionary<Guid, TextElement>();
var textElementToTranslations = new Dictionary<Guid, List<Guid>>();
// Read the data from the JSON file:
foreach (var textElement in jsonData.TextElements)
{
// Convert the next element:
var nextTextElement = TextElement.FromJsonTextElement(textElement);
// We know that the section is already imported, because we imported the sections first:
nextTextElement.Section = allSections[textElement.SectionUniqueId.UniqueId].Entity;
// Store the element in the list:
allTextElements.Add(nextTextElement.UniqueId, nextTextElement);
textElementToTranslations.Add(nextTextElement.UniqueId, textElement.Translations.Select(n => n.UniqueId).ToList());
}
// Now, resolve the parent-child relationships for the text elements to the sections:
foreach (var (sectionUniqueId, textElementsIds) in sectionToTextElements)
{
var section = allSections[sectionUniqueId].Entity;
section.TextElements.AddRange(textElementsIds.Select(n => allTextElements[n]));
}
// Free the memory:
sectionToTextElements.Clear();
// ------------------------
// Import the translations:
// ------------------------
// We must store the intermediate data in a list, because we need to resolve
// the parent-child relationships in a second step.
var allTranslations = new Dictionary<Guid, Translation>();
// Read the data from the JSON file:
foreach (var translation in jsonData.Translations)
{
// Convert the next element:
var nextTranslation = Translation.FromJsonTranslation(translation);
// We know that the text element is already imported, because we imported the text elements first:
nextTranslation.TextElement = allTextElements[translation.TextElementUniqueId.UniqueId];
// Store the element in the list:
allTranslations.Add(nextTranslation.UniqueId, nextTranslation);
}
// Now, resolve the parent-child relationships for the translations to the text elements:
foreach (var (textElementUniqueId, translationsIds) in textElementToTranslations)
{
var textElement = allTextElements[textElementUniqueId];
textElement.Translations.AddRange(translationsIds.Select(n => allTranslations[n]));
}
// Free the memory:
textElementToTranslations.Clear();
// ---------------------------------
// Add all the data to the database:
// ---------------------------------
this.Sections.AddRange(allSections.Values.Select(n => n.Entity));
this.TextElements.AddRange(allTextElements.Values);
this.Translations.AddRange(allTranslations.Values);
// Save the changes:
await this.SaveChangesAsync();
// Commit the transaction:
await transaction.CommitAsync();
}
#endregion