forked from reactiveui/ReactiveUI
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInterfaces.cs
298 lines (271 loc) · 12.9 KB
/
Interfaces.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
using System;
using System.Collections;
using System.Collections.Generic;
namespace ReactiveUI.Serialization
{
/// <summary>
/// ISerializableItem represents any object that can be serialized via a
/// Sync Point, either directly or via a property in the object graph. Its
/// core feature is that it can return a unique hash representing its
/// content.
/// </summary>
public interface ISerializableItem : IReactiveNotifyPropertyChanged
{
/// <summary>
/// A hash representing the content of the object. Note that this should
/// be implemented using a cache-pattern, calling CalculateHash as
/// needed. This property *cannot* be Guid.Empty.
/// </summary>
Guid ContentHash { get; }
/// <summary>
/// Explicitly recalculates the value of ContentHash - note that
/// similarly to CalculateHash, this cannot return Guid.Empty (a
/// suggested solution is to return the MD5 hash of GetType().FullName
/// if an object has no hashable data)
/// </summary>
/// <returns>The hash of the content.</returns>
Guid CalculateHash();
}
/// <summary>
/// ISerializableList represents a list of serializable objects, which can
/// be serialized as their hashes instead of the actual list items.
///
/// This interface exists mainly for the framework, and ISerializableList of
/// T should be used instead.
/// </summary>
public interface ISerializableList : IList, ISerializableItem, IReactiveCollection
{
/// <summary>
/// CreatedOn returns the date each item was added to the list.
/// </summary>
IDictionary<Guid, DateTimeOffset> CreatedOn { get; }
/// <summary>
/// XXX: YOU SHOULD PROBABLY REMOVE THIS
/// </summary>
IDictionary<Guid, DateTimeOffset> UpdatedOn { get; }
/// <summary>
/// GetBaseListType returns the Type of all the items in the list - if
/// the list is heterogenous, return typeof(object).
/// </summary>
/// <returns>The Type of all the items in the list.</returns>
Type GetBaseListType();
}
/// <summary>
/// ISerializableList of T represents a list of serializable objects, which can
/// be serialized as their hashes instead of the actual list items.
/// </summary>
public interface ISerializableList<T> : IReactiveCollection<T>, ISerializableList
where T : ISerializableItem
{
}
/// <summary>
/// ISyncPointInformation represents a Sync Point, a commit to the
/// serialization store where we will record the object state. Override this
/// only if you want to commit custom information with a sync point in an
/// Engine implementation, otherwise use SyncPointInformation.
/// </summary>
public interface ISyncPointInformation : ISerializableItem
{
/// <summary>
/// The content hash of the root object serialized in this sync point.
/// </summary>
Guid RootObjectHash { get; }
/// <summary>
/// The content hash of the parent ISyncPointInformation object, or
/// Guid.Empty if this is the initial commit.
/// </summary>
Guid ParentSyncPoint { get; }
/// <summary>
/// The full type name (i.e. Type.FullName) of the root object.
/// </summary>
string RootObjectTypeName { get; }
/// <summary>
/// An optional string to create separate sync point lists (i.e.
/// "branches") - this is a string provided by the application or null.
/// </summary>
string Qualifier { get; }
/// <summary>
/// The date that the sync point was created.
/// </summary>
DateTimeOffset CreatedOn { get; }
}
/// <summary>
/// IStorageEngine is the core interface for classes that can maintain and
/// persist list of sync points. Its core responsibilities are to quickly
/// Load and Save individual objects, and create and list Sync Points
/// (objects that represent an atomic commit to the Storage engine)
/// </summary>
public interface IStorageEngine : IDisposable
{
/// <summary>
/// Loads an object given its Content Hash. Note that it is critical
/// that separate calls to Load with the same ContentHash return two
/// distinct in-memory copies of the object - i.e. maintaining an
/// "object cache" will result in object corruption.
/// </summary>
/// <param name="contentHash">The hash of the object to load.</param>
/// <returns>The deserialized object, or null if the object is not
/// present.</returns>
T Load<T>(Guid contentHash) where T : ISerializableItem;
/// <summary>
/// Loads an object given its Content Hash. Note that it is critical
/// that separate calls to Load with the same ContentHash return two
/// distinct in-memory copies of the object - i.e. maintaining an
/// "object cache" will result in object corruption.
/// </summary>
/// <param name="contentHash">The hash of the object to load.</param>
/// <returns>The deserialized object, or null if the object is not
/// present.</returns>
object Load(Guid contentHash);
/// <summary>
/// Saves an object to the persistence engine. Note that this does *not*
/// imply that the object is immediately written to the backing store,
/// the engine is free to postpone serialization.
/// </summary>
/// <param name="obj">The object to save.</param>
void Save<T>(T obj) where T : ISerializableItem;
/// <summary>
/// When called, guarantees all objects are serialized to a persistent
/// store.
/// </summary>
void FlushChanges();
/// <summary>
/// Returns the content hash of all objects in the store.
/// </summary>
/// <returns></returns>
Guid[] GetAllObjectHashes();
/// <summary>
/// Returns the count of available objects.
/// </summary>
/// <returns>The count of available objects.</returns>
int GetObjectCount();
/// <summary>
/// Creates a Sync Point using the specified root object and qualifier
/// and persists it to the engine. This sync point will be attached to
/// the parent sync point for this type and qualifier.
/// </summary>
/// <param name="obj">The Root Object to attach to this sync point.</param>
/// <param name="qualifier">An optional string to create separate sync
/// point lists (i.e. "branches") - this is a string provided by the
/// application or null.</param>
/// <param name="createdOn">The date that the sync point was created, or
/// null to use the current time.</param>
/// <returns>A reference to the newly created sync point</returns>
ISyncPointInformation CreateSyncPoint<T>(T obj, string qualifier = null, DateTimeOffset? createdOn = null)
where T : ISerializableItem;
/// <summary>
/// GetOrderedRevisionList returns the list of Sync Points commited for
/// the given root object Type and qualifier.
/// </summary>
/// <param name="type">The Type of the root object associated with the
/// Sync Point.</param>
/// <param name="qualifier">An optional string to create separate sync
/// point lists (i.e. "branches") - this is a string provided by the
/// application or null.</param>
/// <returns>A list of content hashes, in the order that they were
/// committed. Note that this does *not* necessarily correlate to the
/// CreatedOn date since it can be explicitly set.</returns>
Guid[] GetOrderedRevisionList(Type type, string qualifier = null);
}
/// <summary>
/// IExtendedStorageEngine provides additional methods that would most
/// likely be needed by applications. If this interface is not explicitly
/// implemented in storage implementations, a naive low performance
/// implementation that uses the IStorageEngine methods will be used.
/// </summary>
public interface IExtendedStorageEngine : IStorageEngine
{
/// <summary>
/// Load the root object associated with the the latest Sync Point for
/// the given type and qualifier whose CreatedAt date is older than the
/// given date.
/// </summary>
/// <param name="qualifier">An optional string to create separate sync
/// point lists (i.e. "branches") - this is a string provided by the
/// application or null.</param>
/// <param name="olderThan">An optional paramter that requires the
/// return value to be older than the given date. If no date is given,
/// the newest root object is returned.</param>
/// <returns>The root object from the latest Sync Point that satisfies
/// the given constraints.</returns>
T GetLatestRootObject<T>(string qualifier = null, DateTimeOffset? olderThan = null)
where T : ISerializableItem;
/// <summary>
/// Loads the root objects whose CreatedAt date fall within the given
/// date ranges.
/// </summary>
/// <param name="qualifier">An optional string to create separate sync
/// point lists (i.e. "branches") - this is a string provided by the
/// application or null.</param>
/// <param name="olderThan">An optional paramter that requires the
/// return values to be older than the given date. If no date is given,
/// this constraint is ignored.</param>
/// <param name="newerThan">An optional paramter that requires the
/// return values to be newer than the given date. If no date is given,
/// this constraint is ignored.</param>
/// <returns>All root objects whose Sync Points satisfy the given
/// constraints, or an empty array if none of them qualify.</returns>
T[] GetRootObjectsInDateRange<T>(
string qualifier = null,
DateTimeOffset? olderThan = null,
DateTimeOffset? newerThan = null)
where T : ISerializableItem;
}
/// <summary>
/// IObjectSerializationProvider decouples the task of serialization (i.e.
/// reducing an object to its on-disk form) from persistence (i.e.
/// reading/writing the on-disk form to persistent storage).
///
/// Most engine implementations will use JsonNetObjectSerializationProvider,
/// but should take a constructor parameter to allow this to be pluggable.
///
/// Note that this interface is the interface that does the "Git-like"
/// magic; that is, deconstructing objects into their pieces and instead
/// serializing their content hashes.
/// </summary>
public interface IObjectSerializationProvider
{
/// <summary>
/// Write an object to memory, including serializing all of the child
/// objects in the object graph. This object often needs a reference to
/// the IStorageEngine, to be able to ensure sub-objects are already
/// serialized.
/// </summary>
/// <param name="obj">The root object to serialize to disk.</param>
/// <returns>A byte representation of the object.</returns>
byte[] Serialize(object obj);
/// <summary>
/// Reads an object from the data returned by Serialize.
/// </summary>
/// <param name="data">The byte representation of the object.</param>
/// <param name="type">The type of the object to reconstruct.</param>
/// <returns>The deserialized object.</returns>
object Deserialize(byte[] data, Type type);
/// <summary>
/// SerializedDataToString is a method used for debugging purposes to
/// dump a serialized object out as a string. Production
/// implementations are free to return an empty string.
/// </summary>
/// <param name="data">A serialized object to examine.</param>
/// <returns>The string representation of the byte data (i.e. the JSON
/// string).</returns>
string SerializedDataToString(byte[] data);
}
public static class SyncPointInformationMixin
{
static MemoizingMRUCache<string, Type> _ObjectNameCache = new MemoizingMRUCache<string, Type>(
(x, _) => Type.GetType(x), 20);
/// <summary>
/// GetRootObjectType returns the Type of the root object associated with the given Sync
/// Point.
/// </summary>
/// <returns>The Type of the root object.</returns>
public static Type GetRootObjectType(this ISyncPointInformation This)
{
lock(_ObjectNameCache) {
return _ObjectNameCache.Get(This.RootObjectTypeName);
}
}
}
}
// vim: tw=120 ts=4 sw=4 et :