diff --git a/DataAccess/DTOs/CrossSectionDTO.cs b/DataAccess/DTOs/CrossSectionDTO.cs
new file mode 100644
index 0000000..f590e54
--- /dev/null
+++ b/DataAccess/DTOs/CrossSectionDTO.cs
@@ -0,0 +1,21 @@
+using StructureHelperLogics.Models.CrossSections;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DataAccess.DTOs
+{
+ public class CrossSectionDTO : ICrossSection
+ {
+ public ICrossSectionRepository SectionRepository { get; }
+
+ public Guid Id { get; set; }
+
+ public object Clone()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/DataAccess/DataAccess.csproj b/DataAccess/DataAccess.csproj
new file mode 100644
index 0000000..d8687c3
--- /dev/null
+++ b/DataAccess/DataAccess.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net6.0-windows
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DataAccess/Example.cs b/DataAccess/Example.cs
new file mode 100644
index 0000000..92f0f52
--- /dev/null
+++ b/DataAccess/Example.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DataAccess
+{
+ using Newtonsoft.Json;
+ using StructureHelperCommon.Models;
+ using System;
+ using System.Collections.Generic;
+ using static System.Windows.Forms.VisualStyles.VisualStyleElement.ListView;
+ using static System.Windows.Forms.VisualStyles.VisualStyleElement.TextBox;
+
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ var logger = new TraceLogger();
+
+ // Create objects with complex relationships
+ var parent1 = new Parent { Name = "Parent_1" };
+ var parent2 = new Parent { Name = "Parent_2" };
+
+ var detail1 = new Detail { Description = "Detail_1", InternalNote = "Secret Note 1" };
+ var detail2 = new Detail { Description = "Detail_2", InternalNote = "Secret Note 2" };
+ var detail3 = new Detail { Description = "Detail_3", InternalNote = "Secret Note 3" };
+
+ var subDetail1 = new SubDetail { Info = "SubDetail_1" };
+
+ // Set up relationships
+ parent1.Details.Add(detail1);
+ parent1.Details.Add(detail2);
+
+ parent2.Details.Add(detail2); // Shared detail
+ parent2.Details.Add(detail3);
+
+ detail3.SubDetails.Add(subDetail1);
+
+ // Serialize with custom converters and trace logging
+ string json = Serialize(new List { parent1, parent2 }, logger);
+ Console.WriteLine("Serialized JSON:");
+ Console.WriteLine(json);
+
+ // Deserialize with custom converters and trace logging
+ var deserializedParents = Deserialize>(json, logger);
+
+ Console.WriteLine("\nDeserialized Objects:");
+ foreach (var parent in deserializedParents)
+ {
+ Console.WriteLine($"Parent: {parent.Name}, Id: {parent.Id}");
+ foreach (var detail in parent.Details)
+ {
+ Console.WriteLine($" Detail: {detail.Description}, Id: {detail.Id}");
+ }
+ }
+ }
+
+ static string Serialize(object obj, TraceLogger logger)
+ {
+ var settings = new JsonSerializerSettings
+ {
+ Converters = new List
+ {
+ new ParentConverter(logger), // Add the specific converter
+ // Add other converters if needed
+ },
+ Formatting = Formatting.Indented
+ };
+
+ return JsonConvert.SerializeObject(obj, settings);
+ }
+
+ static T Deserialize(string json, TraceLogger logger)
+ {
+ var settings = new JsonSerializerSettings
+ {
+ Converters = new List
+ {
+ new ParentConverter(logger), // Add the specific converter
+ // Add other converters if needed
+ }
+ };
+
+ return JsonConvert.DeserializeObject(json, settings);
+ }
+ }
+
+ using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+
+public class Parent
+ {
+ public Guid Id { get; set; } = Guid.NewGuid();
+
+ [JsonProperty("parent_name")]
+ public string Name { get; set; }
+
+ public List Details { get; set; } = new List();
+ }
+
+ public class Detail
+ {
+ public Guid Id { get; set; } = Guid.NewGuid();
+
+ [JsonPropertyName("detail_description")] // Compatible with System.Text.Json
+ public string Description { get; set; }
+
+ [JsonIgnore] // This property will be ignored during serialization
+ public string InternalNote { get; set; }
+
+ public List SubDetails { get; set; } = new List();
+ }
+
+ public class SubDetail
+ {
+ public Guid Id { get; set; } = Guid.NewGuid();
+ public string Info { get; set; }
+ }
+
+
+}
diff --git a/DataAccess/FileDialogs/FileDialogOpener.cs b/DataAccess/FileDialogs/FileDialogOpener.cs
new file mode 100644
index 0000000..55df864
--- /dev/null
+++ b/DataAccess/FileDialogs/FileDialogOpener.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace DataAccess.FileDialogs
+{
+ public class FileDialogOpener
+ {
+ public void OpenFileAndRead()
+ {
+ // Create an instance of OpenFileDialog
+ using (OpenFileDialog openFileDialog = new OpenFileDialog())
+ {
+ // Set filter options and filter index
+ openFileDialog.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
+ openFileDialog.FilterIndex = 1;
+ openFileDialog.Multiselect = false; // Set to true if you want to allow multiple file selection
+ openFileDialog.Title = "Select a File";
+
+ // Show the dialog and get result
+ if (openFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ // Get the path of the selected file
+ string selectedFilePath = openFileDialog.FileName;
+
+ // Read the content of the file
+ try
+ {
+ string fileContent = File.ReadAllText(selectedFilePath);
+ Console.WriteLine($"File Content of '{selectedFilePath}':");
+ Console.WriteLine(fileContent);
+ }
+ catch (IOException ex)
+ {
+ Console.WriteLine($"An error occurred while reading the file: {ex.Message}");
+ }
+ }
+ else
+ {
+ Console.WriteLine("File selection was cancelled.");
+ }
+ }
+ }
+ }
+
+}
diff --git a/DataAccess/FileDialogs/FileRepository.cs b/DataAccess/FileDialogs/FileRepository.cs
new file mode 100644
index 0000000..d3f5505
--- /dev/null
+++ b/DataAccess/FileDialogs/FileRepository.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DataAccess.FileDialogs
+{
+ using System;
+ using System.IO;
+ using System.Threading.Tasks;
+
+ public class FileRepository : IFileRepository
+ {
+ private readonly string _storageDirectory;
+
+ public FileRepository(string storageDirectory)
+ {
+ _storageDirectory = storageDirectory;
+
+ // Ensure the storage directory exists
+ if (!Directory.Exists(_storageDirectory))
+ {
+ Directory.CreateDirectory(_storageDirectory);
+ }
+ }
+
+ // Save a file to the repository
+ public async Task SaveFileAsync(Stream fileStream, string fileName)
+ {
+ string filePath = Path.Combine(_storageDirectory, fileName);
+
+ // Ensure the file does not already exist
+ if (File.Exists(filePath))
+ {
+ throw new InvalidOperationException("File already exists.");
+ }
+
+ using (var file = new FileStream(filePath, FileMode.Create, FileAccess.Write))
+ {
+ await fileStream.CopyToAsync(file);
+ }
+ }
+
+ // Retrieve a file from the repository
+ public async Task GetFileAsync(string fileName)
+ {
+ string filePath = Path.Combine(_storageDirectory, fileName);
+
+ // Ensure the file exists
+ if (!File.Exists(filePath))
+ {
+ throw new FileNotFoundException("File not found.");
+ }
+
+ var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
+ return await Task.FromResult(fileStream);
+ }
+
+ // Update an existing file in the repository
+ public async Task UpdateFileAsync(Stream fileStream, string fileName)
+ {
+ string filePath = Path.Combine(_storageDirectory, fileName);
+
+ // Ensure the file exists
+ if (!File.Exists(filePath))
+ {
+ throw new FileNotFoundException("File not found.");
+ }
+
+ using (var file = new FileStream(filePath, FileMode.Truncate, FileAccess.Write))
+ {
+ await fileStream.CopyToAsync(file);
+ }
+ }
+
+ // Delete a file from the repository
+ public async Task DeleteFileAsync(string fileName)
+ {
+ string filePath = Path.Combine(_storageDirectory, fileName);
+
+ // Ensure the file exists
+ if (!File.Exists(filePath))
+ {
+ throw new FileNotFoundException("File not found.");
+ }
+
+ File.Delete(filePath);
+ await Task.CompletedTask;
+ }
+ }
+
+}
diff --git a/DataAccess/FileDialogs/FileStorage.cs b/DataAccess/FileDialogs/FileStorage.cs
new file mode 100644
index 0000000..68aac4a
--- /dev/null
+++ b/DataAccess/FileDialogs/FileStorage.cs
@@ -0,0 +1,139 @@
+using DataAccess.FileDialogs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace DataAccess.FileDialogs
+{
+ internal class FileStorage
+ {
+ using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Windows.Forms;
+
+public class FileStorageManager
+ {
+ // Dictionary to store files with unique IDs as keys
+ private readonly Dictionary _openedFiles = new Dictionary();
+
+ // Method to open a file and add it to the storage
+ public Guid OpenFile()
+ {
+ using (OpenFileDialog openFileDialog = new OpenFileDialog())
+ {
+ openFileDialog.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
+ openFileDialog.Multiselect = true; // Allow multiple file selection
+ openFileDialog.Title = "Select Files";
+
+ if (openFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ foreach (var filePath in openFileDialog.FileNames)
+ {
+ var fileId = Guid.NewGuid();
+ var openedFile = new OpenedFile(fileId, filePath);
+
+ // Add to storage
+ _openedFiles[fileId] = openedFile;
+
+ Console.WriteLine($"File '{openedFile.FilePath}' opened with ID: {fileId}");
+ }
+ }
+ }
+
+ return Guid.Empty;
+ }
+
+ // Method to get an opened file by ID
+ public OpenedFile GetFile(Guid fileId)
+ {
+ if (_openedFiles.TryGetValue(fileId, out var openedFile))
+ {
+ return openedFile;
+ }
+
+ throw new KeyNotFoundException("File not found.");
+ }
+
+ // Method to close a file by ID
+ public void CloseFile(Guid fileId)
+ {
+ if (_openedFiles.ContainsKey(fileId))
+ {
+ _openedFiles.Remove(fileId);
+ Console.WriteLine($"File with ID: {fileId} has been closed.");
+ }
+ else
+ {
+ throw new KeyNotFoundException("File not found.");
+ }
+ }
+
+ // Method to read content of an opened file by ID
+ public string ReadFileContent(Guid fileId)
+ {
+ var openedFile = GetFile(fileId);
+ return File.ReadAllText(openedFile.FilePath);
+ }
+
+ // Method to list all opened files
+ public void ListOpenedFiles()
+ {
+ foreach (var file in _openedFiles.Values)
+ {
+ Console.WriteLine($"File ID: {file.Id}, Path: {file.FilePath}");
+ }
+ }
+ }
+
+ // Class representing an opened file
+ public class OpenedFile
+ {
+ public Guid Id { get; }
+ public string FilePath { get; }
+
+ public OpenedFile(Guid id, string filePath)
+ {
+ Id = id;
+ FilePath = filePath;
+ }
+ }
+
+}
+
+class Program
+{
+ [STAThread] // Required for OpenFileDialog
+ static void Main()
+ {
+ var fileStorageManager = new FileStorageManager();
+
+ // Open files and add them to the storage
+ fileStorageManager.OpenFile();
+
+ // List all opened files
+ Console.WriteLine("\nOpened Files:");
+ fileStorageManager.ListOpenedFiles();
+
+ // Example: Read content of the first opened file (if any)
+ var openedFiles = new List(fileStorageManager._openedFiles.Keys);
+ if (openedFiles.Count > 0)
+ {
+ var firstFileId = openedFiles[0];
+ Console.WriteLine($"\nReading content of the first opened file (ID: {firstFileId}):");
+ string content = fileStorageManager.ReadFileContent(firstFileId);
+ Console.WriteLine(content);
+ }
+
+ // Close all files
+ foreach (var fileId in openedFiles)
+ {
+ fileStorageManager.CloseFile(fileId);
+ }
+ }
+}
+
+}
diff --git a/DataAccess/FileDialogs/IFileRepository.cs b/DataAccess/FileDialogs/IFileRepository.cs
new file mode 100644
index 0000000..d42736e
--- /dev/null
+++ b/DataAccess/FileDialogs/IFileRepository.cs
@@ -0,0 +1,14 @@
+using System.IO;
+using System.Threading.Tasks;
+
+namespace DataAccess.FileDialogs
+{
+ public interface IFileRepository
+ {
+ Task SaveFileAsync(Stream fileStream, string fileName);
+ Task GetFileAsync(string fileName);
+ Task DeleteFileAsync(string fileName);
+ Task UpdateFileAsync(Stream fileStream, string fileName);
+ }
+
+}
diff --git a/DataAccess/FileDialogs/ProgramExample.cs b/DataAccess/FileDialogs/ProgramExample.cs
new file mode 100644
index 0000000..86526af
--- /dev/null
+++ b/DataAccess/FileDialogs/ProgramExample.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DataAccess.FileDialogs
+{
+ using System;
+ using System.IO;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ class ProgramExample
+ {
+ static async Task Main(string[] args)
+ {
+ string storagePath = Path.Combine(Environment.CurrentDirectory, "UserFiles");
+ IFileRepository fileRepository = new FileRepository(storagePath);
+
+ // Save a file
+ string fileName = "example.txt";
+ using (var fileStream = new MemoryStream(Encoding.UTF8.GetBytes("Hello, World!")))
+ {
+ await fileRepository.SaveFileAsync(fileStream, fileName);
+ Console.WriteLine($"File '{fileName}' saved.");
+ }
+
+ // Retrieve a file
+ using (Stream retrievedFile = await fileRepository.GetFileAsync(fileName))
+ {
+ using (var reader = new StreamReader(retrievedFile))
+ {
+ string content = await reader.ReadToEndAsync();
+ Console.WriteLine($"Retrieved file content: {content}");
+ }
+ }
+
+ // Update a file
+ using (var updateStream = new MemoryStream(Encoding.UTF8.GetBytes("Updated content!")))
+ {
+ await fileRepository.UpdateFileAsync(updateStream, fileName);
+ Console.WriteLine($"File '{fileName}' updated.");
+ }
+
+ // Retrieve updated file
+ using (Stream updatedFile = await fileRepository.GetFileAsync(fileName))
+ {
+ using (var reader = new StreamReader(updatedFile))
+ {
+ string updatedContent = await reader.ReadToEndAsync();
+ Console.WriteLine($"Updated file content: {updatedContent}");
+ }
+ }
+
+ // Delete a file
+ await fileRepository.DeleteFileAsync(fileName);
+ Console.WriteLine($"File '{fileName}' deleted.");
+ }
+ }
+
+}
diff --git a/DataAccess/JsonConverters/BaseConverter.cs b/DataAccess/JsonConverters/BaseConverter.cs
new file mode 100644
index 0000000..bb5c897
--- /dev/null
+++ b/DataAccess/JsonConverters/BaseConverter.cs
@@ -0,0 +1,95 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using StructureHelperCommon.Models;
+using System;
+using System.Reflection;
+
+namespace DataAccess.JsonConverters
+{
+
+
+ public abstract class BaseConverter : JsonConverter
+ {
+ private IShiftTraceLogger traceLogger;
+
+ protected BaseConverter(IShiftTraceLogger logger)
+ {
+ traceLogger = logger;
+ }
+
+ public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
+ {
+ traceLogger.AddMessage($"Serializing {typeof(T).Name} (ID: {GetId(value)})");
+
+ // Use JsonSerializer's default behavior to handle attributes like [JsonIgnore] and [JsonProperty]
+ var jo = new JObject();
+ foreach (var prop in typeof(T).GetProperties())
+ {
+ if (!ShouldIgnoreProperty(prop))
+ {
+ string propertyName = GetPropertyName(prop);
+ var propValue = prop.GetValue(value);
+ jo.Add(propertyName, JToken.FromObject(propValue, serializer));
+ }
+ }
+ jo.WriteTo(writer);
+ }
+
+ // Helper method to check if a property should be ignored
+ private bool ShouldIgnoreProperty(PropertyInfo prop)
+ {
+ // Check for [JsonIgnore] attribute
+ var jsonIgnoreAttribute = prop.GetCustomAttribute();
+ return jsonIgnoreAttribute != null;
+ }
+
+ // Helper method to get the property name, considering [JsonProperty] and [JsonPropertyName] attributes
+ private string GetPropertyName(PropertyInfo prop)
+ {
+ // Check for [JsonProperty] attribute (for Newtonsoft.Json)
+ var jsonPropertyAttribute = prop.GetCustomAttribute();
+ if (jsonPropertyAttribute != null)
+ {
+ return jsonPropertyAttribute.PropertyName;
+ }
+
+ // Check for [JsonPropertyName] attribute (for System.Text.Json compatibility)
+ var jsonPropertyNameAttribute = prop.GetCustomAttribute();
+ if (jsonPropertyNameAttribute != null)
+ {
+ return jsonPropertyNameAttribute.Name;
+ }
+
+ // Default to the property name if no attributes are found
+ return prop.Name;
+ }
+
+ public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ traceLogger.AddMessage($"Deserializing {typeof(T).Name}");
+ // Use JsonSerializer's default behavior to handle attributes during deserialization
+ JObject jo = JObject.Load(reader);
+ T obj = Activator.CreateInstance();
+
+ foreach (var prop in typeof(T).GetProperties())
+ {
+ if (!ShouldIgnoreProperty(prop) && jo.TryGetValue(GetPropertyName(prop), out JToken value))
+ {
+ var propValue = value.ToObject(prop.PropertyType, serializer);
+ prop.SetValue(obj, propValue);
+ }
+ }
+
+ traceLogger.AddMessage($"Deserialized {typeof(T).Name} (ID: {GetId(obj)})");
+ return obj;
+ }
+
+ // Method to get the ID for logging purposes, assumes all classes have an 'Id' property of type Guid.
+ private Guid GetId(object obj)
+ {
+ var idProp = obj.GetType().GetProperty("Id");
+ return idProp != null ? (Guid)idProp.GetValue(obj) : Guid.Empty;
+ }
+ }
+
+}
diff --git a/DataAccess/JsonConverters/CrossSectionJsonConverter.cs b/DataAccess/JsonConverters/CrossSectionJsonConverter.cs
new file mode 100644
index 0000000..33c3b81
--- /dev/null
+++ b/DataAccess/JsonConverters/CrossSectionJsonConverter.cs
@@ -0,0 +1,17 @@
+using DataAccess.DTOs;
+using StructureHelperCommon.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DataAccess.JsonConverters
+{
+ public class CrossSectionJsonConverter : BaseConverter
+ {
+ public CrossSectionJsonConverter(IShiftTraceLogger logger) : base(logger)
+ {
+ }
+ }
+}
diff --git a/StructureHelper.sln b/StructureHelper.sln
index 7bf9930..1b41809 100644
--- a/StructureHelper.sln
+++ b/StructureHelper.sln
@@ -18,6 +18,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FieldVisualizer", "FieldVis
{F1548BD2-7FE8-46C2-9BC4-9BA813A5C59A} = {F1548BD2-7FE8-46C2-9BC4-9BA813A5C59A}
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataAccess", "DataAccess\DataAccess.csproj", "{BB820532-B66E-4876-903D-D6208C2A5352}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -48,6 +50,10 @@ Global
{6CAC5B83-81F3-47C2-92A1-0F94A58491C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CAC5B83-81F3-47C2-92A1-0F94A58491C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CAC5B83-81F3-47C2-92A1-0F94A58491C2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BB820532-B66E-4876-903D-D6208C2A5352}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BB820532-B66E-4876-903D-D6208C2A5352}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BB820532-B66E-4876-903D-D6208C2A5352}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BB820532-B66E-4876-903D-D6208C2A5352}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/StructureHelperLogics/Models/Storages/FileStorageManager.cs b/StructureHelperLogics/Models/Storages/FileStorageManager.cs
new file mode 100644
index 0000000..8e1f607
--- /dev/null
+++ b/StructureHelperLogics/Models/Storages/FileStorageManager.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Windows.Forms;
+
+namespace StructureHelperLogics.Models.Storages
+{
+
+
+ public class FileStorageManager
+ {
+ private readonly Dictionary _openedFiles = new Dictionary();
+
+ public void OpenFile()
+ {
+ using (OpenFileDialog openFileDialog = new OpenFileDialog())
+ {
+ openFileDialog.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
+ openFileDialog.Multiselect = true;
+ openFileDialog.Title = "Select Files";
+
+ if (openFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ foreach (var filePath in openFileDialog.FileNames)
+ {
+ var fileId = Guid.NewGuid();
+ var openedFile = new OpenedFile(fileId, filePath);
+
+ _openedFiles[fileId] = openedFile;
+
+ Console.WriteLine($"File '{openedFile.FilePath}' opened with ID: {fileId}");
+ }
+ }
+ }
+ }
+
+ public OpenedFile GetFile(Guid fileId)
+ {
+ if (_openedFiles.TryGetValue(fileId, out var openedFile))
+ {
+ return openedFile;
+ }
+
+ throw new KeyNotFoundException("File not found.");
+ }
+
+ public void CloseFile(Guid fileId)
+ {
+ if (_openedFiles.ContainsKey(fileId))
+ {
+ _openedFiles.Remove(fileId);
+ Console.WriteLine($"File with ID: {fileId} has been closed.");
+ }
+ else
+ {
+ throw new KeyNotFoundException("File not found.");
+ }
+ }
+
+ public void AddRelatedObjectToFile(Guid fileId, FileRelatedObject relatedObject)
+ {
+ var openedFile = GetFile(fileId);
+ openedFile.AddRelatedObject(relatedObject);
+ Console.WriteLine($"Added related object to file ID: {fileId}, Related Object: {relatedObject}");
+ }
+
+ public FileRelatedObject GetRelatedObjectFromFile(Guid fileId, Guid objectId)
+ {
+ var openedFile = GetFile(fileId);
+ return openedFile.GetRelatedObject(objectId);
+ }
+
+ public void RemoveRelatedObjectFromFile(Guid fileId, Guid objectId)
+ {
+ var openedFile = GetFile(fileId);
+ openedFile.RemoveRelatedObject(objectId);
+ Console.WriteLine($"Removed related object with ID: {objectId} from file ID: {fileId}");
+ }
+
+ public void ListOpenedFiles()
+ {
+ foreach (var file in _openedFiles.Values)
+ {
+ Console.WriteLine($"File ID: {file.Id}, Path: {file.FilePath}, Related Objects Count: {file.RelatedObjects.Count}");
+ }
+ }
+ }
+
+}
diff --git a/StructureHelperLogics/Models/Storages/OpenedFile.cs b/StructureHelperLogics/Models/Storages/OpenedFile.cs
new file mode 100644
index 0000000..f89cd85
--- /dev/null
+++ b/StructureHelperLogics/Models/Storages/OpenedFile.cs
@@ -0,0 +1,84 @@
+using StructureHelperCommon.Infrastructures.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace StructureHelperLogics.Models.Storages
+{
+ public class OpenedFile
+ {
+ public Guid Id { get; }
+ public string FilePath { get; }
+ public List RelatedObjects { get; } // List of related objects
+
+ public OpenedFile(Guid id, string filePath)
+ {
+ Id = id;
+ FilePath = filePath;
+ RelatedObjects = new List();
+ }
+
+ public void AddRelatedObject(ISaveable relatedObject)
+ {
+ RelatedObjects.Add(relatedObject);
+ }
+
+ public void RemoveRelatedObject(Guid objectId)
+ {
+ RelatedObjects.RemoveAll(o => o.Id == objectId);
+ }
+
+ public ISaveable GetRelatedObject(Guid objectId)
+ {
+ return RelatedObjects.Find(o => o.Id == objectId);
+ }
+ }
+
+ class Program
+ {
+ [STAThread] // Required for OpenFileDialog
+ static void Main()
+ {
+ var fileStorageManager = new FileStorageManager();
+
+ // Open files and add them to the storage
+ fileStorageManager.OpenFile();
+
+ // List all opened files
+ Console.WriteLine("\nOpened Files:");
+ fileStorageManager.ListOpenedFiles();
+
+ // Example: Adding related objects to the first opened file (if any)
+ var openedFiles = new List(fileStorageManager._openedFiles.Keys);
+ if (openedFiles.Count > 0)
+ {
+ var firstFileId = openedFiles[0];
+ var relatedObject = new FileRelatedObject("Sample Object", "This is a sample description");
+ fileStorageManager.AddRelatedObjectToFile(firstFileId, relatedObject);
+
+ Console.WriteLine("\nAfter Adding Related Object:");
+ fileStorageManager.ListOpenedFiles();
+
+ // Retrieve related object
+ var retrievedObject = fileStorageManager.GetRelatedObjectFromFile(firstFileId, relatedObject.Id);
+ Console.WriteLine($"\nRetrieved Related Object: {retrievedObject}");
+
+ // Remove related object
+ fileStorageManager.RemoveRelatedObjectFromFile(firstFileId, relatedObject.Id);
+
+ Console.WriteLine("\nAfter Removing Related Object:");
+ fileStorageManager.ListOpenedFiles();
+ }
+
+ // Close all files
+ foreach (var fileId in openedFiles)
+ {
+ fileStorageManager.CloseFile(fileId);
+ }
+ }
+ }
+
+
+}