Reworked odb.ng namespace, no Dependencies left to odb, Session management

dev_timestamp
Harald Wolff 2019-08-27 23:40:27 +02:00
parent 5dcb7f60ec
commit 1a7d172ba8
23 changed files with 1445 additions and 441 deletions

2
.gitignore vendored
View File

@ -39,3 +39,5 @@ Thumbs.db
# dotCover
*.dotCover
ln.logging

View File

@ -53,7 +53,6 @@
<Compile Include="odb\PreparedObject.cs" />
<Compile Include="odb\ODBObjectWriter.cs" />
<Compile Include="odb\ODBFileStorage.cs" />
<Compile Include="odb\Storage.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="GeoLocation.cs" />
<Compile Include="URI.cs" />
@ -121,6 +120,15 @@
<Compile Include="odb\ng\Query.cs" />
<Compile Include="odb\ng\IStorage.cs" />
<Compile Include="odb\ng\Document.cs" />
<Compile Include="odb\ng\IStorageContainer.cs" />
<Compile Include="odb\ng\storage\OrganizedFile.cs" />
<Compile Include="odb\ng\storage\OrganizedFileType.cs" />
<Compile Include="odb\ng\storage\FSStorageContainer.cs" />
<Compile Include="odb\ng\storage\FSStorage.cs" />
<Compile Include="odb\ng\storage\StorageAreaContainer.cs" />
<Compile Include="odb\ng\storage\StorageArea.cs" />
<Compile Include="odb\ng\Session.cs" />
<Compile Include="odb\ng\ODBMapper.API.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="sync\" />
@ -143,7 +151,7 @@
<Folder Include="stream\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ln.logging\ln.logging.csproj">
<ProjectReference Include="ln.logging\ln.logging.csproj">
<Project>{D471A566-9FB6-41B2-A777-3C32874ECD0E}</Project>
<Name>ln.logging</Name>
</ProjectReference>

29
ln.types.sln 100644
View File

@ -0,0 +1,29 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.types", "ln.types.csproj", "{8D9AB9A5-E513-4BA7-A450-534F6456BF28}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.types.test", "ln.types.test\ln.types.test.csproj", "{E39D8B11-7CF6-4C78-B723-F7E100121704}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.logging", "ln.logging\ln.logging.csproj", "{D471A566-9FB6-41B2-A777-3C32874ECD0E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8D9AB9A5-E513-4BA7-A450-534F6456BF28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D9AB9A5-E513-4BA7-A450-534F6456BF28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D9AB9A5-E513-4BA7-A450-534F6456BF28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D9AB9A5-E513-4BA7-A450-534F6456BF28}.Release|Any CPU.Build.0 = Release|Any CPU
{E39D8B11-7CF6-4C78-B723-F7E100121704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E39D8B11-7CF6-4C78-B723-F7E100121704}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E39D8B11-7CF6-4C78-B723-F7E100121704}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E39D8B11-7CF6-4C78-B723-F7E100121704}.Release|Any CPU.Build.0 = Release|Any CPU
{D471A566-9FB6-41B2-A777-3C32874ECD0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D471A566-9FB6-41B2-A777-3C32874ECD0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D471A566-9FB6-41B2-A777-3C32874ECD0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D471A566-9FB6-41B2-A777-3C32874ECD0E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,90 @@
// /**
// * File: Program.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using ln.types.odb.ng.storage;
using ln.types.odb.ng;
using ln.logging;
using System.Linq;
namespace ln.types.test
{
class MainClass
{
public static void Main(string[] args)
{
Logger.ConsoleLogger.MaxLogLevel = LogLevel.DEBUGDETAIL;
Logging.Log(LogLevel.DEBUG, "ln.types.odb.ng test suite");
using (IStorageContainer container = new Session(new FSStorageContainer(".")))
{
container.Open();
IStorage storage = container.GetStorage("test");
if (!storage.IsOpen)
storage.Open();
foreach (Guid documentID in storage.GetDocumentIDs())
{
Logging.Log(LogLevel.INFO, "DocumentID: {0}", documentID);
Document doc = storage.Load(documentID);
Logging.Log(LogLevel.INFO, doc.ToString());
doc["FeldC"] = doc["FeldC"].AsLong + 1;
storage.Save(doc);
}
Document document = new Document();
document["FeldA"] = "Ich bin das Feld A";
document["FeldB"] = "Ich bin das Feld B";
document["FeldC"] = new Random().Next();
storage.Save(document);
storage.Save(document);
ODBMapper mapper = new ODBMapper(container);
foreach (MapableClass mapableClass in mapper.GetDocumentIDs<MapableClass>().Select((id)=>mapper.Load<MapableClass>(id)))
{
Logging.Log(LogLevel.INFO, "mapper found: {0}",mapableClass);
mapableClass.ANumber++;
mapper.Save(mapableClass);
}
MapableClass mapable = new MapableClass();
mapper.Save<MapableClass>(mapable);
storage.Close();
container.Close();
}
}
}
class MapableClass
{
public int ANumber { get; set; }
public String MyString;
public MapableClass()
{
ANumber = new Random().Next();
MyString = ANumber.ToString();
}
public override string ToString()
{
return String.Format("[MapableClass ANumber={0} MyString={1}]",ANumber,MyString);
}
}
}

View File

@ -0,0 +1,35 @@
// /**
// * File: AssemblyInfo.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
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("ln.types.test")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[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("")]

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E39D8B11-7CF6-4C78-B723-F7E100121704}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>ln.types.test</RootNamespace>
<AssemblyName>ln.types.test</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ExternalConsole>true</ExternalConsole>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ExternalConsole>true</ExternalConsole>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ln.types.csproj">
<Project>{8D9AB9A5-E513-4BA7-A450-534F6456BF28}</Project>
<Name>ln.types</Name>
</ProjectReference>
<ProjectReference Include="..\ln.logging\ln.logging.csproj">
<Project>{D471A566-9FB6-41B2-A777-3C32874ECD0E}</Project>
<Name>ln.logging</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -1,19 +0,0 @@
using System;
namespace ln.types.odb
{
//public abstract class Storage
//{
// public Storage()
// {
// }
// public abstract bool Contains(Guid persistenceID);
// public abstract bool Store(PreparedObject preparedObject);
// public abstract bool Load(PreparedObject preparedObject);
//}
}

View File

