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:
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
|
||||
|
||||
Reference in new issue
Block a user