From a4108ac9206f12a307c27c8d83247d78fbea2db2 Mon Sep 17 00:00:00 2001 From: Harald Christian Joachim Wolff Date: Tue, 16 Oct 2018 21:03:23 +0200 Subject: [PATCH] Initial Commit --- .gitignore | 41 +++ OODB.cs | 186 ++++++++++++++ Persistent.cs | 13 + Properties/AssemblyInfo.cs | 26 ++ Query.cs | 221 ++++++++++++++++ StorageAdapter.cs | 271 ++++++++++++++++++++ attributes/IndexedAttribute.cs | 10 + attributes/ReferencedFieldAttribute.cs | 13 + attributes/UniqueAttribute.cs | 10 + descriptor/Descriptor.cs | 124 +++++++++ index/Index.cs | 111 ++++++++ index/SearchIndex.cs | 105 ++++++++ index/Unique.cs | 73 ++++++ mapping/ArrayMapping.cs | 32 +++ mapping/DoubleMapping.cs | 32 +++ mapping/ExtendedFieldHandling.cs | 25 ++ mapping/FlatTypeMapping.cs | 57 ++++ mapping/IntegerMapping.cs | 32 +++ mapping/ListMapping.cs | 61 +++++ mapping/ReferenceMapping.cs | 37 +++ mapping/StringMapping.cs | 27 ++ mapping/collections/ISetExtendedHandling.cs | 56 ++++ mapping/collections/ISetImplementation.cs | 150 +++++++++++ persistence/PersistenceCache.cs | 99 +++++++ sharp-oodb.csproj | 75 ++++++ 25 files changed, 1887 insertions(+) create mode 100644 .gitignore create mode 100644 OODB.cs create mode 100644 Persistent.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 Query.cs create mode 100644 StorageAdapter.cs create mode 100644 attributes/IndexedAttribute.cs create mode 100644 attributes/ReferencedFieldAttribute.cs create mode 100644 attributes/UniqueAttribute.cs create mode 100644 descriptor/Descriptor.cs create mode 100644 index/Index.cs create mode 100644 index/SearchIndex.cs create mode 100644 index/Unique.cs create mode 100644 mapping/ArrayMapping.cs create mode 100644 mapping/DoubleMapping.cs create mode 100644 mapping/ExtendedFieldHandling.cs create mode 100644 mapping/FlatTypeMapping.cs create mode 100644 mapping/IntegerMapping.cs create mode 100644 mapping/ListMapping.cs create mode 100644 mapping/ReferenceMapping.cs create mode 100644 mapping/StringMapping.cs create mode 100644 mapping/collections/ISetExtendedHandling.cs create mode 100644 mapping/collections/ISetImplementation.cs create mode 100644 persistence/PersistenceCache.cs create mode 100644 sharp-oodb.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf793ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Autosave files +*~ + +# build +[Oo]bj/ +[Bb]in/ +packages/ +TestResults/ + +# globs +Makefile.in +*.DS_Store +*.sln.cache +*.suo +*.cache +*.pidb +*.userprefs +*.usertasks +config.log +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.user +*.tar.gz +tarballs/ +test-results/ +Thumbs.db +.vs/ + +# Mac bundle stuff +*.dmg +*.app + +# resharper +*_Resharper.* +*.Resharper + +# dotCover +*.dotCover diff --git a/OODB.cs b/OODB.cs new file mode 100644 index 0000000..363c5a2 --- /dev/null +++ b/OODB.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using oodb.descriptor; +using oodb.mapping; +using oodb.persistence; +using System.IO; +using System.Linq; +using System.Xml; +using System.CodeDom; +using System.ComponentModel; +using System.Reflection; +using oodb.mapping.collections; +namespace oodb +{ + public class OODB + { + + public PersistenceCache PersistenceCache { get; } + public StorageAdapter StorageAdapter { get; } + + internal Dictionary descriptors = new Dictionary(); + internal Dictionary flatTypeMappings = new Dictionary(); + + + public OODB(String storagePath) + :this(new DirectoryInfo(storagePath)) + { + } + + public OODB(DirectoryInfo storageDirectory) + { + PersistenceCache = new PersistenceCache(this); + StorageAdapter = new StorageAdapter(this,storageDirectory); + + PopulateFlatMappings(); + + AppDomain.CurrentDomain.ProcessExit += (sender, e) => this.Close(); + } + + public void Close() + { + StorageAdapter.Close(); + } + + public void Prepare() + { + foreach (Descriptor descriptor in descriptors.Values) + { + descriptor.Initialize(); + } + } + + + public OODB Add() where T : Persistent => Add(typeof(T)); + public OODB Add(Type type) + { + GetDescriptor(type); + return this; + } + + public void Save(IEnumerable persistents) + { + foreach (Persistent persistent in persistents) + Save(persistent); + } + public void Save(Persistent o) + { + PersistenceCache.Touch(o); + StorageAdapter.Save(o); + } + + public void Ensure(Persistent o) + { + if (!StorageAdapter.Knows(o.GetType(), o.PersistenceID)) + Save(o); + } + + public T Load(Guid persistenceID) where T: Persistent + { + return (T)Load(typeof(T), persistenceID); + } + public Persistent Load(Type type,Guid persistenceID) + { + if (persistenceID.Equals(Guid.Empty)) + return null; + + try { + return PersistenceCache.Get(type, persistenceID); + } catch (KeyNotFoundException) + { + Type realType = StorageAdapter.GetDiscriminator(type, persistenceID); + Persistent persistent = Activator.CreateInstance(type,true) as Persistent; + + persistent.PersistenceID = persistenceID; + PersistenceCache.Touch(persistent); + + GetDescriptor(realType).Attach(persistent); + + StorageAdapter.Restore(persistent); + + return persistent; + } + } + + public IEnumerable List() where T:Persistent => List(typeof(T)).Select((x) => (T)x); + public IEnumerable List(Type type) + { + return ListIdentities(type).Select((x) => Load(x.Value, x.Key)); + } + + private IEnumerable> ListIdentities(Type type) + { + return StorageAdapter.List(GetDescriptor(type)); + } + + + + public Type LookupType(string fullName) + { + foreach (Type t in descriptors.Keys) + if (t.FullName.Equals(fullName)) + return t; + throw new KeyNotFoundException(fullName); + } + + public Descriptor GetDescriptor(Type type) + { + if (!descriptors.ContainsKey(type)) + { + descriptors[type] = new Descriptor(this, type); + } + return descriptors[type]; + } + public Descriptor GetDescriptor() => GetDescriptor(typeof(T)); + + public FlatTypeMapping GetFlatTypeMapping(Type type) + { + if (!flatTypeMappings.ContainsKey(type)) + { + if (type.IsArray) + { + flatTypeMappings[type] = new ArrayMapping(this, type); + } + else if (type.IsSubclassOf(typeof(Persistent))) + { + Add(type); + } + else if (type.IsGenericType && ((type.GetGenericTypeDefinition() == typeof(List<>)) || (type.GetGenericTypeDefinition() == typeof(IList<>)))) + { + FlatTypeMapping elementMapping = GetFlatTypeMapping(type.GetGenericArguments()[0]); + flatTypeMappings[type] = new ListMapping(this, type); + } else { + throw new KeyNotFoundException(); + } + } + return flatTypeMappings[type]; + } + + public ExtendedFieldHandling CreateExtendedFieldHandling(FieldInfo fieldInfo) + { + if (fieldInfo.FieldType.IsGenericType){ + Type genericTypeDefinition = fieldInfo.FieldType.GetGenericTypeDefinition(); + + if (genericTypeDefinition == typeof(ISet<>)) + { + return new ISetExtendedHandling(this, fieldInfo); + } + + } + + throw new NotSupportedException(); + } + + + private void PopulateFlatMappings() + { + flatTypeMappings[typeof(string)] = new StringMapping(this); + flatTypeMappings[typeof(int)] = new IntegerMapping(this); + flatTypeMappings[typeof(short)] = new IntegerMapping(this); + flatTypeMappings[typeof(byte)] = new IntegerMapping(this); + flatTypeMappings[typeof(float)] = new DoubleMapping(this); + flatTypeMappings[typeof(double)] = new DoubleMapping(this); + } + + } +} diff --git a/Persistent.cs b/Persistent.cs new file mode 100644 index 0000000..ac71812 --- /dev/null +++ b/Persistent.cs @@ -0,0 +1,13 @@ +using System; +namespace oodb +{ + public abstract class Persistent + { + public Guid PersistenceID { get; internal set; } + + public Persistent() + { + PersistenceID = Guid.NewGuid(); + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..74cf66b --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("sharp-oodb")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("${AuthorCopyright}")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/Query.cs b/Query.cs new file mode 100644 index 0000000..4dea88d --- /dev/null +++ b/Query.cs @@ -0,0 +1,221 @@ +using System; +using oodb.descriptor; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Remoting.Metadata; +using System.Runtime.Remoting.Messaging; +using System.Collections; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Xml.Xsl.Runtime; +using oodb.index; +namespace oodb +{ + public class QueryField + { + public string FieldName { get; } + + public QueryField(String fieldName) + { + FieldName = fieldName; + } + + public Predicate Contains(object value) + { + return new Predicate.Contains(this, value); + } + } + + public abstract class Predicate + { + public QueryField QueryField { get; } + + public Predicate(QueryField queryField) + { + QueryField = queryField; + } + + public virtual bool MatchesTextValues => false; + + public virtual bool TextMatch(string textValue) { throw new NotSupportedException(); } + public abstract bool Match(object value); + + public OrPredicate Or => new OrPredicate(this); + + + public class OrPredicate : Predicate + { + public HashSet Predicates { get; } = new HashSet(); + + public OrPredicate(Predicate creator) + :base(creator.QueryField) + { + Predicates.Add(creator); + } + + public OrPredicate Contains(object value) + { + Predicates.Add(new Predicate.Contains(this.QueryField, value)); + return this; + } + + public override bool MatchesTextValues + { + get { + foreach (Predicate predicate in Predicates) + if (!predicate.MatchesTextValues) + return false; + return true; + } + } + + public override bool TextMatch(string textValue) + { + foreach (Predicate predicate in Predicates) + if (predicate.TextMatch(textValue)) + return true; + return false; + } + + public override bool Match(object value) + { + foreach (Predicate predicate in Predicates) + if (predicate.Match(value)) + return true; + return false; + } + } + + public class Contains : Predicate + { + public object Value { get; } + + public Contains(QueryField queryField,object value) + :base(queryField) + { + Value = value; + } + + public override bool Match(object value) + { + if ((Value == null) || String.Empty.Equals(Value)) + return true; + + if (value == null) + return false; + + return (value as string).Contains(Value as string); + } + } + + } + + public class Query where T: Persistent + { + public virtual AlternativesQuery Or(Query alternativeQuery) => new AlternativesQuery(this, alternativeQuery); + public virtual AlternativesQuery Or(params Predicate[] predicates) => new AlternativesQuery(this, new Query(predicates)); + + public HashSet Predicates { get; } = new HashSet(); + + + protected Query() + { + } + + public Query(IEnumerable predicates) + { + foreach (Predicate predicate in predicates) + { + Predicates.Add(predicate); + } + } + + public Query(params Predicate[] predicates) + { + foreach (Predicate predicate in predicates) + { + Predicates.Add(predicate); + } + } + + public IEnumerable GetPredicates(QueryField queryField) + { + return Predicates.Where((x) => x.QueryField == queryField); + } + + public virtual IEnumerable Search(OODB oodb) + { + Descriptor descriptor = oodb.GetDescriptor(); + HashSet predicateFields = new HashSet(); + + foreach (Predicate predicate in Predicates) + { + predicateFields.Add(predicate.QueryField); + } + + Guid[] results = null; + + foreach (QueryField queryField in predicateFields) + { + FieldInfo fieldInfo = descriptor.GetFieldInfo(queryField.FieldName); + if (!oodb.StorageAdapter.HasSearchIndex(fieldInfo)) + throw new NotSupportedException("Queries must use indexed fields"); + + SearchIndex searchIndex = oodb.StorageAdapter.GetSearchIndex(fieldInfo); + + foreach (Predicate predicate in GetPredicates(queryField)) + { + IEnumerable myresults = searchIndex.Query(predicate); + if (results == null) + { + results = myresults.ToArray(); + } else { + results = results.Intersect(myresults).ToArray(); + } + } + } + + return results.Select((id)=>oodb.Load(id)); + } + + } + + public class AlternativesQuery : Query where T:Persistent + { + public HashSet> Alternatives = new HashSet>(); + + public AlternativesQuery(params Query[] queries) + { + foreach (Query alternative in queries) + Alternatives.Add(alternative); + } + + public override AlternativesQuery Or(Query alternativeQuery) + { + Alternatives.Add(alternativeQuery); + return this; + } + + public override AlternativesQuery Or(params Predicate[] predicates) + { + Alternatives.Add(new Query(predicates)); + return this; + } + + public override IEnumerable Search(OODB oodb) + { + return Alternatives.SelectMany((alternative) => alternative.Search(oodb)).Distinct(); + } + + } + + + + + + +} + + + + diff --git a/StorageAdapter.cs b/StorageAdapter.cs new file mode 100644 index 0000000..0446135 --- /dev/null +++ b/StorageAdapter.cs @@ -0,0 +1,271 @@ +using System; +using oodb.descriptor; +using System.Collections.Generic; +using System.IO; +using System.Collections; +using System.Reflection; +using System.Text; +using System.Xml; +using oodb.mapping; +using sharp.logging; +using System.Linq; +using oodb.index; +namespace oodb +{ + public class StorageAdapter + { + public OODB OODB { get; } + public DirectoryInfo BaseDirectory { get; } + + Dictionary typeStorages = new Dictionary(); + + public StorageAdapter(OODB oodb,DirectoryInfo baseDirectory) + { + OODB = oodb; + BaseDirectory = baseDirectory; + + if (!BaseDirectory.Exists) + BaseDirectory.Create(); + } + + public void Close() + { + foreach (KeyValuePair kvp in typeStorages) + { + foreach (Index index in kvp.Value.indeces) + index.Save(); + } + } + + public void Save(Persistent persistent) + { + GetTypeStorage(persistent.GetType()).Save(persistent); + } + + public void Restore(Persistent persistent) + { + GetTypeStorage(persistent.GetType()).Restore(persistent); + } + + public bool Knows(Type type,Guid persistenceID) + { + return GetTypeStorage(type).Knows(persistenceID); + } + + public void Reference(Persistent persistent, FieldInfo referencing) + { + GetTypeStorage(persistent.GetType()).Reference(persistent, referencing); + } + public void UnReference(Persistent persistent, FieldInfo referencing) + { + GetTypeStorage(persistent.GetType()).UnReference(persistent, referencing); + } + + public IEnumerable> List(Descriptor descriptor) + { + return GetTypeStorage(descriptor.NativeType).List(); + } + + public Type GetDiscriminator(Type type,Guid persistenceID) + { + return GetTypeStorage(type).GetDiscriminator(persistenceID); + } + + + public SearchIndex GetSearchIndex(FieldInfo fieldInfo) + { + return GetTypeStorage(fieldInfo.DeclaringType).GetSearchIndex(fieldInfo); + } + public bool HasSearchIndex(FieldInfo fieldInfo) + { + return GetTypeStorage(fieldInfo.DeclaringType).HasSearchIndex(fieldInfo); + } + + + public TypeStorage GetTypeStorage(Type type) + { + if (!typeStorages.ContainsKey(type)) + { + typeStorages[type] = new TypeStorage(this, type); + } + return typeStorages[type]; + } + + public class TypeStorage + { + public StorageAdapter StorageAdapter { get; } + + public Type PersistentType { get; } + public DirectoryInfo Directory { get; } + + public Descriptor Descriptor { get; } + public TypeStorage BaseStorage { get; } + + public HashSet indeces = new HashSet(); + public Dictionary searchIndeces = new Dictionary(); + + public TypeStorage(StorageAdapter storageAdapter, Type type) + { + StorageAdapter = storageAdapter; + PersistentType = type; + + Directory = new DirectoryInfo(Path.Combine(StorageAdapter.BaseDirectory.FullName, type.FullName)); + if (!Directory.Exists) + Directory.Create(); + + Descriptor = storageAdapter.OODB.GetDescriptor(type); + if (Descriptor.BaseDescriptor != null) + { + BaseStorage = StorageAdapter.GetTypeStorage(Descriptor.BaseDescriptor.NativeType); + } + + } + + public bool AddIndex(Index index) + { + return indeces.Add(index); + } + + public bool HasSearchIndex(FieldInfo fieldInfo) => searchIndeces.ContainsKey(fieldInfo); + + public SearchIndex GetSearchIndex(FieldInfo fieldInfo) + { + if (!searchIndeces.ContainsKey(fieldInfo)) + { + SearchIndex searchIndex = new SearchIndex(StorageAdapter.OODB, fieldInfo); + searchIndeces.Add(fieldInfo, searchIndex); + } + return searchIndeces[fieldInfo]; + } + + private void RemoveDiscriminator(Guid persistenceID) + { + foreach (Index index in indeces) + { + index.Remove(persistenceID); + } + + File.Delete(Path.Combine(Directory.FullName, String.Format("{0}.discriminator", persistenceID.ToString()))); + if (BaseStorage != null) + BaseStorage.RemoveDiscriminator(persistenceID); + } + private String ReadDiscriminator(Guid persistenceID) + { + byte[] discriminator = File.ReadAllBytes(Path.Combine(Directory.FullName, String.Format("{0}.discriminator", persistenceID.ToString()))); + return Encoding.UTF8.GetString(discriminator); + } + + private void WriteDiscriminator(Persistent persistent) + { + foreach (Index index in indeces) + { + index.Add(persistent); + } + + using (FileStream fileStream = new FileStream(Path.Combine(Directory.FullName,String.Format("{0}.discriminator",persistent.PersistenceID.ToString())),FileMode.Create)) + { + byte[] discriminator = Encoding.UTF8.GetBytes(persistent.GetType().FullName); + fileStream.Write(discriminator, 0, discriminator.Length); + fileStream.Close(); + } + + if (BaseStorage != null) + BaseStorage.WriteDiscriminator(persistent); + } + + public bool Knows(Guid persistenceID) + { + return File.Exists(Path.Combine(Directory.FullName, String.Format("{0}.discriminator", persistenceID.ToString()))); + } + + + public void Save(Persistent persistent) + { + try { + WriteDiscriminator(persistent); + } catch (Exception e) + { + Logger.Default.Log(e); + RemoveDiscriminator(persistent.PersistenceID); + + throw e; + } + + try + { + Descriptor descriptor = StorageAdapter.OODB.GetDescriptor(persistent.GetType()); + + XmlDocument xmlDocument = new XmlDocument(); + XmlElement persistetInstance = xmlDocument.CreateElement("PersistetInstance"); + + xmlDocument.AppendChild(persistetInstance); + + foreach (KeyValuePair flatField in descriptor.FlatMappings) + { + XmlElement fieldElement = xmlDocument.CreateElement("Field"); + fieldElement.SetAttribute("Name", flatField.Key.Name); + fieldElement.AppendChild(flatField.Value.ToXml(xmlDocument, flatField.Key.GetValue(persistent))); + + persistetInstance.AppendChild(fieldElement); + } + + xmlDocument.Save(Path.Combine(Directory.FullName, String.Format("{0}.xml", persistent.PersistenceID.ToString()))); + } catch (Exception e) + { + Logger.Default.Log(LogLevel.ERROR, "StorageAdapter.Save(): {0}", e); + + } + } + + public void Restore(Persistent persistent) + { + Descriptor descriptor = StorageAdapter.OODB.GetDescriptor(persistent.GetType()); + + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.Load(Path.Combine(Directory.FullName, String.Format("{0}.xml", persistent.PersistenceID.ToString()))); + + foreach (XmlElement fieldElement in xmlDocument.SelectNodes("PersistetInstance/Field")) + { + try { + FieldInfo fieldInfo = Descriptor.GetFieldInfo(fieldElement.GetAttribute("Name")); + FlatTypeMapping flatTypeMapping = Descriptor.GetFlatTypeMapping(fieldInfo); + + fieldInfo.SetValue(persistent, flatTypeMapping.FromXml(fieldElement.SelectSingleNode("Value") as XmlElement)); + + } catch (KeyNotFoundException) + { + Logger.Default.Log(LogLevel.WARNING, "data for unknown field {0} ignored", fieldElement.GetAttribute("Name")); + } + } + + } + + public void Reference(Persistent persistent, FieldInfo referencing) + { + throw new NotSupportedException(); + } + public void UnReference(Persistent persistent, FieldInfo referencing) + { + throw new NotSupportedException(); + } + + public IEnumerable> List() + { + KeyValuePair create(FileInfo fileInfo) + { + Guid persistenceID = Guid.Parse(fileInfo.Name.Substring(0, fileInfo.Name.Length - fileInfo.Extension.Length)); + return new KeyValuePair(persistenceID,GetDiscriminator(persistenceID)); + } + return Directory.GetFiles("*.discriminator").Select(create); + } + + public Type GetDiscriminator(Guid persistenceID) + { + String typeName = ReadDiscriminator(persistenceID); + return StorageAdapter.OODB.LookupType(typeName); + } + + + } + } +} diff --git a/attributes/IndexedAttribute.cs b/attributes/IndexedAttribute.cs new file mode 100644 index 0000000..04ba5de --- /dev/null +++ b/attributes/IndexedAttribute.cs @@ -0,0 +1,10 @@ +using System; +namespace oodb.attributes +{ + public class IndexedAttribute : Attribute + { + public IndexedAttribute() + { + } + } +} diff --git a/attributes/ReferencedFieldAttribute.cs b/attributes/ReferencedFieldAttribute.cs new file mode 100644 index 0000000..9540dd5 --- /dev/null +++ b/attributes/ReferencedFieldAttribute.cs @@ -0,0 +1,13 @@ +using System; +namespace oodb.attributes +{ + public class ReferencedFieldAttribute : Attribute + { + public String FieldName { get; } + + public ReferencedFieldAttribute(String FieldName) + { + this.FieldName = FieldName; + } + } +} diff --git a/attributes/UniqueAttribute.cs b/attributes/UniqueAttribute.cs new file mode 100644 index 0000000..785f1b7 --- /dev/null +++ b/attributes/UniqueAttribute.cs @@ -0,0 +1,10 @@ +using System; +namespace oodb.attributes +{ + public class UniqueAttribute : Attribute + { + public UniqueAttribute() + { + } + } +} diff --git a/descriptor/Descriptor.cs b/descriptor/Descriptor.cs new file mode 100644 index 0000000..a5476d7 --- /dev/null +++ b/descriptor/Descriptor.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using oodb.mapping; +using System.Linq.Expressions; +using sharp.logging; +using oodb.attributes; +using oodb.index; +namespace oodb.descriptor +{ + public class Descriptor + { + public OODB OODB { get; } + public Type NativeType { get; } + + public Descriptor BaseDescriptor { get; } + public IEnumerable> FlatMappings => flatMappings; + + + Dictionary flatMappings = new Dictionary(); + Dictionary extendedFieldHandlers = new Dictionary(); + + public Descriptor(OODB oodb,Type type) + { + OODB = oodb; + NativeType = type; + + OODB.descriptors.Add(NativeType, this); + OODB.flatTypeMappings[type] = new ReferenceMapping(oodb, type); + + if (type.BaseType != typeof(Persistent)) + { + BaseDescriptor = OODB.GetDescriptor(type.BaseType); + } + + ConstructMappings(); + } + + public void Initialize() + { + foreach (ExtendedFieldHandling extendedFieldHandling in extendedFieldHandlers.Values) + { + extendedFieldHandling.Initialize(); + } + } + + public void Attach(Persistent persistent) + { + foreach (ExtendedFieldHandling extendedFieldHandling in extendedFieldHandlers.Values) + { + extendedFieldHandling.Attach(persistent); + } + } + public void Detach(Persistent persistent) + { + foreach (ExtendedFieldHandling extendedFieldHandling in extendedFieldHandlers.Values) + { + extendedFieldHandling.Detach(persistent); + } + } + + public FieldInfo GetFieldInfo(string fieldName) + { + foreach (FieldInfo fieldInfo in flatMappings.Keys) + if (fieldInfo.Name.Equals(fieldName)) + return fieldInfo; + + throw new KeyNotFoundException(); + } + + public FlatTypeMapping GetFlatTypeMapping(FieldInfo fieldInfo) + { + return flatMappings[fieldInfo]; + } + + + + private void ConstructMappings() + { + if (BaseDescriptor != null) + { + foreach (FieldInfo baseField in BaseDescriptor.flatMappings.Keys) + { + flatMappings.Add(baseField, BaseDescriptor.flatMappings[baseField]); + } + foreach (FieldInfo baseField in BaseDescriptor.extendedFieldHandlers.Keys) + { + extendedFieldHandlers.Add(baseField, BaseDescriptor.extendedFieldHandlers[baseField]); + } + } + + foreach (FieldInfo fieldInfo in NativeType.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic)) + { + if (fieldInfo.GetCustomAttribute() != null) + { + OODB.StorageAdapter.GetSearchIndex(fieldInfo); + } + + try + { + FlatTypeMapping flatTypeMapping = OODB.GetFlatTypeMapping(fieldInfo.FieldType); + flatMappings.Add(fieldInfo, flatTypeMapping); + + if (fieldInfo.GetCustomAttribute() != null) + { + Unique uniqueIndex = new Unique(OODB, fieldInfo); + } + + } catch (KeyNotFoundException) + { + try { + + ExtendedFieldHandling extendedFieldHandling = OODB.CreateExtendedFieldHandling(fieldInfo); + extendedFieldHandlers.Add(fieldInfo, extendedFieldHandling); + + } catch (NotSupportedException) + { + Logger.Default.Log("Ignoring unsupported Field {0}", fieldInfo); + } + } + } + } + } +} diff --git a/index/Index.cs b/index/Index.cs new file mode 100644 index 0000000..a13a693 --- /dev/null +++ b/index/Index.cs @@ -0,0 +1,111 @@ +using System; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; +using System.Xml; +using oodb.mapping; +using System.IO; +using System.Reflection.Emit; +using System.Linq; +namespace oodb.index +{ + public abstract class Index + { + public OODB OODB { get; } + public FieldInfo FieldInfo { get; } + + public String FileName { get; } + + public FlatTypeMapping ValueMapping { get; } + public FlatTypeMapping StorageMapping { get; protected set; } + + public Index(OODB oodb, FieldInfo fieldInfo) + { + OODB = oodb; + FieldInfo = fieldInfo; + + ValueMapping = OODB.GetFlatTypeMapping(fieldInfo.FieldType); + StorageMapping = ValueMapping; + + FileName = Path.Combine( + OODB.StorageAdapter.GetTypeStorage(FieldInfo.DeclaringType).Directory.FullName, + String.Format("index_{0}_{1}_{2}.xml", GetType().Name, FieldInfo.DeclaringType.Name, FieldInfo.Name) + ); + + OODB.StorageAdapter.GetTypeStorage(fieldInfo.DeclaringType).AddIndex(this); + + Prepare(); + + Load(); + } + + protected abstract void Prepare(); + + public abstract IEnumerable> RetrieveIndexValues(); + public abstract void RestoreIndexValues(IEnumerable> indexValues); + + public abstract void Add(Persistent persistent); + public abstract void Remove(Guid persistenceID); + + public void Remove(Persistent persistent) + { + Remove(persistent.PersistenceID); + } + + public abstract IEnumerable Query(); + + + public void Load() + { + if (File.Exists(FileName)) + { + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.Load(FileName); + + RestoreIndexValues( + xmlDocument.SelectNodes("/Index/Entry").Cast().Select((entry) => new KeyValuePair(Guid.Parse(entry.GetAttribute("PersistenceID")), StorageMapping.FromXml(entry.SelectSingleNode("Value") as XmlElement))) + ); + } + } + public void Save() + { + XmlDocument xmlDocument = new XmlDocument(); + XmlElement indexElement = xmlDocument.CreateElement("Index"); + indexElement.SetAttribute("Type", GetType().FullName); + + xmlDocument.AppendChild(indexElement); + + + foreach (KeyValuePair entry in RetrieveIndexValues()) + { + XmlElement xmlEntry = xmlDocument.CreateElement("Entry"); + xmlEntry.SetAttribute("PersistenceID", entry.Key.ToString()); + xmlEntry.AppendChild(StorageMapping.ToXml(xmlDocument, entry.Value)); + indexElement.AppendChild(xmlEntry); + } + + xmlDocument.Save(FileName); + } + + + public override int GetHashCode() + { + return FileName.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (obj is Index) + { + Index other = obj as Index; + return FileName.Equals(other.FileName); + } + + return false; + } + + + + + } +} diff --git a/index/SearchIndex.cs b/index/SearchIndex.cs new file mode 100644 index 0000000..d2541ef --- /dev/null +++ b/index/SearchIndex.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using oodb.mapping; +using System.Linq; +using System.Collections; +namespace oodb.index +{ + public class SearchIndex : Index + { + + Dictionary> index = new Dictionary>(); + Dictionary reverseIndex = new Dictionary(); + + public SearchIndex(OODB oodb,FieldInfo fieldInfo) + :base(oodb,fieldInfo) + { + } + + protected override void Prepare() + { + StorageMapping = OODB.GetFlatTypeMapping(typeof(string)); + } + + public ISet GetBucket(string value) + { + if (!index.ContainsKey(value)) + { + index.Add(value, new HashSet()); + } + return index[value]; + } + + public void Add(string value,Guid persistenceID) + { + if (reverseIndex.ContainsKey(persistenceID)) + { + if (!reverseIndex[persistenceID].Equals(value)) + { + GetBucket(reverseIndex[persistenceID]).Remove(persistenceID); + GetBucket(value).Add(persistenceID); + reverseIndex[persistenceID] = value; + } + } else { + reverseIndex[persistenceID] = value; + GetBucket(value).Add(persistenceID); + } + } + + public override void Add(Persistent persistent) + { + Add(ValueMapping.ToText(FieldInfo.GetValue(persistent)), persistent.PersistenceID); + } + + public override IEnumerable Query() + { + return reverseIndex.Keys; + } + + public IEnumerable QueryTextValue(string textValue) + { + return GetBucket(textValue); + } + public IEnumerable Query(object value) + { + return QueryTextValue(ValueMapping.ToText(value)); + } + + public IEnumerable Query(Predicate predicate) + { + if (predicate.MatchesTextValues) + { + return reverseIndex.Where((kvp) => predicate.TextMatch(kvp.Value)).Select((kvp) => kvp.Key); + } else + { + return reverseIndex.Where((kvp) => predicate.Match(ValueMapping.FromText(kvp.Value))).Select((kvp) => kvp.Key); + } + } + + public override void Remove(Guid persistenceID) + { + if (reverseIndex.ContainsKey(persistenceID)) + { + GetBucket(reverseIndex[persistenceID]).Remove(persistenceID); + reverseIndex.Remove(persistenceID); + } + } + + public override void RestoreIndexValues(IEnumerable> indexValues) + { + reverseIndex.Clear(); + index.Clear(); + + foreach (KeyValuePair kvp in indexValues) + { + Add(kvp.Value as string, kvp.Key); + } + } + + public override IEnumerable> RetrieveIndexValues() + { + return reverseIndex.Select((x) => new KeyValuePair(x.Key, x.Value)); + } + } +} diff --git a/index/Unique.cs b/index/Unique.cs new file mode 100644 index 0000000..100d141 --- /dev/null +++ b/index/Unique.cs @@ -0,0 +1,73 @@ +using System; +using System.Reflection; +using System.Collections.Generic; +namespace oodb.index +{ + public class Unique : Index + { + + Dictionary uniqueForward = new Dictionary(); + Dictionary uniqueReverse = new Dictionary(); + + public Unique(OODB oodb,FieldInfo fieldInfo) + :base(oodb,fieldInfo) + { + } + + protected override void Prepare() + { + } + + public override void Add(Persistent persistent) + { + object value = FieldInfo.GetValue(persistent); + Guid persistenceId = persistent.PersistenceID; + + if (uniqueReverse.ContainsKey(value)) + { + if (uniqueReverse[value].Equals(persistenceId)) + return; + + throw new ArgumentException(); + } + + if (uniqueForward.ContainsKey(persistenceId)) + Remove(persistenceId); + + uniqueForward.Add(persistenceId, value); + uniqueReverse.Add(value, persistenceId); + } + + public override IEnumerable Query() + { + throw new NotImplementedException(); + } + + public override void Remove(Guid persistenceID) + { + if (uniqueForward.ContainsKey(persistenceID)) + { + uniqueReverse.Remove(uniqueForward[persistenceID]); + uniqueForward.Remove(persistenceID); + } + } + + + public override void RestoreIndexValues(IEnumerable> indexValues) + { + uniqueForward.Clear(); + uniqueReverse.Clear(); + + foreach (KeyValuePair entry in indexValues) + { + uniqueForward.Add(entry.Key, entry.Value); + uniqueReverse.Add(entry.Value, entry.Key); + } + + } + public override IEnumerable> RetrieveIndexValues() + { + return uniqueForward; + } + } +} diff --git a/mapping/ArrayMapping.cs b/mapping/ArrayMapping.cs new file mode 100644 index 0000000..3bd095f --- /dev/null +++ b/mapping/ArrayMapping.cs @@ -0,0 +1,32 @@ +using System; +using System.Xml; +using System.Collections.Generic; +using System.Collections; + +namespace oodb.mapping +{ + public class ArrayMapping : ListMapping + { + public ArrayMapping(OODB oodb,Type type) + :base(oodb,typeof(List<>).MakeGenericType(type.GetElementType())) + { + } + + public override XmlElement ToXml(XmlDocument xmlDocument, object value) + { + IList list = value as IList; + Array array = Array.CreateInstance(ElementMapping.NativeType, list.Count); + (list as IList).CopyTo(array, 0); + + return base.ToXml(xmlDocument,list); + } + public override object FromXml(XmlElement xmlValue) + { + IList list = base.FromXml(xmlValue) as IList; + Array array = Array.CreateInstance(ElementMapping.NativeType, list.Count) as Array; + list.CopyTo(array, list.Count); + return array; + + } + } +} diff --git a/mapping/DoubleMapping.cs b/mapping/DoubleMapping.cs new file mode 100644 index 0000000..44bcbbe --- /dev/null +++ b/mapping/DoubleMapping.cs @@ -0,0 +1,32 @@ +using System; +using System.Xml; +using System.Globalization; + +namespace oodb.mapping +{ + public class DoubleMapping : NaiveMapping + { + public DoubleMapping(OODB oodb) + : base(oodb, typeof(double)) + { + } + + public override object FromText(string text) + { + double iValue = double.Parse(text, CultureInfo.InvariantCulture); + if (NativeType != typeof(double)) + { + return Convert.ChangeType(iValue, NativeType); + } + return iValue; + } + + public override string ToText(object value) + { + double iValue = (double)value; + return iValue.ToString(CultureInfo.InvariantCulture); + } + } + +} + diff --git a/mapping/ExtendedFieldHandling.cs b/mapping/ExtendedFieldHandling.cs new file mode 100644 index 0000000..06e1d02 --- /dev/null +++ b/mapping/ExtendedFieldHandling.cs @@ -0,0 +1,25 @@ +using System; +using System.Reflection; +using System.Xml; +namespace oodb.mapping +{ + public abstract class ExtendedFieldHandling + { + public OODB OODB { get; } + public FieldInfo FieldInfo { get; } + + public ExtendedFieldHandling(OODB oodb,FieldInfo fieldInfo) + { + OODB = oodb; + FieldInfo = fieldInfo; + } + + public virtual void Initialize(){} + + public abstract void Save(XmlElement persistenceElement,Persistent persistent); + public abstract void Load(XmlElement persistenceElement,Persistent persistent); + + public abstract void Attach(Persistent persistent); + public abstract void Detach(Persistent persistent); + } +} diff --git a/mapping/FlatTypeMapping.cs b/mapping/FlatTypeMapping.cs new file mode 100644 index 0000000..9317a72 --- /dev/null +++ b/mapping/FlatTypeMapping.cs @@ -0,0 +1,57 @@ +using System; +using System.Xml; + +namespace oodb.mapping +{ + public abstract class FlatTypeMapping + { + public OODB OODB { get; } + public Type NativeType { get; } + + public FlatTypeMapping(OODB oodb, Type nativeType) + { + OODB = oodb; + NativeType = nativeType; + } + + public XmlElement CreateEmptyValueElement(XmlDocument xmlDocument) + { + XmlElement value = xmlDocument.CreateElement("Value"); + value.SetAttribute("Type", NativeType.FullName); + return value; + } + + public abstract XmlElement ToXml(XmlDocument xmlDocument, object value); + public abstract object FromXml(XmlElement xmlValue); + + public abstract String ToText(object value); + public abstract object FromText(string text); + } + + public abstract class NaiveMapping : FlatTypeMapping + { + public NaiveMapping(OODB oodb, Type nativeType) + :base(oodb,nativeType) + { + } + + public override object FromXml(XmlElement xmlValue) + { + if (!NativeType.FullName.Equals(xmlValue.GetAttribute("Type"))) + throw new ArgumentException("Type != NativeType", nameof(xmlValue)); + + return FromText(xmlValue.InnerText); + } + + public override XmlElement ToXml(XmlDocument xmlDocument, object value) + { + XmlElement valueElement = CreateEmptyValueElement(xmlDocument); + if (value != null) + valueElement.InnerText = ToText(value); + + return valueElement; + } + } + +} + diff --git a/mapping/IntegerMapping.cs b/mapping/IntegerMapping.cs new file mode 100644 index 0000000..7e78338 --- /dev/null +++ b/mapping/IntegerMapping.cs @@ -0,0 +1,32 @@ +using System; +using System.Xml; +using System.Globalization; + +namespace oodb.mapping +{ + public class IntegerMapping : NaiveMapping + { + public IntegerMapping(OODB oodb) + : base(oodb, typeof(int)) + { + } + + public override object FromText(String text) + { + int iValue = int.Parse(text, CultureInfo.InvariantCulture); + if (NativeType != typeof(int)) + { + return Convert.ChangeType(iValue, NativeType); + } + return iValue; + } + + public override string ToText(object value) + { + int iValue = (int)value; + return iValue.ToString(CultureInfo.InvariantCulture); + } + } + +} + diff --git a/mapping/ListMapping.cs b/mapping/ListMapping.cs new file mode 100644 index 0000000..2ad6f65 --- /dev/null +++ b/mapping/ListMapping.cs @@ -0,0 +1,61 @@ +using System; +using System.Xml; +using System.Collections; +using System.Collections.Generic; + +namespace oodb.mapping +{ + public class ListMapping : NaiveMapping + { + public FlatTypeMapping ElementMapping { get; } + + public ListMapping(OODB oodb, Type type) + : base(oodb, type) + { + ElementMapping = OODB.GetFlatTypeMapping(type.GetGenericArguments()[0]); + } + + public override object FromXml(XmlElement xmlValue) + { + if (!NativeType.FullName.Equals(xmlValue.GetAttribute("Type"))) + throw new ArgumentException("Type != NativeType", nameof(xmlValue)); + + IList list = Activator.CreateInstance(typeof(List<>).MakeGenericType(ElementMapping.NativeType)) as IList; + foreach (XmlNode elementNode in xmlValue.ChildNodes) + { + if ((elementNode is XmlElement) && ((((XmlElement)elementNode).Name.Equals("Value")))) + { + list.Add(ElementMapping.FromXml(elementNode as XmlElement)); + } + } + + return list; + } + + public override XmlElement ToXml(XmlDocument xmlDocument, object value) + { + XmlElement valueElement = CreateEmptyValueElement(xmlDocument); + + IList list = value as IList; + + for (int n = 0; n < list.Count; n++) + { + valueElement.AppendChild(ElementMapping.ToXml(xmlDocument, list[n])); + } + + return valueElement; + } + + public override string ToText(object value) + { + throw new NotImplementedException(); + } + + public override object FromText(string text) + { + throw new NotImplementedException(); + } + } + +} + diff --git a/mapping/ReferenceMapping.cs b/mapping/ReferenceMapping.cs new file mode 100644 index 0000000..a4ff285 --- /dev/null +++ b/mapping/ReferenceMapping.cs @@ -0,0 +1,37 @@ +using System; +using System.Xml; + +namespace oodb.mapping +{ + public class ReferenceMapping : NaiveMapping + { + public ReferenceMapping(OODB oodb, Type type) + : base(oodb, type) + { + } + + public override object FromText(string text) + { + if (String.Empty.Equals(text)) + return null; + + Guid persistenceID = Guid.Parse(text); + return OODB.Load(NativeType, persistenceID); + } + public override string ToText(object value) + { + Persistent reference = value as Persistent; + + if (reference == null) + { + return Guid.Empty.ToString(); + } + else + { + string text = reference.PersistenceID.ToString(); + OODB.Ensure(reference); + return text; + } + } + } +} diff --git a/mapping/StringMapping.cs b/mapping/StringMapping.cs new file mode 100644 index 0000000..8551136 --- /dev/null +++ b/mapping/StringMapping.cs @@ -0,0 +1,27 @@ +using System; +using System.Xml; + +namespace oodb.mapping +{ + public class StringMapping : NaiveMapping + { + public StringMapping(OODB oodb) + : base(oodb, typeof(string)) + { + + } + + public override object FromText(string text) + { + return text; + } + + public override string ToText(object value) + { + return value as string; + } + + } + +} + diff --git a/mapping/collections/ISetExtendedHandling.cs b/mapping/collections/ISetExtendedHandling.cs new file mode 100644 index 0000000..cc613fb --- /dev/null +++ b/mapping/collections/ISetExtendedHandling.cs @@ -0,0 +1,56 @@ +using System; +using System.Reflection; +using System.Xml; +using oodb.attributes; + +namespace oodb.mapping.collections +{ + public class ISetExtendedHandling : ExtendedFieldHandling + { + public Type ReferencedType { get; } + public FieldInfo ReferencedField { get; protected set; } + + public Type ImplementationType { get; } + + public ISetExtendedHandling(OODB oodb,FieldInfo fieldInfo) + :base(oodb,fieldInfo) + { + ReferencedType = fieldInfo.FieldType.GetGenericArguments()[0]; + ImplementationType = typeof(ISetImplementation<>).MakeGenericType(ReferencedType); + } + + public override void Initialize() + { + ReferencedFieldAttribute referencedFieldAttribute = FieldInfo.GetCustomAttribute(); + string refFieldName = referencedFieldAttribute != null ? referencedFieldAttribute.FieldName : FieldInfo.DeclaringType.Name; + ReferencedField = ReferencedType.GetField(refFieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + + OODB.StorageAdapter.GetSearchIndex(ReferencedField); + } + + public override void Attach(Persistent persistent) + { + object iset = FieldInfo.GetValue(persistent); + + if (ImplementationType != iset.GetType()) + { + object impl = Activator.CreateInstance(ImplementationType,OODB,FieldInfo,persistent); + FieldInfo.SetValue(persistent, impl); + } + + } + + public override void Detach(Persistent persistent) + { + } + + public override void Load(XmlElement persistenceElement, Persistent persistent) + { + Attach(persistent); + } + + public override void Save(XmlElement persistenceElement, Persistent persistent) + { + } + } +} diff --git a/mapping/collections/ISetImplementation.cs b/mapping/collections/ISetImplementation.cs new file mode 100644 index 0000000..8801f5f --- /dev/null +++ b/mapping/collections/ISetImplementation.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using oodb.attributes; +using oodb.index; +using System.Linq; + +namespace oodb.mapping.collections +{ + public class ISetImplementation : ISet where T: Persistent + { + public OODB OODB { get; } + public FieldInfo FieldInfo { get; } + + public Persistent Owner { get; } + + public FieldInfo ReferencedField { get; } + public Type ReferencedType { get; } + + public SearchIndex SearchIndex { get; } + + public ISetImplementation(OODB oodb,FieldInfo fieldInfo,Persistent owner) + { + OODB = oodb; + FieldInfo = fieldInfo; + Owner = owner; + + ReferencedType = fieldInfo.FieldType.GetGenericArguments()[0]; + + ReferencedFieldAttribute referencedFieldAttribute = FieldInfo.GetCustomAttribute(); + string refFieldName = referencedFieldAttribute != null ? referencedFieldAttribute.FieldName : FieldInfo.DeclaringType.Name; + ReferencedField = ReferencedType.GetField(refFieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + + SearchIndex = OODB.StorageAdapter.GetSearchIndex(ReferencedField); + } + + public IEnumerable ElementPersistenceIDs => SearchIndex.QueryTextValue(Owner.PersistenceID.ToString()); + + public int Count => ElementPersistenceIDs.Count(); + public bool IsReadOnly => false; + + public bool Add(T item) + { + if (!Contains(item)) + { + ReferencedField.SetValue(item, Owner); + OODB.Save(item); + return true; + } + return false; + } + + public bool Remove(T item) + { + if (Contains(item)) + { + ReferencedField.SetValue(item, null); + OODB.Save(item); + return true; + } + return false; + } + + public void Clear() + { + foreach (T item in this) + Remove(item); + } + + public bool Contains(T item) + { + Persistent persistentItem = item as Persistent; + foreach (Guid elementID in ElementPersistenceIDs) + if (elementID.Equals(persistentItem.PersistenceID)) + return true; + return false; + } + + public void CopyTo(T[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public void ExceptWith(IEnumerable other) + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + return ElementPersistenceIDs.Select((id) => OODB.Load(id)).GetEnumerator(); + } + + public void IntersectWith(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool IsProperSubsetOf(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool IsProperSupersetOf(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool IsSubsetOf(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool IsSupersetOf(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool Overlaps(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool SetEquals(IEnumerable other) + { + throw new NotImplementedException(); + } + + public void SymmetricExceptWith(IEnumerable other) + { + throw new NotImplementedException(); + } + + public void UnionWith(IEnumerable other) + { + throw new NotImplementedException(); + } + + void ICollection.Add(T item) + { + Add(item); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/persistence/PersistenceCache.cs b/persistence/PersistenceCache.cs new file mode 100644 index 0000000..306cbc8 --- /dev/null +++ b/persistence/PersistenceCache.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using oodb.descriptor; +using System.Dynamic; +namespace oodb.persistence +{ + public class PersistenceCache + { + public OODB OODB { get; } + + + Dictionary typeCaches = new Dictionary(); + + + public PersistenceCache(OODB oodb) + { + OODB = oodb; + } + + private TypeCache GetTypeCache(Type type) + { + if (!typeCaches.ContainsKey(type)) + typeCaches[type] = new TypeCache(this,type); + return typeCaches[type]; + } + + public Persistent Get(Type type,Guid persistenceID) + { + return GetTypeCache(type).Get(persistenceID); + } + + public void Touch(Persistent persistent) + { + GetTypeCache(persistent.GetType()).Touch(persistent); + } + + public void Remove(Persistent persistent) + { + GetTypeCache(persistent.GetType()).Remove(persistent); + } + + + + class TypeCache + { + PersistenceCache PersistenceCache { get; } + TypeCache BaseCache { get; } + + Type CacheType { get; } + + Descriptor Descriptor { get; } + + Dictionary cache = new Dictionary(); + + public TypeCache(PersistenceCache persistenceCache,Type type) + { + PersistenceCache = persistenceCache; + CacheType = type; + Descriptor = persistenceCache.OODB.GetDescriptor(type); + + if (Descriptor.BaseDescriptor != null) + BaseCache = persistenceCache.GetTypeCache(Descriptor.BaseDescriptor.NativeType); + } + + public Persistent Get(Guid persistenceID) + { + WeakReference weakReference = cache[persistenceID]; + if (!weakReference.IsAlive) + { + Remove(persistenceID); + throw new KeyNotFoundException(persistenceID.ToString()); + } + return weakReference.Target as Persistent; + } + + public void Touch(Persistent persistent) + { + cache[persistent.PersistenceID] = new WeakReference(persistent); + if (Descriptor.BaseDescriptor != null) + PersistenceCache.GetTypeCache(Descriptor.BaseDescriptor.NativeType).Touch(persistent); + } + + public void Remove(Persistent persistent) + { + Remove(persistent.PersistenceID); + } + public void Remove(Guid persistenceID) + { + cache.Remove(persistenceID); + if (Descriptor.BaseDescriptor != null) + { + PersistenceCache.GetTypeCache(Descriptor.BaseDescriptor.NativeType).Remove(persistenceID); + } + } + } + + + } +} diff --git a/sharp-oodb.csproj b/sharp-oodb.csproj new file mode 100644 index 0000000..68fe009 --- /dev/null +++ b/sharp-oodb.csproj @@ -0,0 +1,75 @@ + + + + Debug + x86 + {FF9FA8D2-5E89-4609-83F0-B9512DF777BE} + Library + oodb + sharp-oodb + v4.6 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + + + true + bin\Release + prompt + 4 + true + x86 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {D471A566-9FB6-41B2-A777-3C32874ECD0E} + sharp.logging + + + + \ No newline at end of file