@ -10,13 +10,19 @@ namespace ln.types.odb.ng
private Dictionary<ODBValue, ODBValue> properties = new Dictionary<ODBValue, ODBValue>();
public Document()
:base(0x1000)
:base(0x1001)
{
ID = Guid.NewGuid();
ID = Guid.NewGuid();
}
public Guid ID { get; }
public DateTime StorageTimeStamp { get; private set; }
public Document(Guid id)
:base(0x1001)
{
ID = id;
}
public Guid ID { get; }
public DateTime StorageTimeStamp { get; set; }
public ODBCollection Collection { get; internal set; }
@ -24,7 +30,7 @@ namespace ln.types.odb.ng
: this(documentID, bytes, 0, bytes.Length)
{ }
public Document(Guid documentID,byte[] bytes,int offset,int length)
:this()
:this(documentID)
{
ID = documentID;
@ -53,9 +59,10 @@ namespace ln.types.odb.ng
}
set
{
if (ODBNull.Instance.Equals(value) && properties.ContainsKey(propName))
if (ODBNull.Instance.Equals(value))
{
properties.Remove(propName);
if (properties.ContainsKey(propName))
properties.Remove(propName);
}
else
{
@ -73,11 +80,22 @@ namespace ln.types.odb.ng
return !ODBNull.Instance.Equals(this[propName]);
}
public override byte[] ToStorage()
public override ODBValue Clone()
{
Document clone = new Document(ID);
foreach (ODBValue fieldName in properties.Keys)
{
clone[fieldName] = this[fieldName].Clone();
}
return clone;
}
public override byte[] ToStorage()
{
MemoryStream stream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(ID.ToByteArray());
writer.Write(properties.Count);
foreach (ODBValue propName in properties.Keys)
@ -101,15 +119,15 @@ namespace ln.types.odb.ng
}
public override bool Equals(object obj)
{
if (obj is ODBDocument)
if (obj is Document)
{
ODBDocument you = obj as ODBDocument;
Document you = obj as Document;
return ID.Equals(you.ID);
}
return false;
}
public bool ContentEquals(ODBDocument other)
public bool ContentEquals(Document other)
{
if (object.ReferenceEquals(null, other))
return false;
@ -127,10 +145,10 @@ namespace ln.types.odb.ng
public override bool ValueEquals(ODBValue other)
{
return ContentEquals(other as ODBDocument);
return ContentEquals(other as Document);
}
public int CompareContent(ODBDocument other)
public int CompareContent(Document other)
{
ODBValue[] keys = Keys.Union(other.Keys).ToArray();
@ -149,11 +167,11 @@ namespace ln.types.odb.ng
public override int CompareInType(ODBValue other)
{
return CompareContent(other as ODBDocument);
return CompareContent(other as Document);
}
public override int CompareValueInType(ODBValue other)
{
return CompareContent(other as ODBDocument);
return CompareContent(other as Document);
}
static Document()

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
namespace ln.types.odb.ng
{
public interface IStorage
@ -8,8 +9,8 @@ namespace ln.types.odb.ng
void Close();
bool IsOpen { get; }
ODBDocument Load(Guid documentID);
ODBDocument Save(ODBDocument document);
Document Load(Guid documentID);
void Save(Document document);
IEnumerable<Guid> GetDocumentIDs();
}

View File

@ -0,0 +1,23 @@
// /**
// * File: IStorageContainer.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using System.Collections.Generic;
namespace ln.types.odb.ng
{
public interface IStorageContainer : IDisposable
{
void Open();
void Close();
bool IsOpen { get; }
IStorage GetStorage(string storageName);
IEnumerable<String> GetStorageNames();
}
}

View File

@ -0,0 +1,88 @@
using ln.types.btree;
using System.Collections.Generic;
using System;
namespace ln.types.odb.ng
{
public partial class ODBMapper
{
BTree<Guid, CachedObject> forwardCache = new BTree<Guid, CachedObject>();
Dictionary<object, CachedObject> reverseCache = new Dictionary<object, CachedObject>();
public T Load<T>(Guid documentID) => (T)Load(typeof(T), documentID);
public object Load(Type type,Guid documentID)
{
lock (this)
{
if (forwardCache.ContainsKey(documentID))
return forwardCache[documentID].Instance;
IStorage storage = StorageContainer.GetStorage(type.FullName);
Document document = storage.Load(documentID);
object instance = ObjectMapping.UnmapValue(this, document);
CachedObject cachedObject = new CachedObject(document, instance);
forwardCache.Add(cachedObject.Document.ID,cachedObject);
reverseCache.Add(cachedObject.Instance, cachedObject);
return cachedObject.Instance;
}
}
public void Save<T>(T instance) => Save(typeof(T), instance);
public void Save(Type type,object instance)
{
lock (this)
{
IStorage storage = StorageContainer.GetStorage(type.FullName);
CachedObject cachedObject;
Document document;
if (reverseCache.ContainsKey(instance))
{
cachedObject = reverseCache[instance];
document = (GetMapping(type) as mappings.ClassMapping).MapDocument(this, cachedObject.Document.ID, instance);
storage.Save(document);
cachedObject.Document = document;
}
else
{
document = (GetMapping(type) as mappings.ClassMapping).MapDocument(this, Guid.NewGuid() ,instance) as Document;
cachedObject = new CachedObject(document, instance);
storage.Save(document);
forwardCache.Add(cachedObject.Document.ID, cachedObject);
reverseCache.Add(instance, cachedObject);
}
}
}
public IEnumerable<Guid> GetDocumentIDs<T>() => GetDocumentIDs(typeof(T));
public IEnumerable<Guid> GetDocumentIDs(Type type)
{
IStorage storage = StorageContainer.GetStorage(type.FullName);
return storage.GetDocumentIDs();
}
struct CachedObject
{
public object Instance;
public Document Document;
public CachedObject(Document document, object instance)
{
Document = document;
Instance = instance;
}
}
}
}

View File

@ -5,6 +5,9 @@ using System.Reflection;
using System.Linq;
using System.Collections;
using ln.types.net;
using ln.types.odb.ng.storage;
using ln.types.btree;
using ln.types.odb.ng.mappings;
namespace ln.types.odb.ng
{
@ -13,24 +16,24 @@ namespace ln.types.odb.ng
public delegate ODBValue ODBMap<T>(ODBMapper mapper, T value);
public delegate T ODBUnmap<T>(ODBMapper mapper, ODBValue oval);
public class ODBMapper
public partial class ODBMapper
{
public static ODBMapper Default { get; set; }
public ODB ODB { get; private set; }
Dictionary<string, ObjectCollection> objectCollections = new Dictionary<string, ObjectCollection>();
public IStorageContainer StorageContainer { get; private set; }
Dictionary<Type, IODBMapping> mappings = new Dictionary<Type, IODBMapping>();
mappings.ObjectMapping ObjectMapping { get; }
public ODBMapper(string odbPath)
:this(new ODB(odbPath))
public ODBMapper(string basePath)
:this(new FSStorageContainer(basePath))
{ }
public ODBMapper(ODB odb)
public ODBMapper(IStorageContainer storageContainer)
{
if (Default == null)
Default = this;
this.ODB = odb;
this.StorageContainer = storageContainer;
RegisterMapping<string>(
(mapper, value) => new ODBStringValue(value),
@ -104,10 +107,11 @@ namespace ln.types.odb.ng
(mapper, oval) => new IPv4(oval.AsUInt)
);
RegisterMapping(typeof(object),new mappings.ObjectMapping());
ObjectMapping = new mappings.ObjectMapping();
RegisterMapping(typeof(object),ObjectMapping);
}
public void RegisterMapping(Type nativeType, IODBMapping mapping)
public void RegisterMapping(Type nativeType, IODBMapping mapping)
{
mappings[nativeType] = mapping;
}
@ -123,20 +127,10 @@ namespace ln.types.odb.ng
);
}
public ObjectCollection GetCollection(Type type) => GetCollection(type, type.FullName);
public ObjectCollection GetCollection(Type type,string typeName)
public IStorage GetStorage(Type type) => GetStorage(type, type.FullName);
public IStorage GetStorage(Type type,string typeName)
{
if (!objectCollections.ContainsKey(typeName))
{
ObjectCollection objectCollection = Activator.CreateInstance(typeof(ObjectCollection<>).MakeGenericType(type),this) as ObjectCollection;
objectCollections[typeName] = objectCollection;
}
return objectCollections[typeName];
}
public ObjectCollection<T> GetCollection<T>() where T : class => GetCollection<T>(typeof(T).FullName);
public ObjectCollection<T> GetCollection<T>(string aliasName) where T:class
{
return GetCollection(typeof(T),aliasName) as ObjectCollection<T>;
return StorageContainer.GetStorage(typeName);
}
public IODBMapping GetMapping<T>() => null;
@ -184,26 +178,29 @@ namespace ln.types.odb.ng
}
}
public object GetDocumentID(object o)
{
IODBMapping mapping = GetMapping(o.GetType());
if (mapping is mappings.ClassMapping)
{
mappings.ClassMapping classMapping = mapping as mappings.ClassMapping;
return classMapping.getID(o);
}
return null;
}
public Type GetDocumentIDType(Type type)
{
IODBMapping mapping = GetMapping(type);
if (mapping is mappings.ClassMapping)
{
mappings.ClassMapping classMapping = mapping as mappings.ClassMapping;
return classMapping.IDType;
}
return null;
}
//public object GetDocumentID(object o)
//{
// IODBMapping mapping = GetMapping(o.GetType());
// if (mapping is mappings.ClassMapping)
// {
// mappings.ClassMapping classMapping = mapping as mappings.ClassMapping;
// return classMapping.getID(o);
// }
// return null;
//}
//public Type GetDocumentIDType(Type type)
//{
// IODBMapping mapping = GetMapping(type);
// if (mapping is mappings.ClassMapping)
// {
// mappings.ClassMapping classMapping = mapping as mappings.ClassMapping;
// return classMapping.IDType;
// }
// return null;
//}
public virtual ODBValue MapValue(object value)
{
@ -221,9 +218,9 @@ namespace ln.types.odb.ng
if (ODBNull.Instance.Equals(value))
return null;
if (value is ODBDocument)
if (value is Document)
{
ODBDocument doc = value as ODBDocument;
Document doc = value as Document;
String asmname = doc["__asm__"].AsString;
String typename = doc["__type__"].AsString;
@ -270,6 +267,5 @@ namespace ln.types.odb.ng
return null;
return string.Format("{0}, {1}",type.FullName,type.Assembly.GetName().Name);
}
}
}
}

View File

@ -6,303 +6,303 @@ using System.Collections.Generic;
using System.Linq;
namespace ln.types.odb.ng
{
public class ObjectCollection : IEnumerable
{
public ODBMapper ODBMapper { get; }
public ODBCollection DocumentCollection { get; private set; }
//public class ObjectCollection : IEnumerable
//{
// public ODBMapper ODBMapper { get; }
// public ODBCollection DocumentCollection { get; private set; }
public Type ElementType { get; }
public String CollectionName => DocumentCollection.CollectionName;
// public Type ElementType { get; }
// public String CollectionName => DocumentCollection.CollectionName;
public int Count => DocumentCollection.Count;
// public int Count => DocumentCollection.Count;
public Type IDType => ODBMapper.GetDocumentIDType(ElementType);
// public Type IDType => ODBMapper.GetDocumentIDType(ElementType);
internal ObjectCollection(ODBMapper odbmapper, Type elementType)
: this(odbmapper, elementType, elementType.FullName)
{ }
internal ObjectCollection(ODBMapper odbmapper, Type elementType, String collectionAlias)
{
ODBMapper = odbmapper;
ElementType = elementType;
DocumentCollection = ODBMapper.ODB.GetCollection(elementType.FullName);
}
// internal ObjectCollection(ODBMapper odbmapper, Type elementType)
// : this(odbmapper, elementType, elementType.FullName)
// { }
// internal ObjectCollection(ODBMapper odbmapper, Type elementType, String collectionAlias)
// {
// ODBMapper = odbmapper;
// ElementType = elementType;
// DocumentCollection = ODBMapper.StorageContainer.GetCollection(elementType.FullName);
// }
public object SelectByID(object ID)
{
ODBValue documentID = ODBMapper.MapValue(ID);
return SelectByID(documentID);
}
public object SelectByID(ODBValue documentID)
{
if (ODBNull.Instance.Equals(documentID))
return null;
lock (this)
{
object o = GetCachedObject(documentID);
if (object.ReferenceEquals(null, o))
{
ODBDocument document = DocumentCollection.GetDocumentByID(documentID);
o = ODBMapper.UnmapValue(ElementType, document);
TouchCache(documentID, o);
}
return o;
}
}
// public object SelectByID(object ID)
// {
// ODBValue documentID = ODBMapper.MapValue(ID);
// return SelectByID(documentID);
// }
// public object SelectByID(ODBValue documentID)
// {
// if (ODBNull.Instance.Equals(documentID))
// return null;
// lock (this)
// {
// object o = GetCachedObject(documentID);
// if (object.ReferenceEquals(null, o))
// {
// ODBDocument document = DocumentCollection.GetDocumentByID(documentID);
// o = ODBMapper.UnmapValue(ElementType, document);
// TouchCache(documentID, o);
// }
// return o;
// }
// }
public IEnumerable Select(Query query)
{
lock (this)
{
return new ObjectEnumeration(this, query.Execute(DocumentCollection).ToArray());
}
}
// public IEnumerable Select(Query query)
// {
// lock (this)
// {
// return new ObjectEnumeration(this, query.Execute(DocumentCollection).ToArray());
// }
// }
public bool Ensure(object o)
{
if (!ElementType.IsInstanceOfType(o))
throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o));
// public bool Ensure(object o)
// {
// if (!ElementType.IsInstanceOfType(o))
// throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o));
lock (this)
{
ODBDocument document = ODBMapper.MapValue(o) as ODBDocument;
if (DocumentCollection.Ensure(document))
{
TouchCache(document.ID, o);
return true;
}
return false;
}
}
// lock (this)
// {
// ODBDocument document = ODBMapper.MapValue(o) as ODBDocument;
// if (DocumentCollection.Ensure(document))
// {
// TouchCache(document.ID, o);
// return true;
// }
// return false;
// }
// }
public bool Insert(object o)
{
lock (this)
{
if (!ElementType.IsInstanceOfType(o))
throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o));
// public bool Insert(object o)
// {
// lock (this)
// {
// if (!ElementType.IsInstanceOfType(o))
// throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o));
ODBDocument document = ODBMapper.MapValue(o) as ODBDocument;
if (DocumentCollection.Insert(document))
{
TouchCache(document.ID, o);
return true;
}
return false;
}
}
public bool Update(object o)
{
lock (this)
{
lock (this)
{
if (!ElementType.IsInstanceOfType(o))
throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o));
// ODBDocument document = ODBMapper.MapValue(o) as ODBDocument;
// if (DocumentCollection.Insert(document))
// {
// TouchCache(document.ID, o);
// return true;
// }
// return false;
// }
// }
// public bool Update(object o)
// {
// lock (this)
// {
// lock (this)
// {
// if (!ElementType.IsInstanceOfType(o))
// throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o));
ODBDocument document = ODBMapper.MapValue(o) as ODBDocument;
if (DocumentCollection.Update(document))
{
TouchCache(document.ID, o);
return true;
}
return false;
}
}
}
public bool Upsert(object o)
{
if (!ElementType.IsInstanceOfType(o))
throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o));
// ODBDocument document = ODBMapper.MapValue(o) as ODBDocument;
// if (DocumentCollection.Update(document))
// {
// TouchCache(document.ID, o);
// return true;
// }
// return false;
// }
// }
// }
// public bool Upsert(object o)
// {
// if (!ElementType.IsInstanceOfType(o))
// throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o));
lock (this)
{
ODBDocument document = ODBMapper.MapValue(o) as ODBDocument;
if (DocumentCollection.Upsert(document))
{
TouchCache(document.ID, o);
return true;
}
return false;
}
}
// lock (this)
// {
// ODBDocument document = ODBMapper.MapValue(o) as ODBDocument;
// if (DocumentCollection.Upsert(document))
// {
// TouchCache(document.ID, o);
// return true;
// }
// return false;
// }
// }
public bool Delete(object o) => Delete(ODBMapper.MapValue(ODBMapper.GetDocumentID(o)));
public bool Delete(ODBValue documentID)
{
lock (this)
{
if (DocumentCollection.Delete(documentID))
{
if (objectCache.ContainsKey(documentID))
objectCache.Remove(documentID);
return true;
}
return false;
}
}
// public bool Delete(object o) => Delete(ODBMapper.MapValue(ODBMapper.GetDocumentID(o)));
// public bool Delete(ODBValue documentID)
// {
// lock (this)
// {
// if (DocumentCollection.Delete(documentID))
// {
// if (objectCache.ContainsKey(documentID))
// objectCache.Remove(documentID);
// return true;
// }
// return false;
// }
// }
public bool HasProperty(string propName)
{
propName = IndexPath.TranslatePropertyPath(ElementType, propName);
// public bool HasProperty(string propName)
// {
// propName = IndexPath.TranslatePropertyPath(ElementType, propName);
ClassMapping classMapping = ODBMapper.GetMapping(ElementType) as ClassMapping;
if (classMapping != null)
{
return classMapping.HasField(propName);
}
return false;
}
// ClassMapping classMapping = ODBMapper.GetMapping(ElementType) as ClassMapping;
// if (classMapping != null)
// {
// return classMapping.HasField(propName);
// }
// return false;
// }
/* Indeces */
public void EnsureIndex(string propertyPath, bool unique = false)
{
//string translatedPath = IndexPath.TranslatePropertyPath(ElementType, propertyPath);
//DocumentCollection.EnsureIndex(translatedPath, translatedPath);
EnsureIndeces(false, new string[] { propertyPath });
}
public void EnsureIndeces(params string[] propertyPaths) => EnsureIndeces(false, propertyPaths);
public void EnsureIndeces(bool unique, params string[] propertyPaths)
{
for (int n = 0; n < propertyPaths.Length; n++)
propertyPaths[n] = IndexPath.TranslatePropertyPath(ElementType, propertyPaths[n]);
// /* Indeces */
// public void EnsureIndex(string propertyPath, bool unique = false)
// {
// //string translatedPath = IndexPath.TranslatePropertyPath(ElementType, propertyPath);
// //DocumentCollection.EnsureIndex(translatedPath, translatedPath);
// EnsureIndeces(false, new string[] { propertyPath });
// }
// public void EnsureIndeces(params string[] propertyPaths) => EnsureIndeces(false, propertyPaths);
// public void EnsureIndeces(bool unique, params string[] propertyPaths)
// {
// for (int n = 0; n < propertyPaths.Length; n++)
// propertyPaths[n] = IndexPath.TranslatePropertyPath(ElementType, propertyPaths[n]);
DocumentCollection.EnsureIndeces(propertyPaths, false);
}
// DocumentCollection.EnsureIndeces(propertyPaths, false);
// }
public void EnsureUniqueness(params string[] propertyPaths)
{
for (int n = 0; n < propertyPaths.Length; n++)
propertyPaths[n] = IndexPath.TranslatePropertyPath(ElementType, propertyPaths[n]);
// public void EnsureUniqueness(params string[] propertyPaths)
// {
// for (int n = 0; n < propertyPaths.Length; n++)
// propertyPaths[n] = IndexPath.TranslatePropertyPath(ElementType, propertyPaths[n]);
DocumentCollection.EnsureUniqueness(propertyPaths);
}
// DocumentCollection.EnsureUniqueness(propertyPaths);
// }
/* Object Cache */
public bool UseStrongCache { get; private set; }
Dictionary<ODBValue, object> objectCache = new Dictionary<ODBValue, object>();
// /* Object Cache */
// public bool UseStrongCache { get; private set; }
// Dictionary<ODBValue, object> objectCache = new Dictionary<ODBValue, object>();
public void EnableStrongCache(bool enable)
{
lock (this)
{
if (!enable)
{
foreach (ODBValue key in objectCache.Keys.ToArray())
{
if (!(objectCache[key] is WeakReference))
objectCache.Remove(key);
}
}
else
{
foreach (ODBValue key in objectCache.Keys.ToArray())
{
if ((objectCache[key] is WeakReference))
objectCache[key] = (objectCache[key] as WeakReference).Target;
}
}
}
}
// public void EnableStrongCache(bool enable)
// {
// lock (this)
// {
// if (!enable)
// {
// foreach (ODBValue key in objectCache.Keys.ToArray())
// {
// if (!(objectCache[key] is WeakReference))
// objectCache.Remove(key);
// }
// }
// else
// {
// foreach (ODBValue key in objectCache.Keys.ToArray())
// {
// if ((objectCache[key] is WeakReference))
// objectCache[key] = (objectCache[key] as WeakReference).Target;
// }
// }
// }
// }
private object GetCachedObject(ODBValue documentID)
{
if (objectCache.ContainsKey(documentID))
{
object o = objectCache[documentID];
if (o is WeakReference)
{
WeakReference weak = o as WeakReference;
if (weak.IsAlive)
return weak.Target;
else
return null;
}
return o;
}
return null;
}
// private object GetCachedObject(ODBValue documentID)
// {
// if (objectCache.ContainsKey(documentID))
// {
// object o = objectCache[documentID];
// if (o is WeakReference)
// {
// WeakReference weak = o as WeakReference;
// if (weak.IsAlive)
// return weak.Target;
// else
// return null;
// }
// return o;
// }
// return null;
// }
private void TouchCache(ODBValue documentID, object o)
{
if (object.ReferenceEquals(o, null) && objectCache.ContainsKey(documentID))
{
objectCache.Remove(documentID);
}
else if (!object.ReferenceEquals(o, null))
{
if (UseStrongCache)
objectCache[documentID] = o;
else
objectCache[documentID] = new WeakReference(o);
}
}
// private void TouchCache(ODBValue documentID, object o)
// {
// if (object.ReferenceEquals(o, null) && objectCache.ContainsKey(documentID))
// {
// objectCache.Remove(documentID);
// }
// else if (!object.ReferenceEquals(o, null))
// {
// if (UseStrongCache)
// objectCache[documentID] = o;
// else
// objectCache[documentID] = new WeakReference(o);
// }
// }
public object[] GetDocumentIDs()
{
return DocumentCollection.Index.Select((arg) => ODBMapper.UnmapValue(IDType, arg)).ToArray();
}
// public object[] GetDocumentIDs()
// {
// return DocumentCollection.Index.Select((arg) => ODBMapper.UnmapValue(IDType, arg)).ToArray();
// }
public IEnumerable GetEnumeration()
{
lock (this)
{
return new ObjectEnumeration(this, DocumentCollection.Index.ToArray());
}
}
// public IEnumerable GetEnumeration()
// {
// lock (this)
// {
// return new ObjectEnumeration(this, DocumentCollection.Index.ToArray());
// }
// }
public IEnumerator GetEnumerator()
{
return GetEnumeration().GetEnumerator();
}
// public IEnumerator GetEnumerator()
// {
// return GetEnumeration().GetEnumerator();
// }
public void Close()
{
DocumentCollection = null;
}
// public void Close()
// {
// DocumentCollection = null;
// }
class ObjectEnumeration : IEnumerable
{
ObjectCollection collection;
IEnumerable<ODBValue> documentIDs;
// class ObjectEnumeration : IEnumerable
// {
// ObjectCollection collection;
// IEnumerable<ODBValue> documentIDs;
public ObjectEnumeration(ObjectCollection collection,IEnumerable<ODBValue> documentIDs)
{
this.collection = collection;
this.documentIDs = documentIDs;
}
// public ObjectEnumeration(ObjectCollection collection,IEnumerable<ODBValue> documentIDs)
// {
// this.collection = collection;
// this.documentIDs = documentIDs;
// }
public IEnumerator GetEnumerator()
{
foreach (ODBValue documentID in this.documentIDs)
{
yield return this.collection.SelectByID(documentID);
}
}
}
// public IEnumerator GetEnumerator()
// {
// foreach (ODBValue documentID in this.documentIDs)
// {
// yield return this.collection.SelectByID(documentID);
// }
// }
// }
}
//}
public class ObjectCollection<T> : ObjectCollection where T:class
{
public ObjectCollection(ODBMapper odbmapper)
:base(odbmapper,typeof(T))
{}
//public class ObjectCollection<T> : ObjectCollection where T:class
//{
// public ObjectCollection(ODBMapper odbmapper)
// :base(odbmapper,typeof(T))
// {}
public IEnumerable<T> SelectQuery(Query query) => base.Select(query).Cast<T>();
public T Select(object id) => (T)base.SelectByID(id);
// public IEnumerable<T> SelectQuery(Query query) => base.Select(query).Cast<T>();
// public T Select(object id) => (T)base.SelectByID(id);
public bool Ensure(T o) => base.Ensure(o);
public bool Insert(T o) => base.Insert(o);
public bool Update(T o) => base.Update(o);
public bool Upsert(T o) => base.Upsert(o);
public void Delete(T o) => base.Delete(o);
}
// public bool Ensure(T o) => base.Ensure(o);
// public bool Insert(T o) => base.Insert(o);
// public bool Update(T o) => base.Update(o);
// public bool Upsert(T o) => base.Upsert(o);
// public void Delete(T o) => base.Delete(o);
//}
}

