diff --git a/ReactiveXaml.Serialization.Esent/EsentStorageEngine.cs b/ReactiveXaml.Serialization.Esent/EsentStorageEngine.cs new file mode 100644 index 0000000000..1fd8be15e5 --- /dev/null +++ b/ReactiveXaml.Serialization.Esent/EsentStorageEngine.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using Microsoft.Isam.Esent.Collections.Generic; + +namespace ReactiveXaml.Serialization.Esent +{ + public class EsentPersistedMetadata + { + public Dictionary SyncPointIndex { get; set; } + public Dictionary ItemTypeNames { get; set; } + } + + public class EsentStorageEngine : IExtendedStorageEngine + { + PersistentDictionary _backingStore; + Dictionary _itemTypeNames; + Dictionary _syncPointIndex; + + static readonly Lazy> allStorageTypes = new Lazy>( + () => Utility.GetAllTypesImplementingInterface(typeof(ISerializableItem)).ToArray()); + + Func serializerFactory; + + public EsentStorageEngine(string databasePath) + { + _backingStore = new PersistentDictionary(databasePath); + } + + public T Load(Guid contentHash) where T : ISerializableItem { + return (T)Load(contentHash); + } + + public object Load(Guid contentHash) + { + byte[] data; + + if (!_backingStore.TryGetValue(contentHash, out data)) { + this.Log().ErrorFormat("Failed to load object: {0}", contentHash); + return null; + } + + this.Log().DebugFormat("Loaded {0}", contentHash); + return serializerFactory(contentHash).Deserialize(data, Type.GetType(_itemTypeNames[contentHash])); + } + + public void Save(T obj) where T : ISerializableItem + { + this.Log().DebugFormat("Saving {0}: {1}", obj, obj.ContentHash); + _itemTypeNames[obj.ContentHash] = obj.GetType().FullName; + _backingStore[obj.ContentHash] = serializerFactory(obj).Serialize(obj); + } + + public void FlushChanges() + { + persistMetadata(); + _backingStore.Flush(); + } + + public ISyncPointInformation CreateSyncPoint(T obj, string qualifier = null, DateTimeOffset? createdOn = null) + where T : ISerializableItem + { + Save(obj); + + var key = getKeyFromQualifiedType(typeof (T), qualifier ?? String.Empty); + var parent = (_syncPointIndex.ContainsKey(key) ? _syncPointIndex[key] : Guid.Empty); + var ret = new SyncPointInformation(obj.ContentHash, parent, typeof (T), qualifier ?? String.Empty, createdOn ?? DateTimeOffset.Now); + Save(ret); + _syncPointIndex[key] = ret.ContentHash; + + this.Log().InfoFormat("Created sync point: {0}.{1}", obj.ContentHash, qualifier); + + FlushChanges(); + return ret; + } + + public Guid[] GetOrderedRevisionList(Type type, string qualifier = null) + { + var ret = new List(); + var key = getKeyFromQualifiedType(type, qualifier ?? String.Empty); + + if (!_syncPointIndex.ContainsKey(key)) { + return null; + } + + var current = _syncPointIndex[key]; + while(current != Guid.Empty) { + ret.Add(current); + + var syncPoint = Load(current); + current = syncPoint.ParentSyncPoint; + } + + return ret.ToArray(); + } + + public void Dispose() { + FlushChanges(); + _backingStore.Dispose(); + } + + public T GetLatestRootObject(string qualifier = null, DateTimeOffset? olderThan = null) where T : ISerializableItem { + throw new NotImplementedException(); + } + + public T[] GetRootObjectsInDateRange(string qualifier = null, DateTimeOffset? olderThan = null, DateTimeOffset? newerThan = null) where T : ISerializableItem { + throw new NotImplementedException(); + } + + static string getKeyFromQualifiedType(Type type, string qualifier) + { + return String.Format(CultureInfo.InvariantCulture, "{0}_{1}", type.FullName, qualifier); + } + + void loadOrInitializeMetadata() + { + byte[] data; + + if (!_backingStore.TryGetValue(Guid.Empty, out data)) { + if (_backingStore.Count != 0) { + this.Log().Fatal("Database has been corrupted!"); + throw new Exception("Database is in an inconsistent state"); + } + + this.Log().Warn("Could not load metadata, initializing blank"); + _itemTypeNames = new Dictionary(); + _syncPointIndex = new Dictionary(); + + persistMetadata(); + } + } + + void persistMetadata() { + var metadata = new EsentPersistedMetadata() { ItemTypeNames = _itemTypeNames, SyncPointIndex = null }; + _backingStore[Guid.Empty] = serializerFactory(metadata).Serialize(metadata); + } + } +} \ No newline at end of file diff --git a/ReactiveXaml.Serialization.Esent/Properties/AssemblyInfo.cs b/ReactiveXaml.Serialization.Esent/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..740272ded0 --- /dev/null +++ b/ReactiveXaml.Serialization.Esent/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ReactiveXaml.Serialization.Esent")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("ReactiveXaml.Serialization.Esent")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0fa075f3-b680-4793-b29b-cde3bfb59c9b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ReactiveXaml.Serialization.Esent/ReactiveXaml.Serialization.Esent.csproj b/ReactiveXaml.Serialization.Esent/ReactiveXaml.Serialization.Esent.csproj new file mode 100644 index 0000000000..b856727216 --- /dev/null +++ b/ReactiveXaml.Serialization.Esent/ReactiveXaml.Serialization.Esent.csproj @@ -0,0 +1,73 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E} + Library + Properties + ReactiveXaml.Serialization.Esent + ReactiveXaml.Serialization.Esent + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\ext\Esent.Collections.dll + + + ..\ext\Esent.Interop.dll + + + + + + + + + + + + + + + + {C6A9A8EC-FDA6-45C1-9694-298D44E3C55F} + ReactiveXaml.Serialization + + + {292A477B-BB94-43C1-984E-E177EF9FEDB7} + ReactiveXaml + + + + + + + + \ No newline at end of file diff --git a/ReactiveXaml_Core.sln b/ReactiveXaml_Core.sln index dadbe380ae..71fa7ea19e 100644 --- a/ReactiveXaml_Core.sln +++ b/ReactiveXaml_Core.sln @@ -18,6 +18,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveXaml.Serialization" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveXaml.Serialization.Tests", "ReactiveXaml.Serialization.Tests\ReactiveXaml.Serialization.Tests.csproj", "{ACA93E36-5301-4F2B-95C8-70897574506B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveXaml.Serialization.Esent", "ReactiveXaml.Serialization.Esent\ReactiveXaml.Serialization.Esent.csproj", "{0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E}" +EndProject Global GlobalSection(TestCaseManagementSettings) = postSolution CategoryFile = ReactiveXaml.vsmdi @@ -81,6 +83,16 @@ Global {ACA93E36-5301-4F2B-95C8-70897574506B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {ACA93E36-5301-4F2B-95C8-70897574506B}.Release|Mixed Platforms.Build.0 = Release|Any CPU {ACA93E36-5301-4F2B-95C8-70897574506B}.Release|x86.ActiveCfg = Release|Any CPU + {0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E}.Debug|x86.ActiveCfg = Debug|Any CPU + {0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E}.Release|Any CPU.Build.0 = Release|Any CPU + {0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0B0ABDC8-4EF3-4172-ADE4-E4FB55E5F91E}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE