diff --git a/.idea/.idea.GBase/.idea/encodings.xml b/.idea/.idea.GBase/.idea/encodings.xml deleted file mode 100644 index df87cf9..0000000 --- a/.idea/.idea.GBase/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/GBase.sln.DotSettings b/GBase.sln.DotSettings index 976aca6..cb097b1 100644 --- a/GBase.sln.DotSettings +++ b/GBase.sln.DotSettings @@ -2,5 +2,6 @@ Author: $USER_NAME$ Created: $CREATED_YEAR$-$CREATED_MONTH$-$CREATED_DAY$ Copyright(c) $CREATED_YEAR$ SimonG. All Rights Reserved. + <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> \ No newline at end of file diff --git a/GBase/Attributes/GBaseTableAttribute.cs b/GBase/Attributes/GBaseTableAttribute.cs index 078e9f6..df332f8 100644 --- a/GBase/Attributes/GBaseTableAttribute.cs +++ b/GBase/Attributes/GBaseTableAttribute.cs @@ -13,6 +13,11 @@ namespace GBase.Attributes [AttributeUsage(AttributeTargets.Class)] public class GBaseTableAttribute : Attribute //TODO: Decide how to handle enums (as table or their own type) { + public GBaseTableAttribute(string folderName) + { + FolderName = folderName; + } + public string FolderName { get; } } } \ No newline at end of file diff --git a/GBase/DataHandling/Cache/XmlDataHandlerCache.cs b/GBase/DataHandling/Cache/XmlDataHandlerCache.cs index 43d7883..e6db2a2 100644 --- a/GBase/DataHandling/Cache/XmlDataHandlerCache.cs +++ b/GBase/DataHandling/Cache/XmlDataHandlerCache.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; using GBase.DataHandling.Cache.Factories; using GBase.Interfaces.DataHandling.Cache; @@ -103,9 +105,11 @@ namespace GBase.DataHandling.Cache /// /// The that implements the property /// The of the property + /// /// The name of the property + /// /// An with all the values for the property - public async Task> TryGetValues(string propertyName) + public async Task> TryGetValues(FileStream file, string propertyName, CancellationToken cancellationToken) { IXmlDataHandlerCacheEntry entry = _cache.FirstOrDefault(e => e.Type == typeof(T)); IDataHandlerCachePropertyEntry property = entry?.Properties.FirstOrDefault(p => p.PropertyName.Equals(propertyName)); @@ -114,7 +118,7 @@ namespace GBase.DataHandling.Cache if (!property.IsInitialized) //initialize property by reading the values from the xml { - List values = (await _xmlDataReader.Read(propertyName))?.ToList(); + List values = (await _xmlDataReader.Read(file, propertyName, cancellationToken))?.ToList(); if (values != null) { foreach (TProperty value in values.Where(value => !property.Values.Any(v => v.Equals(value)))) diff --git a/GBase/DataHandling/Exceptions/InvalidXmlFileException.cs b/GBase/DataHandling/Exceptions/InvalidXmlFileException.cs new file mode 100644 index 0000000..adfc8fc --- /dev/null +++ b/GBase/DataHandling/Exceptions/InvalidXmlFileException.cs @@ -0,0 +1,17 @@ +// Author: Gockner, Simon +// Created: 2020-11-09 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; + +namespace GBase.DataHandling.Exceptions +{ + public class InvalidXmlFileException : Exception + { + public InvalidXmlFileException(string message) + : base(message) + { + + } + } +} \ No newline at end of file diff --git a/GBase/DataHandling/Factories/IDataHandlerFactory.cs b/GBase/DataHandling/Factories/IDataHandlerFactory.cs new file mode 100644 index 0000000..2d0413d --- /dev/null +++ b/GBase/DataHandling/Factories/IDataHandlerFactory.cs @@ -0,0 +1,13 @@ +// Author: Simon Gockner +// Created: 2020-09-19 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using GBase.Interfaces.DataHandling; + +namespace GBase.DataHandling.Factories +{ + public interface IDataHandlerFactory + { + IDataHandler Create(); + } +} \ No newline at end of file diff --git a/GBase/DataHandling/Factories/IXmlDataReaderFactory.cs b/GBase/DataHandling/Factories/IXmlDataReaderFactory.cs index 3670de9..ad11818 100644 --- a/GBase/DataHandling/Factories/IXmlDataReaderFactory.cs +++ b/GBase/DataHandling/Factories/IXmlDataReaderFactory.cs @@ -14,8 +14,7 @@ namespace GBase.DataHandling.Factories /// /// Creates an /// - /// The path to the xml file /// A newly created instance of the implementation for - IXmlDataReader Create(string path); + IXmlDataReader Create(); } } \ No newline at end of file diff --git a/GBase/DataHandling/Factories/IXmlDataWriterFactory.cs b/GBase/DataHandling/Factories/IXmlDataWriterFactory.cs index 3c17344..53eb50b 100644 --- a/GBase/DataHandling/Factories/IXmlDataWriterFactory.cs +++ b/GBase/DataHandling/Factories/IXmlDataWriterFactory.cs @@ -14,9 +14,7 @@ namespace GBase.DataHandling.Factories /// /// Creates an /// - /// The path to the xml file - /// The root element name of the xml file /// A newly created instance of the implementation for - IXmlDataWriter Create(string path, string rootElementName); + IXmlDataWriter Create(); } } \ No newline at end of file diff --git a/GBase/DataHandling/Factories/Pool/IPoolItemFactory.cs b/GBase/DataHandling/Factories/Pool/IPoolItemFactory.cs new file mode 100644 index 0000000..5fb24c6 --- /dev/null +++ b/GBase/DataHandling/Factories/Pool/IPoolItemFactory.cs @@ -0,0 +1,13 @@ +// Author: Simon Gockner +// Created: 2020-09-19 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using GBase.Interfaces.DataHandling.Pool; + +namespace GBase.DataHandling.Factories.Pool +{ + public interface IPoolItemFactory + { + IPoolItem Create(T item); + } +} \ No newline at end of file diff --git a/GBase/DataHandling/Factories/Pool/IPoolRequestFactory.cs b/GBase/DataHandling/Factories/Pool/IPoolRequestFactory.cs new file mode 100644 index 0000000..3f1e51e --- /dev/null +++ b/GBase/DataHandling/Factories/Pool/IPoolRequestFactory.cs @@ -0,0 +1,13 @@ +// Author: Simon Gockner +// Created: 2020-09-19 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using GBase.Interfaces.DataHandling.Pool; + +namespace GBase.DataHandling.Factories.Pool +{ + public interface IPoolRequestFactory + { + IPoolRequest Create(object requester); + } +} \ No newline at end of file diff --git a/GBase/DataHandling/Factories/Pool/PoolItemFactory.cs b/GBase/DataHandling/Factories/Pool/PoolItemFactory.cs new file mode 100644 index 0000000..e6bd04e --- /dev/null +++ b/GBase/DataHandling/Factories/Pool/PoolItemFactory.cs @@ -0,0 +1,14 @@ +// Author: Simon Gockner +// Created: 2020-09-19 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using GBase.DataHandling.Pool; +using GBase.Interfaces.DataHandling.Pool; + +namespace GBase.DataHandling.Factories.Pool +{ + public class PoolItemFactory : IPoolItemFactory + { + public IPoolItem Create(T item) => new PoolItem(item); + } +} \ No newline at end of file diff --git a/GBase/DataHandling/Pool/DataHandlerPool.cs b/GBase/DataHandling/Pool/DataHandlerPool.cs new file mode 100644 index 0000000..8438d04 --- /dev/null +++ b/GBase/DataHandling/Pool/DataHandlerPool.cs @@ -0,0 +1,101 @@ +// Author: Simon Gockner +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using GBase.DataHandling.Factories; +using GBase.DataHandling.Factories.Pool; +using GBase.Interfaces.DataHandling; +using GBase.Interfaces.DataHandling.Pool; + +namespace GBase.DataHandling.Pool +{ + public class DataHandlerPool : IDataHandlerPool + { + private readonly int _availableDataHandlers; + private readonly IPoolItemFactory _poolItemFactory; + private readonly IPoolRequestFactory _poolRequestFactory; + private readonly IDataHandlerFactory _dataHandlerFactory; + + private readonly List> _pool; + private readonly List _waitingRequesters; + + public DataHandlerPool(int availableDataHandlers, + IPoolItemFactory poolItemFactory, + IPoolRequestFactory poolRequestFactory, + IDataHandlerFactory dataHandlerFactory) + { + _availableDataHandlers = availableDataHandlers; + _poolItemFactory = poolItemFactory; + _poolRequestFactory = poolRequestFactory; + _dataHandlerFactory = dataHandlerFactory; + + _pool = new List>(); + _waitingRequesters = new List(); + } + + public async Task RequestDataHandler(object requester, bool overwrite, CancellationToken cancellationToken) + { + if (_pool.All(i => i.InUse) && _pool.Count == _availableDataHandlers) //no free dataHandlers available + return await WaitForDataHandler(requester, cancellationToken); + else if (_pool.All(i => i.InUse)) //every existing dataHandler is used -> create a new one + return CreateDataHandler(overwrite); + else //there are unused dataHandlers existing + return GetDataHandler(); + } + + private async Task WaitForDataHandler(object requester, CancellationToken cancellationToken) + { + IDataHandler dataHandler = null; + IPoolRequest request = _poolRequestFactory.Create(requester); + + while (dataHandler == null) + { + if (_waitingRequesters.Contains(request) && _waitingRequesters.IndexOf(request) == 0) //request is in list and is first + { + if (_pool.Any(i => !i.InUse)) + dataHandler = GetDataHandler(); + else + await Task.Delay(1000, cancellationToken); + } + else if (_waitingRequesters.Contains(request)) //request is in list but not first + await Task.Delay(1000, cancellationToken); + else + _waitingRequesters.Add(request); + } + + _waitingRequesters.Remove(request); + + return dataHandler; + } + + private IDataHandler CreateDataHandler(bool overwrite) + { + IPoolItem item = _poolItemFactory.Create(_dataHandlerFactory.Create()); + item.Item.Init(overwrite); + _pool.Add(item); + + item.InUse = true; + return item.Item; + } + + private IDataHandler GetDataHandler() + { + IPoolItem item = _pool.First(i => !i.InUse); + + item.InUse = true; + return item.Item; + } + + public async ValueTask DisposeAsync() + { + while (_waitingRequesters.Any()) + { + await Task.Delay(1000); + } + } + } +} \ No newline at end of file diff --git a/GBase/DataHandling/Pool/PoolItem.cs b/GBase/DataHandling/Pool/PoolItem.cs new file mode 100644 index 0000000..90fd4e9 --- /dev/null +++ b/GBase/DataHandling/Pool/PoolItem.cs @@ -0,0 +1,19 @@ +// Author: Simon Gockner +// Created: 2020-09-19 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using GBase.Interfaces.DataHandling.Pool; + +namespace GBase.DataHandling.Pool +{ + public class PoolItem : IPoolItem + { + public PoolItem(T item) + { + Item = item; + } + + public T Item { get; } + public bool InUse { get; set; } + } +} \ No newline at end of file diff --git a/GBase/DataHandling/Pool/PoolRequest.cs b/GBase/DataHandling/Pool/PoolRequest.cs new file mode 100644 index 0000000..9a565fe --- /dev/null +++ b/GBase/DataHandling/Pool/PoolRequest.cs @@ -0,0 +1,18 @@ +// Author: Simon Gockner +// Created: 2020-09-19 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using GBase.Interfaces.DataHandling.Pool; + +namespace GBase.DataHandling.Pool +{ + public class PoolRequest : IPoolRequest + { + public PoolRequest(object requester) + { + Requester = requester; + } + + public object Requester { get; } + } +} \ No newline at end of file diff --git a/GBase/DataHandling/XmlDataHandler.cs b/GBase/DataHandling/XmlDataHandler.cs index 6c79de3..34948f6 100644 --- a/GBase/DataHandling/XmlDataHandler.cs +++ b/GBase/DataHandling/XmlDataHandler.cs @@ -5,12 +5,15 @@ using System; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using GBase.DataHandling.Cache.Factories; using GBase.DataHandling.Factories; using GBase.Helpers; +using GBase.Interfaces; using GBase.Interfaces.DataHandling; using GBase.Interfaces.DataHandling.Xml; using GBase.Interfaces.DataHandling.Xml.Cache; @@ -46,18 +49,15 @@ namespace GBase.DataHandling /// A that handles its data in an xml file /// /// The path to the xml file - /// The root element name of the xml file /// The factory /// The factory /// The factory - public XmlDataHandler(string path, - string rootElementName, - IXmlDataReaderFactory xmlDataReaderFactory, + public XmlDataHandler(IXmlDataReaderFactory xmlDataReaderFactory, IXmlDataWriterFactory xmlDataWriterFactory, IXmlDataHandlerCacheFactory xmlDataHandlerCacheFactory) { - _xmlDataWriter = xmlDataWriterFactory.Create(path, rootElementName); - _xmlDataReader = xmlDataReaderFactory.Create(path); + _xmlDataWriter = xmlDataWriterFactory.Create(); + _xmlDataReader = xmlDataReaderFactory.Create(); _cache = xmlDataHandlerCacheFactory.Create(_xmlDataReader); } @@ -66,35 +66,58 @@ namespace GBase.DataHandling /// Initialize the /// /// If true an existing value is overwritten, if false an additional value is added - /// A to cancel the async operation /// Returns true if successful, false if not - public async Task Init(bool overwrite, CancellationToken cancellationToken) + public bool Init(bool overwrite) { if (_isInitialized) return false; _overwrite = overwrite; - if (!await _xmlDataWriter.Init(cancellationToken)) - return false; + _isInitialized = true; + return true; + } - if (!await _xmlDataReader.Init(cancellationToken)) + public async Task AddEntry(T entry, IGBaseTable table, FileStream entryFile, CancellationToken cancellationToken) + { + if (!await _xmlDataWriter.InitFile(entryFile, table.Type.Name, cancellationToken)) return false; - _isInitialized = true; + foreach (var column in table.Columns) + { + //TODO: Set value for each column + PropertyInfo property = entry.GetType().GetProperty(column.Name); + if (property == null) + continue; //TODO: What to do in this case? (Shouldn't really happen...) + + string valueString; + if (property.PropertyType != typeof(string) && property.GetValue(entry) is IEnumerable enumerable) + valueString = enumerable.ToGBaseString(); + else + valueString = property.GetValue(entry).ToString(); + + await _xmlDataWriter.Write(entryFile, property.Name, valueString, property.PropertyType, _overwrite, cancellationToken); + } return true; } + public Task RemoveEntry(T entry) + { + throw new NotImplementedException(); + } + /// /// Set the value for the given property /// /// The that implements the property /// The of the property + /// /// The name of the property /// The value to set + /// /// A to await - public async Task SetValue(string propertyName, TProperty value) + public async Task SetValue(FileStream entryFile, string propertyName, TProperty value, CancellationToken cancellationToken) { if (value == null) return; @@ -106,7 +129,7 @@ namespace GBase.DataHandling valueString = value.ToString(); await _cache.SetValue(propertyName, value, _overwrite); - await _xmlDataWriter.Write(propertyName, valueString, _overwrite); + await _xmlDataWriter.Write(entryFile, propertyName, valueString, _overwrite, cancellationToken); } /// @@ -114,10 +137,12 @@ namespace GBase.DataHandling /// /// The of the property /// The of the property + /// /// The name of the property /// The value to set + /// /// A to await - public async Task RemoveValue(string propertyName, TProperty value) + public async Task RemoveValue(FileStream entryFile, string propertyName, TProperty value, CancellationToken cancellationToken) { if (value == null) return; @@ -129,7 +154,7 @@ namespace GBase.DataHandling valueString = value.ToString(); await _cache.TryRemoveValue(propertyName, value); - await _xmlDataWriter.Remove(propertyName, valueString); + await _xmlDataWriter.Remove(entryFile, propertyName, valueString, cancellationToken); } /// @@ -137,11 +162,13 @@ namespace GBase.DataHandling /// /// The that implements the property /// The of the property + /// /// The name of the property + /// /// The value for the given property - public async Task GetValue(string propertyName) + public async Task GetValue(FileStream file, string propertyName, CancellationToken cancellationToken) { - IEnumerable enumerable = await GetValues(propertyName); + IEnumerable enumerable = await GetValues(file, propertyName, cancellationToken); return enumerable == null ? default : enumerable.FirstOrDefault(); } @@ -150,25 +177,17 @@ namespace GBase.DataHandling /// /// The that implements the property /// The of the property + /// /// The name of the property + /// /// An with all the values for the property - public async Task> GetValues(string propertyName) + public async Task> GetValues(FileStream file, string propertyName, CancellationToken cancellationToken) { - IEnumerable cachedValues = await _cache.TryGetValues(propertyName); + IEnumerable cachedValues = await _cache.TryGetValues(file, propertyName, cancellationToken); if (cachedValues != null) return cachedValues; - return await _xmlDataReader.Read(propertyName); - } - - /// - /// Dispose used resources asynchronously - /// - /// A to await - public async ValueTask DisposeAsync() - { - await _xmlDataReader.DisposeAsync(); - await _xmlDataWriter.DisposeAsync(); + return await _xmlDataReader.Read(file, propertyName, cancellationToken); } } } \ No newline at end of file diff --git a/GBase/DataHandling/XmlDataReader.cs b/GBase/DataHandling/XmlDataReader.cs index 03b7396..e83ba65 100644 --- a/GBase/DataHandling/XmlDataReader.cs +++ b/GBase/DataHandling/XmlDataReader.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; +using GBase.DataHandling.Exceptions; using GBase.Helpers; using GBase.Interfaces.DataHandling; using GBase.Interfaces.DataHandling.Xml; @@ -21,65 +22,34 @@ namespace GBase.DataHandling /// public class XmlDataReader : IXmlDataReader { - private readonly FileStream _file; - private XDocument _xmlDocument; - private XElement _rootElement; - - private bool _isInitialized; - private CancellationToken _cancellationToken; - - /// - /// A that reads from a xml file - /// - /// The path to the xml file - public XmlDataReader(string path) - { - _file = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - } - - /// - /// Initialize the - /// - /// A to cancel the async operation - /// Returns true if successful, false if not - /// No root element found - public async Task Init(CancellationToken cancellationToken) - { - if (_isInitialized) - return false; - - _cancellationToken = cancellationToken; - _xmlDocument = await XDocument.LoadAsync(_file, LoadOptions.None, _cancellationToken); - - _rootElement = _xmlDocument.Root; - if (_rootElement == null) - throw new Exception("No root element found."); - - _isInitialized = true; - - return true; - } - /// /// Read the data of a property /// /// The /// The of the property + /// /// The name of the property + /// /// The data of the given property, null if no data found /// /// Invalid found for the read object - public async Task> Read(string propertyName) + public async Task> Read(FileStream file, string propertyName, CancellationToken cancellationToken) //TODO: Read currently doesn't work for newly added items -> probably because file was loaded before new items were added; is probably not a problem -> cache { string typeName = typeof(T).FullName; if (typeName == null) throw new ArgumentNullException(nameof(typeName)); + XDocument xmlDocument = await XDocument.LoadAsync(file, LoadOptions.None, cancellationToken); + file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it + return await Task.Run(() => { - XElement typeElement = _rootElement.Element(typeName); - XElement propertyElement = typeElement?.Element(propertyName); + XElement rootElement = xmlDocument.Root; + if (rootElement == null) + throw new InvalidXmlFileException("No root element is set."); + + XElement propertyElement = rootElement.Element(propertyName); XAttribute propertyTypeAttribute = propertyElement?.Attribute(XmlDataHandler.VALUE_TYPE_ATTRIBUTE_NAME); if (propertyTypeAttribute == null) return null; @@ -98,16 +68,7 @@ namespace GBase.DataHandling return values.Select(Enumerables.ConvertToGBaseEnumerable).ToList(); return values.Select(value => (TProperty)Convert.ChangeType(value, typeof(TProperty))).ToList(); - }, _cancellationToken); - } - - /// - /// Dispose used resources asynchronously - /// - /// A to await - public async ValueTask DisposeAsync() - { - await _file.DisposeAsync(); + }, cancellationToken); } } } \ No newline at end of file diff --git a/GBase/DataHandling/XmlDataWriter.cs b/GBase/DataHandling/XmlDataWriter.cs index 3799f0b..31cfc74 100644 --- a/GBase/DataHandling/XmlDataWriter.cs +++ b/GBase/DataHandling/XmlDataWriter.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; +using GBase.DataHandling.Exceptions; using GBase.Interfaces.DataHandling; using GBase.Interfaces.DataHandling.Xml; @@ -18,115 +19,96 @@ namespace GBase.DataHandling /// public class XmlDataWriter : IXmlDataWriter { - private readonly string _rootElementName; - private readonly FileStream _file; - - private XDocument _xmlDocument; - private XElement _rootElement; - - private bool _isInitialized; - private CancellationToken _cancellationToken; - - /// - /// A that writes to an xml file - /// - /// The path to the xml file - /// The root element name of the xml file - public XmlDataWriter(string path, string rootElementName) - { - _rootElementName = rootElementName; - _file = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); - } - /// /// Initialize the /// + /// + /// /// A to cancel the async operation /// Returns true if successful, false if not /// No root element found - public async Task Init(CancellationToken cancellationToken) + public async Task InitFile(FileStream file, string rootElementName, CancellationToken cancellationToken) { - if (_isInitialized) - return false; + //if the xml file isn't empty, return + if (file.Length > 3) //> 3 because of BOM + return true; //TODO: Don't return bool? + + XDocument xmlDocument = new XDocument(); + xmlDocument.Add(new XElement(rootElementName)); + file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it + await xmlDocument.SaveAsync(file, SaveOptions.OmitDuplicateNamespaces, cancellationToken); - _cancellationToken = cancellationToken; - //if the xml file is empty, write the root element - if (_file.Length <= 3) //<= 3 because of BOM - { - _xmlDocument = new XDocument(); - _xmlDocument.Add(new XElement(_rootElementName)); - _file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it - await _xmlDocument.SaveAsync(_file, SaveOptions.OmitDuplicateNamespaces, cancellationToken); - } - else - { - _xmlDocument = await XDocument.LoadAsync(_file, LoadOptions.None, _cancellationToken); - } - - _rootElement = _xmlDocument.Root;//?.Element(_rootElementName); - if (_rootElement == null) - throw new Exception("No root element found."); - - _isInitialized = true; return true; } /// /// Write the data of a property /// - /// The that implements the property + /// The /// The of the property + /// + /// The name of the property + /// The value of the property + /// If true an existing value is overwritten, if false an additional value is added + /// + /// A to await + public async Task Write(FileStream file, string propertyName, string value, bool overwrite, CancellationToken cancellationToken) => + await Write(file, propertyName, value, typeof(TProperty), overwrite, cancellationToken); + + /// + /// Write the data of a property + /// + /// The that implements the property + /// /// The name of the property /// The value of the property + /// /// If true an existing value is overwritten, if false an additional value is added + /// /// A to await /// - public async Task Write(string propertyName, string value, bool overwrite) + /// No root element is set + public async Task Write(FileStream file, string propertyName, string value, Type propertyType, bool overwrite, CancellationToken cancellationToken) { string typeName = typeof(T).FullName; if (typeName == null) throw new ArgumentNullException(nameof(typeName)); - - _file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it - - XElement typeElement = _rootElement.Element(typeName); - if (typeElement != null) //type element already exists + + file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it + XDocument xmlDocument = await XDocument.LoadAsync(file, LoadOptions.None, cancellationToken); + file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it + + XElement rootElement = xmlDocument.Root; + if (rootElement == null) + throw new InvalidXmlFileException("No root element is set."); + + XElement propertyElement = rootElement.Element(propertyName); + if (propertyElement != null) //property element already exists { - XElement propertyElement = typeElement.Element(propertyName); - if (propertyElement != null) //property element already exists + XElement valueElement = propertyElement.Element(XmlDataHandler.VALUE_ELEMENT_NAME); + if (valueElement != null && overwrite) //value element exists and overwrite is true { - XElement valueElement = propertyElement.Element(XmlDataHandler.VALUE_ELEMENT_NAME); - if (valueElement != null && overwrite) //value element exists and overwrite is true - { - valueElement.Value = value; //overwrite existing value - } - else if (!overwrite && propertyElement.Elements(XmlDataHandler.VALUE_ELEMENT_NAME).Any(v => v.Value.Equals(value))) //no overwrite and same value exists already - { - XElement sameValueElement = propertyElement.Elements(XmlDataHandler.VALUE_ELEMENT_NAME).First(v => v.Value.Equals(value)); - sameValueElement.Remove(); //remove the already existing value from its current position - propertyElement.AddFirst(sameValueElement); //add it as the first element again - } - else //no value element exists or overwrite is false - propertyElement.AddFirst(new XElement(XmlDataHandler.VALUE_ELEMENT_NAME) { Value = value }); //add a new value element + valueElement.Value = value; //overwrite existing value } - else //property element doesn't exist + else if (!overwrite && propertyElement.Elements(XmlDataHandler.VALUE_ELEMENT_NAME).Any(v => v.Value.Equals(value))) //no overwrite and same value exists already { - propertyElement = new XElement(propertyName, new XElement(XmlDataHandler.VALUE_ELEMENT_NAME) { Value = value }); //create the new property element with the value element - propertyElement.SetAttributeValue(XmlDataHandler.VALUE_TYPE_ATTRIBUTE_NAME, typeof(TProperty).FullName); //add the property type attribute - - typeElement.Add(propertyElement); //create new property element with the value element + XElement sameValueElement = propertyElement.Elements(XmlDataHandler.VALUE_ELEMENT_NAME).First(v => v.Value.Equals(value)); + sameValueElement.Remove(); //remove the already existing value from its current position + propertyElement.AddFirst(sameValueElement); //add it as the first element again } + else //no value element exists or overwrite is false + propertyElement.AddFirst(new XElement(XmlDataHandler.VALUE_ELEMENT_NAME) { Value = value }); //add a new value element } - else //type element doesn't exist + else //property element doesn't exist { - XElement propertyElement = new XElement(propertyName, new XElement(XmlDataHandler.VALUE_ELEMENT_NAME) { Value = value }); //create the new property element with the value element - propertyElement.SetAttributeValue(XmlDataHandler.VALUE_TYPE_ATTRIBUTE_NAME, typeof(TProperty).FullName); //add the property type attribute + propertyElement = new XElement(propertyName, new XElement(XmlDataHandler.VALUE_ELEMENT_NAME) { Value = value }); //create the new property element with the value element + propertyElement.SetAttributeValue(XmlDataHandler.VALUE_TYPE_ATTRIBUTE_NAME, propertyType.FullName); //add the property type attribute - _rootElement.Add(new XElement(typeName, propertyElement)); //create a new type element with the new property element + rootElement.Add(propertyElement); //create new property element with the value element } //TODO: check if whole file is overwritten (probably) -> performance issues for large files? - await _xmlDocument.SaveAsync(_file, SaveOptions.OmitDuplicateNamespaces, _cancellationToken); //save the document with the added elements + await xmlDocument.SaveAsync(file, SaveOptions.OmitDuplicateNamespaces, cancellationToken); //save the document with the added elements } /// @@ -134,19 +116,27 @@ namespace GBase.DataHandling /// /// The of the property /// The of the property + /// /// The name of the property /// The value to set + /// /// A to await /// - public async Task Remove(string propertyName, string value) + /// No root element is set + public async Task Remove(FileStream file, string propertyName, string value, CancellationToken cancellationToken) { string typeName = typeof(T).FullName; if (typeName == null) throw new ArgumentNullException(nameof(typeName)); - _file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it + XDocument xmlDocument = await XDocument.LoadAsync(file, LoadOptions.None, cancellationToken); + file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it - XElement typeElement = _rootElement.Element(typeName); + XElement rootElement = xmlDocument.Root; + if (rootElement == null) + throw new InvalidXmlFileException("No root element is set."); + + XElement typeElement = rootElement.Element(typeName); XElement propertyElement = typeElement?.Element(propertyName); XElement valueElement = propertyElement?.Elements(XmlDataHandler.VALUE_ELEMENT_NAME).FirstOrDefault(e => e.Value.Equals(value)); if (valueElement == null) @@ -155,17 +145,7 @@ namespace GBase.DataHandling valueElement.Remove(); //TODO: check if whole file is overwritten (probably) -> performance issues for large files? - await _xmlDocument.SaveAsync(_file, SaveOptions.OmitDuplicateNamespaces, _cancellationToken); //save the document with the added elements - } - - /// - /// Dispose used resources asynchronously - /// - /// A to await - public async ValueTask DisposeAsync() - { - await _xmlDocument.SaveAsync(_file, SaveOptions.None, _cancellationToken); - await _file.DisposeAsync(); + await xmlDocument.SaveAsync(file, SaveOptions.OmitDuplicateNamespaces, cancellationToken); //save the document with the added elements } } } \ No newline at end of file diff --git a/GBase/Exceptions/InvalidTableTypeException.cs b/GBase/Exceptions/InvalidTableTypeException.cs new file mode 100644 index 0000000..9be6013 --- /dev/null +++ b/GBase/Exceptions/InvalidTableTypeException.cs @@ -0,0 +1,27 @@ +// Author: Simon Gockner +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; +using GBase.Api; + +namespace GBase.Exceptions +{ + /// + /// that the passed table type doesn't implement "/> + /// + public class InvalidTableTypeException : Exception + { + /// + /// that the passed table type doesn't implement "/> + /// + /// The table type + public InvalidTableTypeException(Type type) + : base($"Table type ({type}) doesn't implement {nameof(INotifyGBaseEntryChanged)}.") + { + Type = type; + } + + public Type Type { get; } + } +} \ No newline at end of file diff --git a/GBase/Exceptions/MissingTableException.cs b/GBase/Exceptions/MissingTableException.cs new file mode 100644 index 0000000..78af97c --- /dev/null +++ b/GBase/Exceptions/MissingTableException.cs @@ -0,0 +1,23 @@ +// Author: Gockner, Simon +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; + +namespace GBase.Exceptions +{ + /// + /// that the table for the given is missing + /// + public class MissingTableException : Exception + { + /// + /// that the table for the given is missing + /// + public MissingTableException() + : base($"There is no table for type {typeof(T)}.") + { + + } + } +} \ No newline at end of file diff --git a/GBase/Factories/GBaseTableFactory.cs b/GBase/Factories/GBaseTableFactory.cs new file mode 100644 index 0000000..d20772d --- /dev/null +++ b/GBase/Factories/GBaseTableFactory.cs @@ -0,0 +1,45 @@ +// Author: Simon Gockner +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; +using GBase.FileHandling.Factories; +using GBase.Interfaces; +using GBase.Interfaces.DataHandling.Pool; +using GBase.Interfaces.FileHandling; + +namespace GBase.Factories +{ + /// + /// Factory for the + /// + public class GBaseTableFactory : IGBaseTableFactory + { + private readonly IFileHandlerFactory _fileHandlerFactory; + private readonly IDataHandlerPool _dataHandlerPool; + private readonly IGBaseColumnFactory _gBaseColumnFactory; + + /// + /// Factory for the + /// + /// Factory for the + /// + /// Factory for the + public GBaseTableFactory(IFileHandlerFactory fileHandlerFactory, IDataHandlerPool dataHandlerPool, IGBaseColumnFactory gBaseColumnFactory) + { + _fileHandlerFactory = fileHandlerFactory; + _dataHandlerPool = dataHandlerPool; + _gBaseColumnFactory = gBaseColumnFactory; + } + + /// + /// Creates an + /// + /// A newly created instance of the implementation for + public IGBaseTable Create(Type type) + { + Type gBaseTableType = typeof(GBaseTable<>).MakeGenericType(type); + return (IGBaseTable) Activator.CreateInstance(gBaseTableType, _fileHandlerFactory, _dataHandlerPool, _gBaseColumnFactory); + } + } +} \ No newline at end of file diff --git a/GBase/Factories/IGBaseColumnFactory.cs b/GBase/Factories/IGBaseColumnFactory.cs index 46f5b4c..7bd39ed 100644 --- a/GBase/Factories/IGBaseColumnFactory.cs +++ b/GBase/Factories/IGBaseColumnFactory.cs @@ -16,6 +16,6 @@ namespace GBase.Factories /// Creates an /// /// A newly created instance of the implementation for - IGBaseColumn Create(); + IGBaseColumn Create(string name); } } \ No newline at end of file diff --git a/GBase/Factories/IGBaseTableFactory.cs b/GBase/Factories/IGBaseTableFactory.cs index 43792c0..11c8554 100644 --- a/GBase/Factories/IGBaseTableFactory.cs +++ b/GBase/Factories/IGBaseTableFactory.cs @@ -3,6 +3,7 @@ // Copyright(c) 2020 SimonG. All Rights Reserved. +using System; using GBase.Interfaces; namespace GBase.Factories @@ -16,6 +17,6 @@ namespace GBase.Factories /// Creates an /// /// A newly created instance of the implementation for - IGBaseTable Create(); + IGBaseTable Create(Type type); } } \ No newline at end of file diff --git a/GBase/FileHandling/Exceptions/FileNotExistingException.cs b/GBase/FileHandling/Exceptions/FileNotExistingException.cs new file mode 100644 index 0000000..2981ec1 --- /dev/null +++ b/GBase/FileHandling/Exceptions/FileNotExistingException.cs @@ -0,0 +1,17 @@ +// Author: Gockner, Simon +// Created: 2020-11-10 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; + +namespace GBase.FileHandling.Exceptions +{ + public class FileNotExistingException : Exception + { + public FileNotExistingException() + : base($"File for the given type {typeof(T)} is not existing.") + { + + } + } +} \ No newline at end of file diff --git a/GBase/FileHandling/FileHandler.cs b/GBase/FileHandling/FileHandler.cs index 468fe12..4e1d396 100644 --- a/GBase/FileHandling/FileHandler.cs +++ b/GBase/FileHandling/FileHandler.cs @@ -2,13 +2,13 @@ // Created: 2020-02-12 // Copyright(c) 2020 SimonG. All Rights Reserved. -using System; using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using GBase.DataHandling.Factories; -using GBase.Interfaces.DataHandling; -using GBase.Interfaces.DataHandling.Xml; +using GBase.FileHandling.Exceptions; +using GBase.Interfaces; using GBase.Interfaces.FileHandling; namespace GBase.FileHandling @@ -18,22 +18,22 @@ namespace GBase.FileHandling /// public class FileHandler : IFileHandler { - private readonly IXmlDataHandlerFactory _dataHandlerFactory; - + /// + /// The file extension for all GBase tables + /// + public const string GBASE_TABLE_FILE_EXTENSION = "gb"; + + private readonly List _files; + private string _path; + /// /// Internal file handler /// - /// Factory for the - public FileHandler(IXmlDataHandlerFactory xmlDataHandlerFactory) + public FileHandler() { - _dataHandlerFactory = xmlDataHandlerFactory; + _files = new List(); } - /// - /// The of this - /// - private IDataHandler DataHandler { get; set; } - /// /// Initialize this /// @@ -42,60 +42,48 @@ namespace GBase.FileHandling /// True if successful, false if not public async Task Init(string path, CancellationToken cancellationToken) { - DataHandler = _dataHandlerFactory.Create(); - - bool success = await DataHandler.Init(false, cancellationToken); - return success; + _path = path; + return true; //TODO: initialize existing files } - /// - /// Set the value for the given property - /// - /// The of the property - /// The of the property - /// The name of the property - /// The value to set - /// A to await - public async Task SetValue(string propertyName, TProperty value) + public IGBaseFile CreateEntryFile(T entry, IGBaseTable table) { - await DataHandler.SetValue(propertyName, value); - } + string directoryPath = Path.Combine(_path, table.FolderName); + + //create directory if it doesn't exist + Directory.CreateDirectory(directoryPath); + + //create new entry file + string filePath = $"{Path.Combine(directoryPath, entry.ToString())}.{GBASE_TABLE_FILE_EXTENSION}"; + FileStream entryFile = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite); //TODO: Stream has to be disposed + + IGBaseFile file = new GBaseFile(entry, entryFile, filePath); + _files.Add(file); - /// - /// Remove the value for the given property - /// - /// The of the property - /// The of the property - /// The name of the property - /// The value to set - /// A to await - public async Task RemoveValue(string propertyName, TProperty value) - { - await DataHandler.RemoveValue(propertyName, value); + return file.UseFile(); } - /// - /// Get the value for the given property, if multiple values are set the first is returned - /// - /// The of the property - /// The of the property - /// The name of the property - /// The value for the given property - public async Task GetValue(string propertyName) + public async Task DeleteEntryFile(T entry) { - return await DataHandler.GetValue(propertyName); + IGBaseFile file = _files.FirstOrDefault(f => f.Entry.Equals(entry)); + if (file == default) + return false; + + await file.File.DisposeAsync(); + File.Delete(file.FilePath); + return true; } - /// - /// Get all the values that are set for the given property - /// - /// The of the property - /// The of the property - /// The name of the property - /// An with all the values for the property - public async Task> GetValues(string propertyName) + public async Task RequestEntryFile(T entry) { - return await DataHandler.GetValues(propertyName); + IGBaseFile file = _files.FirstOrDefault(f => f.Entry.Equals(entry)); + if (file == default) + throw new FileNotExistingException(); + + if (file.InUse) + await Task.Delay(1000); + + return file.UseFile(); } /// @@ -104,7 +92,15 @@ namespace GBase.FileHandling /// A to await public async ValueTask DisposeAsync() { - await DataHandler.DisposeAsync(); + while (_files.Any(f => f.InUse)) + { + await Task.Delay(1000); + } + + foreach (var file in _files) + { + await file.File.DisposeAsync(); + } } } } \ No newline at end of file diff --git a/GBase/FileHandling/GBaseFile.cs b/GBase/FileHandling/GBaseFile.cs new file mode 100644 index 0000000..aaf0f56 --- /dev/null +++ b/GBase/FileHandling/GBaseFile.cs @@ -0,0 +1,32 @@ +// Author: Gockner, Simon +// Created: 2020-11-10 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System.IO; +using GBase.Interfaces.FileHandling; + +namespace GBase.FileHandling +{ + public class GBaseFile : IGBaseFile + { + public GBaseFile(object entry, FileStream file, string filePath) + { + Entry = entry; + File = file; + FilePath = filePath; + } + + public object Entry { get; } + public FileStream File { get; } + public string FilePath { get; } + public bool InUse { get; private set; } + + public IGBaseFile UseFile() + { + InUse = true; + return this; + } + + public void Dispose() => InUse = false; + } +} \ No newline at end of file diff --git a/GBase/GBase.cs b/GBase/GBase.cs index 0bf717e..eba244f 100644 --- a/GBase/GBase.cs +++ b/GBase/GBase.cs @@ -9,6 +9,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; using GBase.Attributes; +using GBase.Exceptions; using GBase.Factories; using GBase.Interfaces; using GBase.Interfaces.Settings; @@ -20,11 +21,6 @@ namespace GBase /// public class GBase : IGBase { - /// - /// The file extension for all GBase tables - /// - public const string GBASE_TABLE_FILE_EXTENSION = "gb"; //TODO: Find correct place for this const - private readonly IGBaseTableFactory _gBaseTableFactory; /// @@ -74,8 +70,8 @@ namespace GBase if (gBaseTableAttribute == null) continue; - IGBaseTable gBaseTable = _gBaseTableFactory.Create(); - await gBaseTable.Init(type, type.Name, Settings.DatabasePath, cancellationToken); + IGBaseTable gBaseTable = _gBaseTableFactory.Create(type); + await gBaseTable.Init(type, Settings.DatabasePath, gBaseTableAttribute.FolderName, cancellationToken); AddTable(gBaseTable); } @@ -93,13 +89,23 @@ namespace GBase if (Tables.Contains(table)) return false; - if (Tables.Any(t => t.Name.Equals(table.Name))) + if (Tables.Any(t => t.FolderName.Equals(table.FolderName))) return false; Tables.Add(table); return true; } + public IGBaseTable GetTable() + { + if (Tables.OfType>().Any()) + return Tables.OfType>().First(); + + throw new MissingTableException(); + // //TODO: This probably doesn't work, because even though t.Type : T, IGBaseTable !: IGBaseTable + // return (IGBaseTable) Convert.ChangeType(Tables.FirstOrDefault(t => typeof(T).IsAssignableFrom(t.Type)), typeof(IGBaseTable)); //TestMe + } + /// /// Removes a given from this /// @@ -113,6 +119,43 @@ namespace GBase return Tables.Remove(table); } + public async Task AddEntry(T entry, CancellationToken cancellationToken) + { + IGBaseTable table = GetTable(); + if (table == null) + throw new MissingTableException(); + + return await table.AddEntry(entry, cancellationToken); + } + + public async Task SetValue(T entry, string propertyName, TProperty value, CancellationToken cancellationToken) + { + IGBaseTable table = GetTable(); + if (table == null) + throw new MissingTableException(); + + await table.SetValue(entry, propertyName, value, cancellationToken); + } + + public async Task GetValue(T entry, string propertyName, CancellationToken cancellationToken) + { + IGBaseTable table = GetTable(); + if (table == null) + throw new MissingTableException(); + + return await table.GetValue(entry, propertyName, cancellationToken); + } + + public async Task> GetValues(T entry, string propertyName, CancellationToken cancellationToken) + { + IGBaseTable table = GetTable(); + if (table == null) + throw new MissingTableException(); + + return await table.GetValues(entry, propertyName, cancellationToken); + } + + /// /// Dispose used resources asynchronously /// diff --git a/GBase/GBase.xml b/GBase/GBase.xml index 2945813..d9641b4 100644 --- a/GBase/GBase.xml +++ b/GBase/GBase.xml @@ -87,13 +87,15 @@ The value to set A to await - + Try to get values from the cache for the given property The that implements the property The of the property + The name of the property + An with all the values for the property @@ -163,11 +165,10 @@ Factory for the - + Creates an - The path to the xml file A newly created instance of the implementation for @@ -175,12 +176,10 @@ Factory for the - + Creates an - The path to the xml file - The root element name of the xml file A newly created instance of the implementation for @@ -198,152 +197,142 @@ The attribute name of the value attribute - + A that handles its data in an xml file The path to the xml file - The root element name of the xml file The factory The factory The factory - + Initialize the If true an existing value is overwritten, if false an additional value is added - A to cancel the async operation Returns true if successful, false if not - + Set the value for the given property The that implements the property The of the property + The name of the property The value to set + A to await - + Remove the value for the given property The of the property The of the property + The name of the property The value to set + A to await - + Get the value for the given property, if multiple values are set the first is returned The that implements the property The of the property + The name of the property + The value for the given property - + Get all the values that are set for the given property The that implements the property The of the property + The name of the property + An with all the values for the property - - - Dispose used resources asynchronously - - A to await - A that reads from a xml file - - - A that reads from a xml file - - The path to the xml file - - - - Initialize the - - A to cancel the async operation - Returns true if successful, false if not - No root element found - - + Read the data of a property The The of the property + The name of the property + The data of the given property, null if no data found Invalid found for the read object - - - Dispose used resources asynchronously - - A to await - A that writes to a xml file - - - A that writes to an xml file - - The path to the xml file - The root element name of the xml file - - + Initialize the + + A to cancel the async operation Returns true if successful, false if not No root element found - + Write the data of a property - The that implements the property + The The of the property + The name of the property The value of the property If true an existing value is overwritten, if false an additional value is added + + A to await + + + + Write the data of a property + + The that implements the property + + The name of the property + The value of the property + + If true an existing value is overwritten, if false an additional value is added + A to await + No root element is set - + Remove the value for the given property The of the property The of the property + The name of the property The value to set + A to await - - - - Dispose used resources asynchronously - - A to await + No root element is set @@ -361,12 +350,52 @@ The of the passed interface + + + that the passed table type doesn't implement "/> + + + + + that the passed table type doesn't implement "/> + + The table type + + + + that the table for the given is missing + + + + + that the table for the given is missing + + + + + Factory for the + + + + + Factory for the + + Factory for the + + Factory for the + + + + Creates an + + A newly created instance of the implementation for + Factory for the - + Creates an @@ -389,7 +418,7 @@ Factory for the - + Creates an @@ -411,15 +440,14 @@ Internal file handler - + - Internal file handler + The file extension for all GBase tables - Factory for the - + - The of this + Internal file handler @@ -430,44 +458,6 @@ A to cancel the asynchronous operation True if successful, false if not - - - Set the value for the given property - - The of the property - The of the property - The name of the property - The value to set - A to await - - - - Remove the value for the given property - - The of the property - The of the property - The name of the property - The value to set - A to await - - - - Get the value for the given property, if multiple values are set the first is returned - - The of the property - The of the property - The name of the property - The value for the given property - - - - Get all the values that are set for the given property - - The of the property - The of the property - The name of the property - An with all the values for the property - Dispose used resources asynchronously @@ -479,11 +469,6 @@ The base class of the GBase database - - - The file extension for all GBase tables - - The base class of the GBase database @@ -540,7 +525,7 @@ A column of a - + A column of a @@ -551,91 +536,96 @@ A to await - + + + GBase object that supplies inheriting classes with methods to get data from a + + + + + GBase object that allows conversion from + + No table for is existing + + + + Initialize this from a given + + The given + + A table - + A table + + The + - + The of the class that this represents - + The name of this - + The s of this - + The entries of this - + Initialize this The of the class that this represents - The name of this The path to the database files + A to cancel the asynchronous operation True if successful, false if not - + Add a given to this The given True if successful, false if not - + Remove a given from this The given True if successful, false if not - + Add an entry that implements to this The entry implementing + True if successful, false if not - + Remove an entry that implements from this The entry implementing True if successful, false if not - - - Modify the property of a given entry with the given value - - The entry - The name of the property - The new value to set - True if successful, false if not - - - - A entry changed - - The entry (sender) - The - - + The method @@ -707,13 +697,15 @@ The value to set A to await - + Try to get values from the cache for the given property The that implements the property The of the property + The name of the property + An with all the values for the property @@ -756,50 +748,57 @@ Interface for data handlers to implement - + Initialize the If true an existing value is overwritten, if false an additional value is added - A to cancel the async operation Returns true if successful, false if not - + Set the value for the given property The of the property The of the property + The name of the property The value to set + A to await - + Remove the value for the given property The of the property The of the property + The name of the property The value to set + A to await - + Get the value for the given property, if multiple values are set the first is returned The of the property The of the property + The name of the property + The value for the given property - + Get all the values that are set for the given property The of the property The of the property + The name of the property + An with all the values for the property @@ -807,20 +806,15 @@ Interface for data readers to implement - - - Initialize the - - A to cancel the async operation - Returns true if successful, false if not - - + Read the data of a property The The of the property + The name of the property + The data of the given property, null if no data found @@ -828,32 +822,51 @@ Interface for data writers to implement - + Initialize the + + A to cancel the async operation Returns true if successful, false if not - + Write the data of a property The The of the property + The name of the property The value of the property If true an existing value is overwritten, if false an additional value is added + A to await - + + + Write the data of a property + + The + + The name of the property + The value of the property + + If true an existing value is overwritten, if false an additional value is added + + A to await + + Remove the value for the given property The of the property The of the property + The name of the property The value to set + A to await @@ -899,44 +912,6 @@ A to cancel the asynchronous operation True if successful, false if not - - - Set the value for the given property - - The of the property - The of the property - The name of the property - The value to set - A to await - - - - Remove the value for the given property - - The of the property - The of the property - The name of the property - The value to set - A to await - - - - Get the value for the given property, if multiple values are set the first is returned - - The of the property - The of the property - The name of the property - The value for the given property - - - - Get all the values that are set for the given property - - The of the property - The of the property - The name of the property - An with all the values for the property - The base class of the GBase database @@ -985,6 +960,26 @@ A column of a + + + The entries of this + + + + + Add an entry that implements to this + + The entry implementing + + True if successful, false if not + + + + Remove an entry that implements from this + + The entry implementing + True if successful, false if not + A table @@ -995,7 +990,7 @@ The of the class that this represents - + The name of this @@ -1005,19 +1000,15 @@ The s of this - - - The entries of this - - Initialize this The of the class that this represents - The name of this - /// The path to the database files + The path to the database files + A to cancel the asynchronous operation + /// True if successful, false if not @@ -1034,20 +1025,6 @@ The given True if successful, false if not - - - Add an entry that implements to this - - The entry implementing - True if successful, false if not - - - - Remove an entry that implements from this - - The entry implementing - True if successful, false if not - Settings of a instance diff --git a/GBase/GBaseColumn.cs b/GBase/GBaseColumn.cs index ee3f45b..25d2da2 100644 --- a/GBase/GBaseColumn.cs +++ b/GBase/GBaseColumn.cs @@ -16,10 +16,12 @@ namespace GBase /// /// A column of a /// - public GBaseColumn() + public GBaseColumn(string name) { - + Name = name; } + + public string Name { get; } /// /// The method diff --git a/GBase/GBaseObject.cs b/GBase/GBaseObject.cs new file mode 100644 index 0000000..2711bed --- /dev/null +++ b/GBase/GBaseObject.cs @@ -0,0 +1,47 @@ +// Author: Gockner, Simon +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GBase.Api; +using GBase.Exceptions; +using GBase.Interfaces; + +namespace GBase +{ + /// + /// GBase object that supplies inheriting classes with methods to get data from a + /// + public abstract class GBaseObject : IGBaseObject + { + private readonly IGBaseTable _gBaseTable; + + /// + /// GBase object that allows conversion from + /// + /// No table for is existing + protected GBaseObject(IGBase gBase) + { + _gBaseTable = gBase.GetTable(); + if (_gBaseTable == null) + throw new MissingTableException(); + } + + protected async Task SetValue(T @this, string propertyName, TProperty value, CancellationToken cancellationToken) => + await _gBaseTable.SetValue(@this, propertyName, value, cancellationToken); + + protected async Task GetValue(T @this, string propertyName, CancellationToken cancellationToken) => + await _gBaseTable.GetValue(@this, propertyName, cancellationToken); + + protected async Task> GetValues(T @this, string propertyName, CancellationToken cancellationToken) => + await _gBaseTable.GetValues(@this, propertyName, cancellationToken); + + /// + /// Initialize this from a given + /// + /// The given + public abstract void InitializeFromString(string @string); + } +} \ No newline at end of file diff --git a/GBase/GBaseTable.cs b/GBase/GBaseTable.cs index 887f8d8..644e0fe 100644 --- a/GBase/GBaseTable.cs +++ b/GBase/GBaseTable.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; using System.Threading; @@ -14,6 +13,8 @@ using GBase.Attributes; using GBase.Factories; using GBase.FileHandling.Factories; using GBase.Interfaces; +using GBase.Interfaces.DataHandling; +using GBase.Interfaces.DataHandling.Pool; using GBase.Interfaces.FileHandling; namespace GBase @@ -21,21 +22,26 @@ namespace GBase /// /// A table /// - public class GBaseTable : IGBaseTable + public class GBaseTable : IGBaseTable { private readonly IFileHandler _fileHandler; + private readonly IDataHandlerPool _dataHandlerPool; private readonly IGBaseColumnFactory _gBaseColumnFactory; /// /// A table /// - public GBaseTable(IFileHandlerFactory fileHandlerFactory, IGBaseColumnFactory gBaseColumnFactory) + /// + /// The + /// + public GBaseTable(IFileHandlerFactory fileHandlerFactory, IDataHandlerPool dataHandlerPool, IGBaseColumnFactory gBaseColumnFactory) { _fileHandler = fileHandlerFactory.Create(); + _dataHandlerPool = dataHandlerPool; _gBaseColumnFactory = gBaseColumnFactory; Columns = new List(); - Entries = new List(); + Entries = new List(); } @@ -47,7 +53,7 @@ namespace GBase /// /// The name of this /// - public string Name { get; private set; } + public string FolderName { get; private set; } /// /// The s of this @@ -57,25 +63,22 @@ namespace GBase /// /// The entries of this /// - public List Entries { get; } + public List Entries { get; } /// /// Initialize this /// /// The of the class that this represents - /// The name of this /// The path to the database files + /// /// A to cancel the asynchronous operation /// True if successful, false if not - public async Task Init(Type type, string name, string databasePath, CancellationToken cancellationToken) + public async Task Init(Type type, string databasePath, string folderName, CancellationToken cancellationToken) { Type = type; - Name = name; - - string fileName = $"{name}.{GBase.GBASE_TABLE_FILE_EXTENSION}"; - string path = Path.Combine(databasePath, fileName); - await _fileHandler.Init(path, cancellationToken); + FolderName = folderName; + await _fileHandler.Init(databasePath, cancellationToken); //TODO: Init columns list depending on GBaseColumnAttributes set for this GBaseTable foreach (var property in type.GetProperties()) @@ -84,7 +87,7 @@ namespace GBase if (gBaseColumnAttribute == null) continue; - IGBaseColumn gBaseColumn = _gBaseColumnFactory.Create(); + IGBaseColumn gBaseColumn = _gBaseColumnFactory.Create(property.Name); AddColumn(gBaseColumn); } @@ -96,7 +99,7 @@ namespace GBase /// /// The given /// True if successful, false if not - public bool AddColumn(IGBaseColumn column) + public bool AddColumn(IGBaseColumn column) //TODO: if a column is added, it has to be added to every entry file as well { if (Columns.Contains(column)) return false; @@ -110,7 +113,7 @@ namespace GBase /// /// The given /// True if successful, false if not - public bool RemoveColumn(IGBaseColumn column) + public bool RemoveColumn(IGBaseColumn column) //TODO: if a column is removed, it has to be removed from every entry file as well { if (!Columns.Contains(column)) return false; @@ -122,11 +125,15 @@ namespace GBase /// Add an entry that implements to this /// /// The entry implementing + /// /// True if successful, false if not - public bool AddEntry(INotifyGBaseEntryChanged entry) //TODO: Write to file + public async Task AddEntry(T entry, CancellationToken cancellationToken) { Entries.Add(entry); - entry.GBaseEntryChanged += OnGBaseEntryChanged; + + using IGBaseFile file = _fileHandler.CreateEntryFile(entry, this); + IDataHandler dataHandler = await _dataHandlerPool.RequestDataHandler(this, false, cancellationToken); + await dataHandler.AddEntry(entry, this, file.File, cancellationToken); return true; } @@ -136,41 +143,35 @@ namespace GBase /// /// The entry implementing /// True if successful, false if not - public bool RemoveEntry(INotifyGBaseEntryChanged entry) //TODO: remove from file + public async Task RemoveEntry(T entry) { if (!Entries.Contains(entry)) return false; - entry.GBaseEntryChanged -= OnGBaseEntryChanged; + await _fileHandler.DeleteEntryFile(entry); return Entries.Remove(entry); } - /// - /// Modify the property of a given entry with the given value - /// - /// The entry - /// The name of the property - /// The new value to set - /// True if successful, false if not - private bool ModifyEntry(object entry, string propertyName, object value) //TODO: Write to file + public async Task SetValue(T entry, string propertyName, TProperty value, CancellationToken cancellationToken) { - //don't need to change value of property in `Entries` list, the instance is saved there, any property change is already changed there as well - return true; + using IGBaseFile file = await _fileHandler.RequestEntryFile(entry); + IDataHandler dataHandler = await _dataHandlerPool.RequestDataHandler(this, false, CancellationToken.None); + await dataHandler.SetValue(file.File, propertyName, value, cancellationToken); } - /// - /// A entry changed - /// - /// The entry (sender) - /// The - private void OnGBaseEntryChanged(object entry, GBaseEntryChangedEventArgs args) + public async Task GetValue(T entry, string propertyName, CancellationToken cancellationToken) { - //TODO: Might change, depending on #23 - bool success = ModifyEntry(entry, args.ColumnName, args.Value); - - if (!success) - throw new Exception("Failed to handle EntryChanged"); //TODO: Decide what to do here + using IGBaseFile file = await _fileHandler.RequestEntryFile(entry); + IDataHandler dataHandler = await _dataHandlerPool.RequestDataHandler(this, false, CancellationToken.None); + return await dataHandler.GetValue(file.File, propertyName, cancellationToken); + } + + public async Task> GetValues(T entry, string propertyName, CancellationToken cancellationToken) + { + using IGBaseFile file = await _fileHandler.RequestEntryFile(entry); + IDataHandler dataHandler = await _dataHandlerPool.RequestDataHandler(this, false, CancellationToken.None); + return await dataHandler.GetValues(file.File, propertyName, cancellationToken); } /// @@ -180,6 +181,7 @@ namespace GBase public async ValueTask DisposeAsync() { await _fileHandler.DisposeAsync(); + await _dataHandlerPool.DisposeAsync(); foreach (var column in Columns) { @@ -188,9 +190,9 @@ namespace GBase Columns.Clear(); - foreach (var entry in Entries.OfType().ToList()) + foreach (var entry in Entries.ToList()) { - RemoveEntry(entry); + await RemoveEntry(entry); } Entries.Clear(); diff --git a/GBase/Installers/DataHandlingInstaller.cs b/GBase/Installers/DataHandlingInstaller.cs index cd523e5..134ccee 100644 --- a/GBase/Installers/DataHandlingInstaller.cs +++ b/GBase/Installers/DataHandlingInstaller.cs @@ -7,8 +7,13 @@ using GBase.DataHandling; using GBase.DataHandling.Cache; using GBase.DataHandling.Cache.Factories; using GBase.DataHandling.Factories; +using GBase.DataHandling.Factories.Pool; +using GBase.DataHandling.Pool; +using GBase.Interfaces.DataHandling; +using GBase.Interfaces.DataHandling.Pool; using GBase.Interfaces.DataHandling.Xml; using GBase.Interfaces.DataHandling.Xml.Cache; +using LightweightIocContainer; using LightweightIocContainer.Interfaces; using LightweightIocContainer.Interfaces.Installers; @@ -22,16 +27,22 @@ namespace GBase.Installers /// public void Install(IIocContainer container) { - container.Register(); - container.Register(); - container.Register(); + container.Register(); + container.Register(); + container.Register(); //cache container.Register(); container.Register(); container.Register(); + //pool + container.Register(Lifestyle.Singleton).WithParameters(10); + container.Register(); + container.RegisterFactory(); + //factories + container.RegisterFactory(); container.RegisterFactory(); container.RegisterFactory(); container.RegisterFactory(); diff --git a/GBase/Installers/GBaseInstaller.cs b/GBase/Installers/GBaseInstaller.cs index 9ace16d..902e7cd 100644 --- a/GBase/Installers/GBaseInstaller.cs +++ b/GBase/Installers/GBaseInstaller.cs @@ -19,12 +19,11 @@ namespace GBase.Installers public void Install(IIocContainer container) { container.Register(); - container.Register(); container.Register(); //factories container.RegisterFactory(); - container.RegisterFactory(); + container.Register(); //TODO: Once LightweightIocContainer can handle open generic registrations this can be changed back again container.RegisterFactory(); } } diff --git a/GBase/Interfaces/DataHandling/Cache/IDataHandlerCache.cs b/GBase/Interfaces/DataHandling/Cache/IDataHandlerCache.cs index e02a452..9bcba60 100644 --- a/GBase/Interfaces/DataHandling/Cache/IDataHandlerCache.cs +++ b/GBase/Interfaces/DataHandling/Cache/IDataHandlerCache.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Threading; using System.Threading.Tasks; namespace GBase.Interfaces.DataHandling.Cache @@ -39,8 +41,10 @@ namespace GBase.Interfaces.DataHandling.Cache /// /// The that implements the property /// The of the property + /// /// The name of the property + /// /// An with all the values for the property - Task> TryGetValues(string propertyName); + Task> TryGetValues(FileStream file, string propertyName, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/GBase/Interfaces/DataHandling/IDataHandler.cs b/GBase/Interfaces/DataHandling/IDataHandler.cs index cc600be..afab835 100644 --- a/GBase/Interfaces/DataHandling/IDataHandler.cs +++ b/GBase/Interfaces/DataHandling/IDataHandler.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading; using System.Threading.Tasks; @@ -12,52 +13,63 @@ namespace GBase.Interfaces.DataHandling /// /// Interface for data handlers to implement /// - public interface IDataHandler : IAsyncDisposable + public interface IDataHandler { /// /// Initialize the /// /// If true an existing value is overwritten, if false an additional value is added - /// A to cancel the async operation /// Returns true if successful, false if not - Task Init(bool overwrite, CancellationToken cancellationToken); + bool Init(bool overwrite); + + Task AddEntry(T entry, IGBaseTable table, FileStream entryFile, CancellationToken cancellationToken); + + Task RemoveEntry(T entry); /// /// Set the value for the given property /// /// The of the property /// The of the property + /// /// The name of the property /// The value to set + /// /// A to await - Task SetValue(string propertyName, TProperty value); + Task SetValue(FileStream entryFile, string propertyName, TProperty value, CancellationToken cancellationToken); /// /// Remove the value for the given property /// /// The of the property /// The of the property + /// /// The name of the property /// The value to set + /// /// A to await - Task RemoveValue(string propertyName, TProperty value); + Task RemoveValue(FileStream entryFile, string propertyName, TProperty value, CancellationToken cancellationToken); /// /// Get the value for the given property, if multiple values are set the first is returned /// /// The of the property /// The of the property + /// /// The name of the property + /// /// The value for the given property - Task GetValue(string propertyName); + Task GetValue(FileStream file, string propertyName, CancellationToken cancellationToken); /// /// Get all the values that are set for the given property /// /// The of the property /// The of the property + /// /// The name of the property + /// /// An with all the values for the property - Task> GetValues(string propertyName); + Task> GetValues(FileStream file, string propertyName, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/GBase/Interfaces/DataHandling/IDataReader.cs b/GBase/Interfaces/DataHandling/IDataReader.cs index e6d34da..cacdfb5 100644 --- a/GBase/Interfaces/DataHandling/IDataReader.cs +++ b/GBase/Interfaces/DataHandling/IDataReader.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading; using System.Threading.Tasks; @@ -12,22 +13,17 @@ namespace GBase.Interfaces.DataHandling /// /// Interface for data readers to implement /// - public interface IDataReader : IAsyncDisposable + public interface IDataReader { - /// - /// Initialize the - /// - /// A to cancel the async operation - /// Returns true if successful, false if not - Task Init(CancellationToken cancellationToken); - /// /// Read the data of a property /// /// The /// The of the property + /// /// The name of the property + /// /// The data of the given property, null if no data found - Task> Read(string propertyName); + Task> Read(FileStream file, string propertyName, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/GBase/Interfaces/DataHandling/IDataWriter.cs b/GBase/Interfaces/DataHandling/IDataWriter.cs index c049b66..98ffef4 100644 --- a/GBase/Interfaces/DataHandling/IDataWriter.cs +++ b/GBase/Interfaces/DataHandling/IDataWriter.cs @@ -3,6 +3,7 @@ // Copyright(c) 2020 SimonG. All Rights Reserved. using System; +using System.IO; using System.Threading; using System.Threading.Tasks; @@ -11,34 +12,53 @@ namespace GBase.Interfaces.DataHandling /// /// Interface for data writers to implement /// - public interface IDataWriter : IAsyncDisposable + public interface IDataWriter { /// /// Initialize the /// + /// + /// /// A to cancel the async operation /// Returns true if successful, false if not - Task Init(CancellationToken cancellationToken); + Task InitFile(FileStream file, string rootElementName, CancellationToken cancellationToken); /// /// Write the data of a property /// /// The /// The of the property + /// /// The name of the property /// The value of the property /// If true an existing value is overwritten, if false an additional value is added + /// /// A to await - Task Write(string propertyName, string value, bool overwrite); + Task Write(FileStream file, string propertyName, string value, bool overwrite, CancellationToken cancellationToken); + + /// + /// Write the data of a property + /// + /// The + /// + /// The name of the property + /// The value of the property + /// + /// If true an existing value is overwritten, if false an additional value is added + /// + /// A to await + Task Write(FileStream file, string propertyName, string value, Type propertyType, bool overwrite, CancellationToken cancellationToken); /// /// Remove the value for the given property /// /// The of the property /// The of the property + /// /// The name of the property /// The value to set + /// /// A to await - Task Remove(string propertyName, string value); + Task Remove(FileStream file, string propertyName, string value, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/GBase/Interfaces/DataHandling/Pool/IDataHandlerPool.cs b/GBase/Interfaces/DataHandling/Pool/IDataHandlerPool.cs new file mode 100644 index 0000000..ccece22 --- /dev/null +++ b/GBase/Interfaces/DataHandling/Pool/IDataHandlerPool.cs @@ -0,0 +1,15 @@ +// Author: Simon Gockner +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace GBase.Interfaces.DataHandling.Pool +{ + public interface IDataHandlerPool : IAsyncDisposable + { + Task RequestDataHandler(object requester, bool overwrite, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/GBase/Interfaces/DataHandling/Pool/IPoolItem.cs b/GBase/Interfaces/DataHandling/Pool/IPoolItem.cs new file mode 100644 index 0000000..8cdb3d9 --- /dev/null +++ b/GBase/Interfaces/DataHandling/Pool/IPoolItem.cs @@ -0,0 +1,12 @@ +// Author: Simon Gockner +// Created: 2020-09-19 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +namespace GBase.Interfaces.DataHandling.Pool +{ + public interface IPoolItem + { + T Item { get; } + bool InUse { get; set; } + } +} \ No newline at end of file diff --git a/GBase/Interfaces/DataHandling/Pool/IPoolRequest.cs b/GBase/Interfaces/DataHandling/Pool/IPoolRequest.cs new file mode 100644 index 0000000..6e36e93 --- /dev/null +++ b/GBase/Interfaces/DataHandling/Pool/IPoolRequest.cs @@ -0,0 +1,14 @@ +// Author: Simon Gockner +// Created: 2020-09-19 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; +using System.Threading.Tasks; + +namespace GBase.Interfaces.DataHandling.Pool +{ + public interface IPoolRequest + { + object Requester { get; } + } +} \ No newline at end of file diff --git a/GBase/Interfaces/FileHandling/IFileHandler.cs b/GBase/Interfaces/FileHandling/IFileHandler.cs index 9e33882..0e9ca47 100644 --- a/GBase/Interfaces/FileHandling/IFileHandler.cs +++ b/GBase/Interfaces/FileHandling/IFileHandler.cs @@ -3,7 +3,6 @@ // Copyright(c) 2020 SimonG. All Rights Reserved. using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -22,42 +21,10 @@ namespace GBase.Interfaces.FileHandling /// True if successful, false if not Task Init(string path, CancellationToken cancellationToken); - /// - /// Set the value for the given property - /// - /// The of the property - /// The of the property - /// The name of the property - /// The value to set - /// A to await - Task SetValue(string propertyName, TProperty value); + IGBaseFile CreateEntryFile(T entry, IGBaseTable table); - /// - /// Remove the value for the given property - /// - /// The of the property - /// The of the property - /// The name of the property - /// The value to set - /// A to await - Task RemoveValue(string propertyName, TProperty value); + Task DeleteEntryFile(T entry); - /// - /// Get the value for the given property, if multiple values are set the first is returned - /// - /// The of the property - /// The of the property - /// The name of the property - /// The value for the given property - Task GetValue(string propertyName); - - /// - /// Get all the values that are set for the given property - /// - /// The of the property - /// The of the property - /// The name of the property - /// An with all the values for the property - Task> GetValues(string propertyName); + Task RequestEntryFile(T entry); } } \ No newline at end of file diff --git a/GBase/Interfaces/FileHandling/IGBaseFile.cs b/GBase/Interfaces/FileHandling/IGBaseFile.cs new file mode 100644 index 0000000..9904130 --- /dev/null +++ b/GBase/Interfaces/FileHandling/IGBaseFile.cs @@ -0,0 +1,19 @@ +// Author: Gockner, Simon +// Created: 2020-11-10 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; +using System.IO; + +namespace GBase.Interfaces.FileHandling +{ + public interface IGBaseFile : IDisposable + { + object Entry { get; } + FileStream File { get; } + string FilePath { get; } + bool InUse { get; } + + IGBaseFile UseFile(); + } +} \ No newline at end of file diff --git a/GBase/Interfaces/IGBase.cs b/GBase/Interfaces/IGBase.cs index 614211a..74b571c 100644 --- a/GBase/Interfaces/IGBase.cs +++ b/GBase/Interfaces/IGBase.cs @@ -48,11 +48,19 @@ namespace GBase.Interfaces /// True if successful, false if not bool AddTable(IGBaseTable table); + IGBaseTable GetTable(); + /// /// Removes a given from this /// /// The given /// True if successful, false if not bool RemoveTable(IGBaseTable table); + + Task AddEntry(T entry, CancellationToken cancellationToken); + + Task SetValue(T entry, string propertyName, TProperty value, CancellationToken cancellationToken); + Task GetValue(T entry, string propertyName, CancellationToken cancellationToken); + Task> GetValues(T entry, string propertyName, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/GBase/Interfaces/IGBaseColumn.cs b/GBase/Interfaces/IGBaseColumn.cs index d78a691..59cd728 100644 --- a/GBase/Interfaces/IGBaseColumn.cs +++ b/GBase/Interfaces/IGBaseColumn.cs @@ -11,6 +11,6 @@ namespace GBase.Interfaces /// public interface IGBaseColumn : IAsyncDisposable //TODO: Make column generic (generic type is type of the value of the column?)? { - + string Name { get; } } } \ No newline at end of file diff --git a/GBase/Interfaces/IGBaseTable.cs b/GBase/Interfaces/IGBaseTable.cs index 613c0af..277c1c5 100644 --- a/GBase/Interfaces/IGBaseTable.cs +++ b/GBase/Interfaces/IGBaseTable.cs @@ -10,6 +10,38 @@ using GBase.Api; namespace GBase.Interfaces { + public interface IGBaseTable : IGBaseTable + { + /// + /// The entries of this + /// + List Entries { get; } + + /// + /// Add an entry that implements to this + /// + /// The entry implementing + /// + /// True if successful, false if not + Task AddEntry(T entry, CancellationToken cancellationToken); + + + //T GetEntry(T entry); //TODO: This doesn't make sense... (passing instance of T to get the same instance back...) + + /// + /// Remove an entry that implements from this + /// + /// The entry implementing + /// True if successful, false if not + Task RemoveEntry(T entry); + + Task SetValue(T entry, string propertyName, TProperty value, CancellationToken cancellationToken); + + Task GetValue(T entry, string propertyName, CancellationToken cancellationToken); + + Task> GetValues(T entry, string propertyName, CancellationToken cancellationToken); + } + /// /// A table /// @@ -23,27 +55,23 @@ namespace GBase.Interfaces /// /// The name of this /// - string Name { get; } + string FolderName { get; } /// /// The s of this /// List Columns { get; } - /// - /// The entries of this - /// - List Entries { get; } - /// /// Initialize this /// /// The of the class that this represents - /// The name of this - /// /// The path to the database files + /// The path to the database files + /// /// A to cancel the asynchronous operation + /// /// /// True if successful, false if not - Task Init(Type type, string name, string databasePath, CancellationToken cancellationToken); + Task Init(Type type, string databasePath, string folderName, CancellationToken cancellationToken); /// /// Add a given to this @@ -58,19 +86,5 @@ namespace GBase.Interfaces /// The given /// True if successful, false if not bool RemoveColumn(IGBaseColumn column); - - /// - /// Add an entry that implements to this - /// - /// The entry implementing - /// True if successful, false if not - bool AddEntry(INotifyGBaseEntryChanged entry); - - /// - /// Remove an entry that implements from this - /// - /// The entry implementing - /// True if successful, false if not - bool RemoveEntry(INotifyGBaseEntryChanged entry); } } \ No newline at end of file diff --git a/GBaseStructure.md b/GBaseStructure.md new file mode 100644 index 0000000..14e8c1b --- /dev/null +++ b/GBaseStructure.md @@ -0,0 +1,41 @@ +# GBase Structure + +```md +- GBase + | + +-GBaseTable + | + | + +-GBaseTable + | + +-GBaseColumn--GBaseColumn--GBaseColumn + +-GBaseEntry + +-GBaseEntry +``` + +## Terminology + +| GBase Terminology | C# equivalent | +|-------------------|---------------| +| GBaseTable | Type | +| GBaseColumn | Property | +| GBaseEntry | Instance | + +## File Structure + +```c# +public class Model +{ + private readonly IGBase _gBase; + + public +} +``` + +## Process + +### StartUp + +### GetValue + +### SetValue diff --git a/Test.GBase/DataHandling/XmlDataHandlerLocalIntegrationTest.cs b/Test.GBase/DataHandling/XmlDataHandlerLocalIntegrationTest.cs index 72f4459..db09cfc 100644 --- a/Test.GBase/DataHandling/XmlDataHandlerLocalIntegrationTest.cs +++ b/Test.GBase/DataHandling/XmlDataHandlerLocalIntegrationTest.cs @@ -3,6 +3,7 @@ // Copyright(c) 2020 SimonG. All Rights Reserved. using System; +using System.IO; using System.Threading; using System.Threading.Tasks; using GBase.DataHandling; @@ -27,12 +28,12 @@ namespace Test.GBase.DataHandling Assert.Pass(); //Remove this assert.pass() if you want to run this test locally Mock dataWriterFactoryMock = new Mock(); - dataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())) - .Returns((string path, string rootElementName) => new XmlDataWriter(path, rootElementName)); + dataWriterFactoryMock.Setup(w => w.Create()) + .Returns(new XmlDataWriter()); Mock dataReaderFactoryMock = new Mock(); - dataReaderFactoryMock.Setup(r => r.Create(It.IsAny())) - .Returns((string path) => new XmlDataReader(path)); + dataReaderFactoryMock.Setup(r => r.Create()) + .Returns(new XmlDataReader()); Mock dataHandlerCachePropertyEntryFactoryMock = new Mock(); dataHandlerCachePropertyEntryFactoryMock.Setup(p => p.Create(It.IsAny(), It.IsAny())) @@ -48,21 +49,22 @@ namespace Test.GBase.DataHandling .Returns((IXmlDataReader xmlDataReader) => new XmlDataHandlerCache(xmlDataReader, dataHandlerCacheEntryFactoryMock.Object, dataHandlerCachePropertyEntryFactoryMock.Object)); - IXmlDataHandler xmlDataHandler = new XmlDataHandler("TestXml.xml", "Properties", dataReaderFactoryMock.Object, dataWriterFactoryMock.Object, - dataHandlerCacheFactoryMock.Object); + IXmlDataHandler xmlDataHandler = new XmlDataHandler(dataReaderFactoryMock.Object, dataWriterFactoryMock.Object, dataHandlerCacheFactoryMock.Object); - await xmlDataHandler.Init(false, new CancellationToken()); - // + xmlDataHandler.Init(false); + + FileStream file = null; //FixMe: init this correctly if you want to run this test + //await xmlDataHandler.SetValue(nameof(TestProperty), TestProperty); //await xmlDataHandler.SetValue(nameof(TestProperty), "empty"); - await xmlDataHandler.SetValue(nameof(TestProperty), "test"); + await xmlDataHandler.SetValue(file, nameof(TestProperty), "test", new CancellationToken()); //await xmlDataHandler.SetValue(nameof(TestProperty), "OverwrittenString"); // - await xmlDataHandler.SetValue(nameof(TestInt), 1); + await xmlDataHandler.SetValue(file, nameof(TestInt), 1, new CancellationToken()); //await xmlDataHandler.SetValue(nameof(TestInt), 2); //await xmlDataHandler.SetValue(nameof(TestInt), 3); - TestProperty = await xmlDataHandler.GetValue(nameof(TestProperty)); - TestInt = await xmlDataHandler.GetValue(nameof(TestInt)); + TestProperty = await xmlDataHandler.GetValue(file, nameof(TestProperty), new CancellationToken()); + TestInt = await xmlDataHandler.GetValue(file, nameof(TestInt), new CancellationToken()); } } } \ No newline at end of file diff --git a/Test.GBase/DataHandling/XmlDataHandlerTest.cs b/Test.GBase/DataHandling/XmlDataHandlerTest.cs index 699aede..475d5b8 100644 --- a/Test.GBase/DataHandling/XmlDataHandlerTest.cs +++ b/Test.GBase/DataHandling/XmlDataHandlerTest.cs @@ -19,104 +19,45 @@ namespace Test.GBase.DataHandling public class XmlDataHandlerTest { [Test] - public async Task TestInit() + public void TestInit() { Mock xmlDataReaderMock = new Mock(); - xmlDataReaderMock.Setup(r => r.Init(It.IsAny())).ReturnsAsync(true); - Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); + xmlDataReaderFactoryMock.Setup(r => r.Create()).Returns(xmlDataReaderMock.Object); Mock xmlDataWriterMock = new Mock(); - xmlDataWriterMock.Setup(w => w.Init(It.IsAny())).ReturnsAsync(true); - - Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); - - Mock xmlDataHandlerCacheMock = new Mock(); - Mock xmlDataHandlerCacheFactoryMock = new Mock(); - xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); - - Assert.True(await xmlDataHandler.Init(false, CancellationToken.None)); - } - - [Test] - public async Task TestInitFailedTriedTwice() - { - Mock xmlDataReaderMock = new Mock(); - xmlDataReaderMock.Setup(r => r.Init(It.IsAny())).ReturnsAsync(true); - - Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); - - Mock xmlDataWriterMock = new Mock(); - xmlDataWriterMock.Setup(w => w.Init(It.IsAny())).ReturnsAsync(true); - Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); + xmlDataWriterFactoryMock.Setup(w => w.Create()).Returns(xmlDataWriterMock.Object); Mock xmlDataHandlerCacheMock = new Mock(); Mock xmlDataHandlerCacheFactoryMock = new Mock(); xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); + XmlDataHandler xmlDataHandler = new XmlDataHandler(xmlDataReaderFactoryMock.Object, xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); - Assert.True(await xmlDataHandler.Init(false, CancellationToken.None)); - Assert.False(await xmlDataHandler.Init(false, CancellationToken.None)); + Assert.True(xmlDataHandler.Init(false)); } [Test] - public async Task TestInitFailedInitReaderFailed() + public void TestInitFailedTriedTwice() { Mock xmlDataReaderMock = new Mock(); - xmlDataReaderMock.Setup(r => r.Init(It.IsAny())).ReturnsAsync(false); Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); + xmlDataReaderFactoryMock.Setup(r => r.Create()).Returns(xmlDataReaderMock.Object); Mock xmlDataWriterMock = new Mock(); - xmlDataWriterMock.Setup(w => w.Init(It.IsAny())).ReturnsAsync(true); - - Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); - - Mock xmlDataHandlerCacheMock = new Mock(); - Mock xmlDataHandlerCacheFactoryMock = new Mock(); - xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); - - Assert.False(await xmlDataHandler.Init(false, CancellationToken.None)); - } - - [Test] - public async Task TestInitFailedInitWriterFailed() - { - Mock xmlDataReaderMock = new Mock(); - xmlDataReaderMock.Setup(r => r.Init(It.IsAny())).ReturnsAsync(true); - - Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); - - Mock xmlDataWriterMock = new Mock(); - xmlDataWriterMock.Setup(w => w.Init(It.IsAny())).ReturnsAsync(false); - Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); + xmlDataWriterFactoryMock.Setup(w => w.Create()).Returns(xmlDataWriterMock.Object); Mock xmlDataHandlerCacheMock = new Mock(); Mock xmlDataHandlerCacheFactoryMock = new Mock(); xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); + XmlDataHandler xmlDataHandler = new XmlDataHandler(xmlDataReaderFactoryMock.Object, xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); - Assert.False(await xmlDataHandler.Init(false, CancellationToken.None)); + Assert.True(xmlDataHandler.Init(false)); + Assert.False(xmlDataHandler.Init(false)); } [Test] @@ -124,23 +65,22 @@ namespace Test.GBase.DataHandling { Mock xmlDataReaderMock = new Mock(); Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); + xmlDataReaderFactoryMock.Setup(r => r.Create()).Returns(xmlDataReaderMock.Object); Mock xmlDataWriterMock = new Mock(); Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); + xmlDataWriterFactoryMock.Setup(w => w.Create()).Returns(xmlDataWriterMock.Object); Mock xmlDataHandlerCacheMock = new Mock(); Mock xmlDataHandlerCacheFactoryMock = new Mock(); xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); + XmlDataHandler xmlDataHandler = new XmlDataHandler(xmlDataReaderFactoryMock.Object, xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); - await xmlDataHandler.SetValue("property", "SomeString"); + await xmlDataHandler.SetValue(null, "property", "SomeString", new CancellationToken()); xmlDataHandlerCacheMock.Verify(c => c.SetValue("property", "SomeString", false), Times.Once); - xmlDataWriterMock.Verify(w => w.Write("property", "SomeString", false), Times.Once); + xmlDataWriterMock.Verify(w => w.Write(null, "property", "SomeString", false, It.IsAny()), Times.Once); } [Test] @@ -148,24 +88,23 @@ namespace Test.GBase.DataHandling { Mock xmlDataReaderMock = new Mock(); Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); + xmlDataReaderFactoryMock.Setup(r => r.Create()).Returns(xmlDataReaderMock.Object); Mock xmlDataWriterMock = new Mock(); Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); + xmlDataWriterFactoryMock.Setup(w => w.Create()).Returns(xmlDataWriterMock.Object); Mock xmlDataHandlerCacheMock = new Mock(); Mock xmlDataHandlerCacheFactoryMock = new Mock(); xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); + XmlDataHandler xmlDataHandler = new XmlDataHandler(xmlDataReaderFactoryMock.Object, xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); List stringList = new List() {"string", "secondString", "thirdString"}; - await xmlDataHandler.SetValue>("property", stringList); + await xmlDataHandler.SetValue>(null, "property", stringList, new CancellationToken()); xmlDataHandlerCacheMock.Verify(c => c.SetValue>("property", stringList, false), Times.Once); - xmlDataWriterMock.Verify(w => w.Write>("property", $"{stringList[0]},{stringList[1]},{stringList[2]}", false), Times.Once); + xmlDataWriterMock.Verify(w => w.Write>(null, "property", $"{stringList[0]},{stringList[1]},{stringList[2]}",false, It.IsAny()), Times.Once); } [Test] @@ -173,23 +112,22 @@ namespace Test.GBase.DataHandling { Mock xmlDataReaderMock = new Mock(); Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); + xmlDataReaderFactoryMock.Setup(r => r.Create()).Returns(xmlDataReaderMock.Object); Mock xmlDataWriterMock = new Mock(); Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); + xmlDataWriterFactoryMock.Setup(w => w.Create()).Returns(xmlDataWriterMock.Object); Mock xmlDataHandlerCacheMock = new Mock(); Mock xmlDataHandlerCacheFactoryMock = new Mock(); xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); + XmlDataHandler xmlDataHandler = new XmlDataHandler(xmlDataReaderFactoryMock.Object, xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); - await xmlDataHandler.SetValue("property", null); + await xmlDataHandler.SetValue(null, "property", null, new CancellationToken()); xmlDataHandlerCacheMock.Verify(c => c.SetValue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); - xmlDataWriterMock.Verify(w => w.Write(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); + xmlDataWriterMock.Verify(w => w.Write(null, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Test] @@ -197,23 +135,22 @@ namespace Test.GBase.DataHandling { Mock xmlDataReaderMock = new Mock(); Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); + xmlDataReaderFactoryMock.Setup(r => r.Create()).Returns(xmlDataReaderMock.Object); Mock xmlDataWriterMock = new Mock(); Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); + xmlDataWriterFactoryMock.Setup(w => w.Create()).Returns(xmlDataWriterMock.Object); Mock xmlDataHandlerCacheMock = new Mock(); Mock xmlDataHandlerCacheFactoryMock = new Mock(); xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); + XmlDataHandler xmlDataHandler = new XmlDataHandler(xmlDataReaderFactoryMock.Object, xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); - await xmlDataHandler.RemoveValue("property", "SomeString"); + await xmlDataHandler.RemoveValue(null, "property", "SomeString", new CancellationToken()); xmlDataHandlerCacheMock.Verify(c => c.TryRemoveValue("property", "SomeString"), Times.Once); - xmlDataWriterMock.Verify(w => w.Remove("property", "SomeString"), Times.Once); + xmlDataWriterMock.Verify(w => w.Remove(null, "property", "SomeString", It.IsAny()), Times.Once); } [Test] @@ -221,24 +158,23 @@ namespace Test.GBase.DataHandling { Mock xmlDataReaderMock = new Mock(); Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); + xmlDataReaderFactoryMock.Setup(r => r.Create()).Returns(xmlDataReaderMock.Object); Mock xmlDataWriterMock = new Mock(); Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); + xmlDataWriterFactoryMock.Setup(w => w.Create()).Returns(xmlDataWriterMock.Object); Mock xmlDataHandlerCacheMock = new Mock(); Mock xmlDataHandlerCacheFactoryMock = new Mock(); xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); + XmlDataHandler xmlDataHandler = new XmlDataHandler(xmlDataReaderFactoryMock.Object, xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); List stringList = new List() { "string", "secondString", "thirdString" }; - await xmlDataHandler.RemoveValue>("property", stringList); + await xmlDataHandler.RemoveValue>(null, "property", stringList, new CancellationToken()); xmlDataHandlerCacheMock.Verify(c => c.TryRemoveValue>("property", stringList), Times.Once); - xmlDataWriterMock.Verify(w => w.Remove>("property", $"{stringList[0]},{stringList[1]},{stringList[2]}"), Times.Once); + xmlDataWriterMock.Verify(w => w.Remove>(null, "property", $"{stringList[0]},{stringList[1]},{stringList[2]}", It.IsAny()), Times.Once); } [Test] @@ -246,23 +182,22 @@ namespace Test.GBase.DataHandling { Mock xmlDataReaderMock = new Mock(); Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); + xmlDataReaderFactoryMock.Setup(r => r.Create()).Returns(xmlDataReaderMock.Object); Mock xmlDataWriterMock = new Mock(); Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); + xmlDataWriterFactoryMock.Setup(w => w.Create()).Returns(xmlDataWriterMock.Object); Mock xmlDataHandlerCacheMock = new Mock(); Mock xmlDataHandlerCacheFactoryMock = new Mock(); xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); + XmlDataHandler xmlDataHandler = new XmlDataHandler(xmlDataReaderFactoryMock.Object, xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); - await xmlDataHandler.RemoveValue("property", null); + await xmlDataHandler.RemoveValue(null, "property", null, new CancellationToken()); xmlDataHandlerCacheMock.Verify(c => c.TryRemoveValue(It.IsAny(), It.IsAny()), Times.Never); - xmlDataWriterMock.Verify(w => w.Remove(It.IsAny(), It.IsAny()), Times.Never); + xmlDataWriterMock.Verify(w => w.Remove(null, It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Test] @@ -274,18 +209,17 @@ namespace Test.GBase.DataHandling //xmlDataReaderMock.Setup(r => r.Read("property")).ReturnsAsync>(values); Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); + xmlDataReaderFactoryMock.Setup(r => r.Create()).Returns(xmlDataReaderMock.Object); Mock xmlDataWriterMock = new Mock(); Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); + xmlDataWriterFactoryMock.Setup(w => w.Create()).Returns(xmlDataWriterMock.Object); Mock xmlDataHandlerCacheMock = new Mock(); Mock xmlDataHandlerCacheFactoryMock = new Mock(); xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); + XmlDataHandler xmlDataHandler = new XmlDataHandler(xmlDataReaderFactoryMock.Object, xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); } [Test] @@ -297,20 +231,19 @@ namespace Test.GBase.DataHandling //xmlDataReaderMock.Setup(r => r.Read("property")).ReturnsAsync>(values); Mock xmlDataReaderFactoryMock = new Mock(); - xmlDataReaderFactoryMock.Setup(r => r.Create(It.IsAny())).Returns(xmlDataReaderMock.Object); + xmlDataReaderFactoryMock.Setup(r => r.Create()).Returns(xmlDataReaderMock.Object); Mock xmlDataWriterMock = new Mock(); Mock xmlDataWriterFactoryMock = new Mock(); - xmlDataWriterFactoryMock.Setup(w => w.Create(It.IsAny(), It.IsAny())).Returns(xmlDataWriterMock.Object); + xmlDataWriterFactoryMock.Setup(w => w.Create()).Returns(xmlDataWriterMock.Object); Mock xmlDataHandlerCacheMock = new Mock(); Mock xmlDataHandlerCacheFactoryMock = new Mock(); xmlDataHandlerCacheFactoryMock.Setup(c => c.Create(It.IsAny())).Returns(xmlDataHandlerCacheMock.Object); - XmlDataHandler xmlDataHandler = new XmlDataHandler("path", "root", xmlDataReaderFactoryMock.Object, - xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); + XmlDataHandler xmlDataHandler = new XmlDataHandler(xmlDataReaderFactoryMock.Object, xmlDataWriterFactoryMock.Object, xmlDataHandlerCacheFactoryMock.Object); - IEnumerable> readValues = await xmlDataHandler.GetValues>("property"); + IEnumerable> readValues = await xmlDataHandler.GetValues>(null, "property", new CancellationToken()); } } } \ No newline at end of file diff --git a/Test.GBase/GBaseIntegrationTest/GBaseIntegrationTest.cs b/Test.GBase/GBaseIntegrationTest/GBaseIntegrationTest.cs new file mode 100644 index 0000000..50a8541 --- /dev/null +++ b/Test.GBase/GBaseIntegrationTest/GBaseIntegrationTest.cs @@ -0,0 +1,56 @@ +// Author: Gockner, Simon +// Created: 2020-11-11 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System.Threading; +using System.Threading.Tasks; +using GBase.Factories; +using GBase.Installers; +using LightweightIocContainer; +using LightweightIocContainer.Interfaces; +using NUnit.Framework; + +namespace Test.GBase.GBaseIntegrationTest +{ + [TestFixture] + public class GBaseIntegrationTest + { + private CancellationTokenSource _cancellationTokenSource; + private IIocContainer _iocContainer; + + [SetUp] + public void SetUp() + { + _cancellationTokenSource = new CancellationTokenSource(); + + _iocContainer = new IocContainer(); + _iocContainer.Install(new GBaseInstaller(), + new DataHandlingInstaller(), + new FileHandlingInstaller()); + } + + [TearDown] + public void TearDown() + { + _cancellationTokenSource.Dispose(); + _iocContainer.Dispose(); + } + + [Test] + public async Task TestGBase() + { + IGBaseFactory gBaseFactory = _iocContainer.Resolve(); + Model model = new Model(gBaseFactory); + + await model.Initialize(_cancellationTokenSource.Token); + + await model.AddItem(new Item("Item 1", 1), _cancellationTokenSource.Token); + await model.AddItem(new Item("Item 2", 2), _cancellationTokenSource.Token); + await model.AddItem(new Item("Item 3", 3), _cancellationTokenSource.Token); + + await model.AddGroup(new Group(1), _cancellationTokenSource.Token); + await model.AddGroup(new Group(2), _cancellationTokenSource.Token); + await model.AddGroup(new Group(3), _cancellationTokenSource.Token); + } + } +} \ No newline at end of file diff --git a/Test.GBase/GBaseIntegrationTest/Group.cs b/Test.GBase/GBaseIntegrationTest/Group.cs new file mode 100644 index 0000000..b9587af --- /dev/null +++ b/Test.GBase/GBaseIntegrationTest/Group.cs @@ -0,0 +1,26 @@ +// Author: Gockner, Simon +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System.Collections.Generic; +using GBase.Attributes; + +namespace Test.GBase.GBaseIntegrationTest +{ + [GBaseTable("Groups")] + public class Group : IGroup + { + public Group(int key) + { + Key = key; + Items = new List(); + } + + [GBaseColumn] + public int Key { get; private set; } + public List Items { get; } + + public override string ToString() => $"{Key}"; + public void InitializeFromString(string @string) => Key = int.Parse(@string); + } +} \ No newline at end of file diff --git a/Test.GBase/GBaseIntegrationTest/IGroup.cs b/Test.GBase/GBaseIntegrationTest/IGroup.cs new file mode 100644 index 0000000..1957f03 --- /dev/null +++ b/Test.GBase/GBaseIntegrationTest/IGroup.cs @@ -0,0 +1,15 @@ +// Author: Gockner, Simon +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System.Collections.Generic; +using GBase.Api; + +namespace Test.GBase.GBaseIntegrationTest +{ + public interface IGroup : IGBaseObject //: INotifyGBaseEntryChanged + { + int Key { get; } + List Items { get; } + } +} \ No newline at end of file diff --git a/Test.GBase/GBaseIntegrationTest/IItem.cs b/Test.GBase/GBaseIntegrationTest/IItem.cs new file mode 100644 index 0000000..8aaea05 --- /dev/null +++ b/Test.GBase/GBaseIntegrationTest/IItem.cs @@ -0,0 +1,14 @@ +// Author: Gockner, Simon +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using GBase.Api; + +namespace Test.GBase.GBaseIntegrationTest +{ + public interface IItem : IGBaseObject //: INotifyGBaseEntryChanged + { + string Name { get; } + int Key { get; } + } +} \ No newline at end of file diff --git a/Test.GBase/GBaseIntegrationTest/Item.cs b/Test.GBase/GBaseIntegrationTest/Item.cs new file mode 100644 index 0000000..e49acd8 --- /dev/null +++ b/Test.GBase/GBaseIntegrationTest/Item.cs @@ -0,0 +1,34 @@ +// Author: Gockner, Simon +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using GBase.Attributes; + +namespace Test.GBase.GBaseIntegrationTest +{ + [GBaseTable("Items")] + public class Item : IItem + { + public Item(string name, int key) + { + Name = name; + Key = key; + } + + [GBaseColumn] + public string Name { get; private set; } + + [GBaseColumn] + public int Key { get; private set; } + + public override string ToString() => $"{Name}"; + + public void InitializeFromString(string @string) + { + string[] properties = @string.Split('/'); + + Key = int.Parse(properties[0]); + Name = properties[1]; + } + } +} \ No newline at end of file diff --git a/Test.GBase/GBaseIntegrationTest/Model.cs b/Test.GBase/GBaseIntegrationTest/Model.cs new file mode 100644 index 0000000..0b608a7 --- /dev/null +++ b/Test.GBase/GBaseIntegrationTest/Model.cs @@ -0,0 +1,54 @@ +// Author: Gockner, Simon +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using GBase.Factories; +using GBase.Interfaces; + +namespace Test.GBase.GBaseIntegrationTest +{ + public class Model + { + private readonly IGBase _gBase; + + public Model(IGBaseFactory gBaseFactory) + { + _gBase = gBaseFactory.Create(new Settings()); + Items = new List(); + Groups = new List(); + } + + public List Items { get; private set; } + public List Groups { get; private set; } + + public async Task Initialize(CancellationToken cancellationToken) + { + await _gBase.Init("DB", Assembly.GetExecutingAssembly(), cancellationToken); + + // IGBaseTable itemsTable = _gBase.GetTable(); + // Items = itemsTable.Entries.Cast().ToList(); + Items = (await _gBase.GetValues(this, nameof(Items), cancellationToken)).Cast().ToList(); + + // IGBaseTable groupsTable = _gBase.GetTable(); + // Groups = groupsTable.Entries.Cast().ToList(); + Groups = (await _gBase.GetValues(this, nameof(Groups), cancellationToken)).Cast().ToList(); + } + + public async Task AddItem(Item item, CancellationToken cancellationToken) + { + Items.Add(item); + await _gBase.AddEntry(item, cancellationToken); + } + + public async Task AddGroup(Group group, CancellationToken cancellationToken) + { + Groups.Add(group); + await _gBase.AddEntry(group, cancellationToken); + } + } +} \ No newline at end of file diff --git a/Test.GBase/GBaseIntegrationTest/Settings.cs b/Test.GBase/GBaseIntegrationTest/Settings.cs new file mode 100644 index 0000000..9040384 --- /dev/null +++ b/Test.GBase/GBaseIntegrationTest/Settings.cs @@ -0,0 +1,15 @@ +// Author: Gockner, Simon +// Created: 2020-09-18 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; +using System.IO; +using GBase.Interfaces.Settings; + +namespace Test.GBase.GBaseIntegrationTest +{ + public class Settings : IGBaseSettings + { + public string DatabasePath => Path.Combine(Environment.CurrentDirectory, "DB"); + } +} \ No newline at end of file diff --git a/Test.GBase/GBaseTableIntegrationTest.cs b/Test.GBase/GBaseTableIntegrationTest.cs index 11da737..cf69136 100644 --- a/Test.GBase/GBaseTableIntegrationTest.cs +++ b/Test.GBase/GBaseTableIntegrationTest.cs @@ -7,6 +7,7 @@ using GBase; using GBase.Factories; using GBase.FileHandling.Factories; using GBase.Interfaces; +using GBase.Interfaces.DataHandling.Pool; using GBase.Interfaces.FileHandling; using Moq; using NUnit.Framework; @@ -22,18 +23,25 @@ namespace Test.GBase { Mock fileHandlerFactoryMock = new Mock(); fileHandlerFactoryMock.Setup(f => f.Create()).Returns(new Mock().Object); - + + IGBaseColumn gBaseColumn = null; Mock gBaseColumnFactoryMock = new Mock(); - gBaseColumnFactoryMock.Setup(c => c.Create()).Returns(new GBaseColumn()); + gBaseColumnFactoryMock.Setup(c => c.Create(It.IsAny())) + .Callback(name => { gBaseColumn = new GBaseColumn(name); }) + .Returns(gBaseColumn); - IGBaseTable table = new GBaseTable(fileHandlerFactoryMock.Object, gBaseColumnFactoryMock.Object); - table.Init(typeof(Foo), nameof(Foo), "", CancellationToken.None); + Mock dataHandlerPoolMock = new Mock(); + + IGBaseTable table = new GBaseTable(fileHandlerFactoryMock.Object, dataHandlerPoolMock.Object, gBaseColumnFactoryMock.Object); + table.Init(typeof(Foo), "", "", CancellationToken.None); + CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + Foo foo = new Foo("Test"); - table.AddEntry(foo); + table.AddEntry(foo, cancellationTokenSource.Token); Foo foo2 = new Foo("Test2"); - table.AddEntry(foo2); + table.AddEntry(foo2, cancellationTokenSource.Token); foo.Name = "Foo - ex Test"; foo2.Name = "Some Name"; diff --git a/Test.GBase/TestClasses/Foo.cs b/Test.GBase/TestClasses/Foo.cs index 0d4f1c2..2cfbb7c 100644 --- a/Test.GBase/TestClasses/Foo.cs +++ b/Test.GBase/TestClasses/Foo.cs @@ -7,7 +7,7 @@ using GBase.Attributes; namespace Test.GBase.TestClasses { - [GBaseTable] + [GBaseTable("Foo")] public class Foo : NotifyGBaseEntryChanged { private string _name;