View File

@ -1,36 +1,36 @@
using System;
namespace ln.types.odb.ng
{
public class Reference<T> where T:class
{
ODBMapper mapper;
ObjectCollection<T> Collection => mapper.GetCollection<T>();
//public class Reference<T> where T:class
//{
// ODBMapper mapper;
// ObjectCollection<T> Collection => mapper.GetCollection<T>();
internal object valueID;
// internal object valueID;
public Reference(ODBMapper mapper)
{
this.mapper = mapper;
}
public Reference(ODBMapper mapper,T value)
{
this.mapper = mapper;
this.Value = value;
}
// public Reference(ODBMapper mapper)
// {
// this.mapper = mapper;
// }
// public Reference(ODBMapper mapper,T value)
// {
// this.mapper = mapper;
// this.Value = value;
// }
public T Value
{
get
{
if (this.valueID == null)
return null;
return this.Collection.Select(valueID);
}
set
{
this.Collection.Ensure(value);
this.valueID = this.mapper.GetDocumentID(value);
}
}
}
// public T Value
// {
// get
// {
// if (this.valueID == null)
// return null;
// return this.Collection.Select(valueID);
// }
// set
// {
// this.Collection.Ensure(value);
// this.valueID = this.mapper.GetDocumentID(value);
// }
// }
//}
}

163
odb/ng/Session.cs 100644
View File

@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using ln.types.odb.values;
using ln.logging;
namespace ln.types.odb.ng
{
public class Session : IStorageContainer
{
public IStorageContainer StorageContainer { get; private set; }
public ODBMapper ODBMapper { get; private set; }
public bool IsOpen => StorageContainer.IsOpen;
public Session(IStorageContainer storageContainer)
{
StorageContainer = storageContainer;
ODBMapper = ODBMapper.Default;
}
private Dictionary<string, IStorage> storages = new Dictionary<string, IStorage>();
public IStorage GetStorage(string storageName)
{
if (storages.ContainsKey(storageName))
return storages[storageName];
IStorage storage = StorageContainer.GetStorage(storageName);
storages.Add(storageName, new SessionCache(storage));
if (!storage.IsOpen)
storage.Open();
return storages[storageName];
}
public void Open()
{
StorageContainer.Open();
}
public void Close()
{
StorageContainer.Close();
}
public IEnumerable<string> GetStorageNames()
{
return StorageContainer.GetStorageNames();
}
public void Dispose()
{
}
class SessionCache : IStorage
{
public IStorage Storage { get; }
public bool IsOpen => Storage.IsOpen;
Dictionary<Guid, CachedDocument> documentCache = new Dictionary<Guid, CachedDocument>();
public SessionCache(IStorage storage)
{
Storage = storage;
}
public bool Open()
{
return Storage.Open();
}
public void Close()
{
Storage.Close();
}
public Document Load(Guid documentID)
{
lock (this)
{
if (!documentCache.ContainsKey(documentID))
{
Document cacheDocument = Storage.Load(documentID);
documentCache.Add(documentID, new CachedDocument(cacheDocument));
}
return documentCache[documentID].WorkingCopy;
}
}
public void Save(Document document)
{
lock (this)
{
if (!documentCache.ContainsKey(document.ID))
{
Logging.Log(LogLevel.DEBUG, "SessionCache: Save(): saving new Document {0}",document.ID);
Storage.Save(document);
documentCache.Add(document.ID, new CachedDocument(document.Clone() as Document,document));
}
else
{
lock (Storage)
{
Document storageDocument = Storage.Load(document.ID);
Document cacheDocument = documentCache[document.ID].CachedCopy;
bool changedFlag = false;
foreach (ODBValue propertyName in document.Keys)
{
if (!document[propertyName].Equals(cacheDocument[propertyName]))
{
Logging.Log(LogLevel.DEBUG, "SessionCache: Save(): found changed field for Document {0}: {1}={2}", document.ID, propertyName, document[propertyName]);
storageDocument[propertyName] = document[propertyName];
changedFlag = true;
}
}
foreach (ODBValue propertyName in cacheDocument.Keys)
{
if (!document.Contains(propertyName))
{
storageDocument[propertyName] = ODBNull.Instance;
changedFlag = true;
}
}
if (changedFlag)
{
Storage.Save(storageDocument);
documentCache[document.ID] = new CachedDocument(document.Clone() as Document,document);
}
else
{
Logging.Log(LogLevel.DEBUG, "SessionCache: Save(): No changes to be saved for document {0}",document.ID);
}
}
}
}
}
public IEnumerable<Guid> GetDocumentIDs()
{
return Storage.GetDocumentIDs();
}
struct CachedDocument
{
public Document CachedCopy;
public Document WorkingCopy;
public CachedDocument(Document cachedCopy)
{
CachedCopy = cachedCopy;
WorkingCopy = cachedCopy.Clone() as Document;
}
public CachedDocument(Document cachedCopy,Document workingCopy)
{
CachedCopy = cachedCopy;
WorkingCopy = workingCopy;
}
}
}
}
}

View File

@ -16,8 +16,8 @@ namespace ln.types.odb.ng.mappings
public Type MappedType { get; }
public GetID getID { get; private set; } = (o) => Guid.NewGuid();
public Type IDType { get; private set; }
//public GetID getID { get; private set; } = (o) => Guid.NewGuid();
//public Type IDType { get; private set; }
List<FieldInfo> mappedFields = new List<FieldInfo>();
@ -36,20 +36,20 @@ namespace ln.types.odb.ng.mappings
if (fieldinfo.GetCustomAttribute<IgnoreFieldAttribute>() == null)
{
mappedFields.Add(fieldinfo);
if (fieldinfo.GetCustomAttribute<DocumentIDAttribute>() != null)
{
getID = (o) => fieldinfo.GetValue(o);
IDType = fieldinfo.FieldType;
}
//if (fieldinfo.GetCustomAttribute<DocumentIDAttribute>() != null)
//{
// getID = (o) => fieldinfo.GetValue(o);
// IDType = fieldinfo.FieldType;
//}
}
}
foreach (PropertyInfo propInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
if (propInfo.GetCustomAttribute<DocumentIDAttribute>() != null)
{
getID = (o) => propInfo.GetValue(o);
IDType = propInfo.PropertyType;
}
//if (propInfo.GetCustomAttribute<DocumentIDAttribute>() != null)
//{
// getID = (o) => propInfo.GetValue(o);
// IDType = propInfo.PropertyType;
//}
}
if ((type != null) && !type.IsValueType && (!typeof(object).Equals(type.BaseType)))
@ -68,7 +68,7 @@ namespace ln.types.odb.ng.mappings
public object UnmapValue(ODBMapper mapper,ODBValue oval)
{
ODBDocument document = oval as ODBDocument;
Document document = oval as Document;
object o = Activator.CreateInstance(MappedType, true);
foreach (FieldInfo fieldInfo in mappedFields)
@ -76,12 +76,13 @@ namespace ln.types.odb.ng.mappings
ByReferenceAttribute byReferenceAttribute = fieldInfo.GetCustomAttribute<ByReferenceAttribute>();
if (byReferenceAttribute != null)
{
Type ceType = byReferenceAttribute.ElementType ?? fieldInfo.FieldType;
String cAlias = byReferenceAttribute.Alias ?? ceType.FullName;
// ToDo: Implement Referentiel Mapping
//Type ceType = byReferenceAttribute.ElementType ?? fieldInfo.FieldType;
//String cAlias = byReferenceAttribute.Alias ?? ceType.FullName;
ObjectCollection objectCollection = mapper.GetCollection(ceType, cAlias);
ODBValue refID = document[fieldInfo.Name];
fieldInfo.SetValue(o, objectCollection.SelectByID(refID));
//ObjectCollection objectCollection = mapper.GetStorage(ceType, cAlias);
//ODBValue refID = document[fieldInfo.Name];
//fieldInfo.SetValue(o, objectCollection.SelectByID(refID));
}
else
{
@ -104,53 +105,50 @@ namespace ln.types.odb.ng.mappings
return o;
}
public ODBValue MapValue(ODBMapper mapper,object value)
public Document MapDocument(ODBMapper mapper,Guid documentID,object value)
{
Document document = new Document(documentID);
document["__asm__"] = value.GetType().Assembly.GetName().Name;
document["__type__"] = value.GetType().FullName;
foreach (FieldInfo fieldInfo in mappedFields)
{
object fv = fieldInfo.GetValue(value);
ODBValue ov = null;
if (fieldInfo.GetCustomAttribute<ByReferenceAttribute>() != null)
{
if (!object.ReferenceEquals(fv, null))
{
// ToDo: Implement referential mapping
//mapper.GetStorage(fieldInfo.FieldType).Ensure(fv);
//ov = mapper.MapValue(mapper.GetDocumentID(fv));
}
else
ov = ODBNull.Instance;
}
else
{
ov = mapper.MapValue(fv);
}
document[fieldInfo.Name] = ov;
}
return document;
}
public ODBValue MapValue(ODBMapper mapper,object value)
{
if (Object.ReferenceEquals(value, null))
return ODBNull.Instance;
ODBDocument document = new ODBDocument();
document["__asm__"] = value.GetType().Assembly.GetName().Name;
document["__type__"] = value.GetType().FullName;
foreach (FieldInfo fieldInfo in mappedFields)
{
object fv = fieldInfo.GetValue(value);
ODBValue ov = null;
if (fieldInfo.GetCustomAttribute<ByReferenceAttribute>() != null)
{
if (!object.ReferenceEquals(fv, null))
{
mapper.GetCollection(fieldInfo.FieldType).Ensure(fv);
ov = mapper.MapValue(mapper.GetDocumentID(fv));
}
else
ov = ODBNull.Instance;
}
else
{
ov = mapper.MapValue(fv);
}
document[fieldInfo.Name] = ov;
}
document.ID = mapper.MapValue(getID(value));
return document;
return MapDocument(mapper, Guid.Empty, value);
}
public Type GetFieldType(ODBMapper mapper,string fieldName)
{
foreach (FieldInfo fieldInfo in mappedFields)
if (fieldInfo.Name.Equals(fieldName))
if (fieldInfo.GetCustomAttribute<ByReferenceAttribute>() != null)
{
return mapper.GetDocumentIDType(fieldInfo.FieldType);
} else
{
return fieldInfo.FieldType;
}
return fieldInfo.FieldType;
throw new KeyNotFoundException();
}
@ -162,14 +160,14 @@ namespace ln.types.odb.ng.mappings
public ODBValue MapValue(ODBMapper mapper, object value)
{
return new ODBDocument();
return new Document();
}
public object UnmapValue(ODBMapper mapper, ODBValue oval)
{
if (oval is ODBDocument)
if (oval is Document)
{
ODBDocument document = oval.AsDocument;
Document document = oval as Document;
if (!document.Contains("__type__"))
return new object();

View File

@ -20,13 +20,13 @@ namespace ln.types.odb.ng.mappings
if (dType.GetInterfaces().Contains(typeof(IDictionary)))
{
IDictionary dictionary = value as IDictionary;
ODBDocument document = new ODBDocument();
Document document = new Document();
document["__asm__"] = value.GetType().Assembly.GetName().Name;
document["__type__"] = value.GetType().FullName;
ODBDocument kTypes = new ODBDocument();
ODBDocument vTypes = new ODBDocument();
Document kTypes = new Document();
Document vTypes = new Document();
document["__ktypes__"] = kTypes;
document["__vtypes__"] = vTypes;
@ -47,7 +47,7 @@ namespace ln.types.odb.ng.mappings
public object UnmapValue(ODBMapper mapper, ODBValue oval)
{
ODBDocument document = oval.AsDocument;
Document document = oval as Document;
Type dType = Type.GetType(String.Format("{0}, {1}",document["__type__"].AsString,document["__asm__"].AsString)); //;Assembly.Load(document["__asm__"].AsString).GetType(document["__type__"].AsString);
if (dType.IsGenericType)
@ -59,8 +59,8 @@ namespace ln.types.odb.ng.mappings
Type kType = dType.GetGenericArguments()[0];
Type vType = dType.GetGenericArguments()[1];
ODBDocument ktypes = document.Contains("__ktypes__") ? document["__ktypes__"].AsDocument : new ODBDocument();
ODBDocument vtypes = document.Contains("__vtypes__") ? document["__vtypes__"].AsDocument : new ODBDocument();
Document ktypes = document.Contains("__ktypes__") ? document["__ktypes__"] as Document : new Document();
Document vtypes = document.Contains("__vtypes__") ? document["__vtypes__"] as Document : new Document();
foreach (ODBValue key in document.Keys)
{

View File

@ -0,0 +1,292 @@
// /**
// * File: FSSTorage.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization.Advanced;
using ln.logging;
namespace ln.types.odb.ng.storage
{
/**
* FSStorage
*
* Directory Layout:
*
* <BasePath>/<StorageName>
* /data.odb Serialized Document Data
* /data.idx Serialized Lookup Index for Documents and Free Areas
*
* data.odb
* ----------
* 0000 4 MAGIC Bytes
* 0004 4 Version
* 0008 8 LastCloseTimestamp
* 0010 4 FirstOffset
* 0014 4 GranularWidth
* 0018 8 Reserved 0
*
**/
public class FSStorage : IStorage
{
public byte[] MagicBytes { get; } = new byte[] { 0x0F, 0x0E, 0x0D, 0x0A };
public String StoragePath { get; }
public String StorageName { get; }
public int FileVersion { get; private set; }
public long LastCloseTimestamp { get; private set; }
public int FirstOffset { get; private set; }
public int GranularWidth { get; private set; } = 12;
public int GranularityMask => (1 << GranularWidth) - 1;
public int AppendOffset { get; private set; }
StorageAreaContainer storageAreas = new StorageAreaContainer();
Dictionary<Guid, StorageArea> documentAreas = new Dictionary<Guid, StorageArea>();
FileStream fileStream;
public FSStorage(string storagePath)
{
StoragePath = storagePath;
StorageName = Path.GetFileName(storagePath);
}
public FSStorage(string storagePath,int granularWidth)
:this(storagePath)
{
GranularWidth = granularWidth;
}
private void AssertOpen()
{
if (fileStream == null)
throw new IOException("FSStorage not opened");
}
public bool IsOpen => (fileStream != null);
public bool Open()
{
if (!IsOpen)
{
try
{
if (!Directory.Exists(StoragePath))
Directory.CreateDirectory(StoragePath);
fileStream = new FileStream(Path.Combine(StoragePath, "data.odb"), FileMode.OpenOrCreate);
if (fileStream.Length == 0)
{
FileVersion = 0;
LastCloseTimestamp = 0;
FirstOffset = (1 << GranularWidth);
if (FirstOffset < 0x20)
throw new NotSupportedException("Granularity too small");
AppendOffset = FirstOffset;
Close();
return Open();
}
else
{
if (!fileStream.ReadBytes(4).SequenceEqual(MagicBytes))
throw new IOException("Magic bytes do not match");
FileVersion = fileStream.ReadInteger();
LastCloseTimestamp = fileStream.ReadLong();
FirstOffset = fileStream.ReadInteger();
GranularWidth = fileStream.ReadInteger();
Scan();
}
}
catch (Exception e)
{
Logging.Log(e);
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
fileStream = null;
}
return false;
}
return true;
}
return false;
}
private void Scan()
{
int offset = FirstOffset;
while (offset < fileStream.Length)
{
fileStream.Position = offset;
StorageArea storageArea = new StorageArea(offset, fileStream.ReadInteger());
Guid documentID = new Guid(fileStream.ReadBytes(16));
if (Guid.Empty.Equals(documentID))
storageAreas.Push(storageArea);
else
{
if (documentAreas.ContainsKey(documentID))
{
Document previousDoc = LoadDocument(documentAreas[documentID]);
Document currentDoc = LoadDocument(storageArea);
if (previousDoc.StorageTimeStamp < currentDoc.StorageTimeStamp)
{
WriteStorageArea(documentAreas[documentID]);
storageAreas.Push(documentAreas[documentID]);
documentAreas[documentID] = storageArea;
}
else
{
WriteStorageArea(storageArea);
storageAreas.Push(storageArea);
}
} else
{
documentAreas.Add(documentID, storageArea);
}
}
offset = storageArea.NextOffset;
}
AppendOffset = offset;
}
public void Close()
{
lock (this){
AssertOpen();
fileStream.Position = 0;
fileStream.WriteBytes(MagicBytes);
fileStream.WriteInteger(FileVersion);
LastCloseTimestamp = (long)DateTime.Now.ToUnixTimeMilliseconds();
fileStream.WriteLong(LastCloseTimestamp);
fileStream.WriteInteger(FirstOffset);
fileStream.WriteInteger(GranularWidth);
fileStream.Close();
fileStream.Dispose();
fileStream = null;
}
}
public IEnumerable<Guid> GetDocumentIDs()
{
lock (this)
{
return documentAreas.Keys.ToArray();
}
}
public Document Load(Guid documentID)
{
lock (this)
{
if (!documentAreas.ContainsKey(documentID))
throw new KeyNotFoundException();
StorageArea storageArea = documentAreas[documentID];
return LoadDocument(storageArea);
}
}
private Document LoadDocument(StorageArea storageArea)
{
fileStream.Position = storageArea.Offset + 4;
Guid documentID = new Guid(fileStream.ReadBytes(16));
byte[] storageBytes = fileStream.ReadBytes(storageArea.Size - 20);
return new Document(documentID, storageBytes);
}
public void Save(Document document)
{
lock (this)
{
document.StorageTimeStamp = DateTime.Now;
byte[] storageBytes = document.ToStorage();
StorageArea storageArea = storageAreas.Pop(storageBytes.Length + 20);
if (storageArea == null)
storageArea = AppendStorageArea(storageBytes.Length + 20);
int neededSize = storageBytes.Length + 20;
CheckGranularity(ref neededSize);
if (storageArea.Size > neededSize)
{
StorageArea splitArea = storageArea.Split(storageArea.Size - neededSize);
WriteStorageArea(splitArea);
}
WriteStorageArea(storageArea, storageBytes);
if (documentAreas.ContainsKey(document.ID))
{
StorageArea oldStorageArea = documentAreas[document.ID];
WriteStorageArea(oldStorageArea);
storageAreas.Push(oldStorageArea);
}
documentAreas[document.ID] = storageArea;
}
}
public void Delete(Guid documentID)
{
lock (this)
{
if (documentAreas.ContainsKey(documentID))
{
StorageArea storageArea = documentAreas[documentID];
documentAreas.Remove(documentID);
storageArea = storageAreas.Push(storageArea);
WriteStorageArea(storageArea);
}
}
}
private StorageArea AppendStorageArea(int size)
{
CheckGranularity(ref size);
return new StorageArea(AppendOffset, size);
}
private void WriteStorageArea(StorageArea storageArea) => WriteStorageArea(storageArea, new byte[0]);
private void WriteStorageArea(StorageArea storageArea,byte[] data)
{
AssertOpen();
if (data.Length > (storageArea.Size - 4))
throw new ArgumentOutOfRangeException(nameof(data));
fileStream.Position = storageArea.Offset;
fileStream.WriteInteger(storageArea.Size);
fileStream.WriteBytes(data);
fileStream.WriteBytes(new byte[ storageArea.Size - 4 - data.Length]);
}
private void CheckGranularity(ref int i)
{
i = (i + GranularityMask) & ~GranularityMask;
}
}
}

View File

@ -0,0 +1,97 @@
// /**
// * File: FSStorage.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ln.types.odb.ng.storage
{
public class FSStorageContainer : IStorageContainer,IDisposable
{
public string BasePath { get; }
FileStream lockFile;
Dictionary<string, FSStorage> storages = new Dictionary<string, FSStorage>();
public FSStorageContainer(string basePath)
{
BasePath = basePath;
}
public bool IsOpen => lockFile != null;
private void AssertOpen()
{
if (!IsOpen)
throw new IOException("FSSTorage not open");
}
public void Close()
{
lock (this)
{
AssertOpen();
if (lockFile != null)
{
lockFile.Close();
lockFile.Dispose();
lockFile = null;
}
}
}
public IStorage GetStorage(string storageName)
{
lock (this)
{
AssertOpen();
if (!storages.ContainsKey(storageName))
storages.Add(storageName, new FSStorage(Path.Combine(BasePath, storageName)));
return storages[storageName];
}
}
public IEnumerable<string> GetStorageNames()
{
lock (this)
{
AssertOpen();
return storages.Keys;
}
}
public void Open()
{
lock (this)
{
if (!IsOpen)
{
if (!Directory.Exists(BasePath))
Directory.CreateDirectory(BasePath);
lockFile = new FileStream(Path.Combine(BasePath, ".lock"), FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 1024, FileOptions.DeleteOnClose);
foreach (String storagePath in Directory.EnumerateDirectories(BasePath))
{
FSStorage storage = new FSStorage(storagePath);
storages.Add(storage.StorageName, storage);
}
}
}
}
public void Dispose()
{
if (IsOpen)
Close();
}
}
}

View File

@ -0,0 +1,38 @@
// /**
// * File: StorageArea.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
namespace ln.types.odb.ng.storage
{
public class StorageArea
{
public int Offset { get; }
public int Size { get; set; }
public int NextOffset => Offset + Size;
public StorageArea(int offset,int size)
{
Offset = offset;
Size = size;
}
public StorageArea Split(int splitSize)
{
if (splitSize >= Size)
throw new ArgumentOutOfRangeException(nameof(splitSize));
StorageArea splitArea = new StorageArea(Offset + Size - splitSize,splitSize);
Size -= splitSize;
return splitArea;
}
}
}

View File

@ -0,0 +1,69 @@
// /**
// * File: StorageAreaContainer.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using ln.types.btree;
namespace ln.types.odb.ng.storage
{
public class StorageAreaContainer
{
public int SplitLimit { get; set; } = 32;
MappingBTree<int, StorageArea> storageAreas = new MappingBTree<int, StorageArea>((value)=>value.Offset);
public StorageAreaContainer()
{
}
public StorageArea Push(StorageArea storageArea)
{
storageAreas.Add(storageArea);
try
{
StorageArea previousStorageArea = storageAreas.Previous(storageArea);
if ((previousStorageArea != null) && (previousStorageArea.NextOffset == storageArea.Offset))
{
previousStorageArea.Size += storageArea.Size;
storageAreas.Remove(storageArea);
storageArea = previousStorageArea;
}
} catch
{
}
try
{
StorageArea nextStorageArea = storageAreas.Next(storageArea);
if ((nextStorageArea != null) && (storageArea.NextOffset == nextStorageArea.Offset))
{
storageArea.Size += nextStorageArea.Size;
storageAreas.Remove(nextStorageArea);
}
} catch
{ }
return storageArea;
}
public StorageArea Pop(int minSize)
{
foreach (StorageArea storageArea in storageAreas)
{
if (storageArea.Size >= minSize)
{
storageAreas.RemoveKey(storageArea.Offset);
return storageArea;
}
}
return null;
}
}
}

View File

@ -43,7 +43,14 @@ namespace ln.types.odb.values
public int Count => items.Count;
public override object Value {
public override ODBValue Clone()
{
ODBList clone = new ODBList();
clone.items.AddRange(this.items);
return clone;
}
public override object Value {
get
{
object[] a = new object[items.Count];

View File

@ -94,7 +94,13 @@ namespace ln.types.odb.values
public virtual ODBDocument AsDocument => (ODBDocument)this;
public virtual void Store(BinaryWriter storage)
public virtual ODBValue Clone()
{
return this;
}
public virtual void Store(BinaryWriter storage)
{
byte[] storageBytes = ToStorage();
@ -221,5 +227,21 @@ namespace ln.types.odb.values
{
return a.CompareValueTo(b);
}
}
static ODBValue()
{
new ODBDocument();
new ODBNull();
new ODBStringValue();
new ODBBool();
new ODBList();
new ODBInteger();
new ODBUInteger();
new ODBLong();
new ODBULong();
new ODBDouble();
new ODBGuid();
new ODBTypedValue();
}
}
}