Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
MongoDBProvider.cs
1using MongoDB.Bson;
2using MongoDB.Bson.IO;
3using MongoDB.Bson.Serialization;
4using MongoDB.Driver;
5using System;
6using System.Collections.Generic;
7using System.IO;
8using System.Reflection;
9using System.Runtime.ExceptionServices;
10using System.Threading;
11using System.Threading.Tasks;
12using System.Xml;
13using Waher.Events;
22
24{
29 {
30 private readonly Dictionary<string, IMongoCollection<BsonDocument>> collections = new Dictionary<string, IMongoCollection<BsonDocument>>();
31 private readonly Dictionary<Type, IObjectSerializer> serializers = new Dictionary<Type, IObjectSerializer>();
32 private readonly AutoResetEvent serializerAdded = new AutoResetEvent(false);
33 private MongoClient client;
34 private IMongoDatabase database;
35 private string id;
36 private string defaultCollectionName;
37 private string lastCollectionName = null;
38 private IMongoCollection<BsonDocument> lastCollection = null;
39 private IMongoCollection<BsonDocument> defaultCollection;
40
46 public MongoDBProvider(string DatabaseName, string DefaultCollectionName)
47 {
48 MongoClientSettings Settings = new MongoClientSettings();
49 this.Init(Settings, DatabaseName, DefaultCollectionName);
50 }
51
58 public MongoDBProvider(string HostName, string DatabaseName, string DefaultCollectionName)
59 {
60 MongoClientSettings Settings = new MongoClientSettings()
61 {
62 Server = new MongoServerAddress(HostName)
63 };
64
65 this.Init(Settings, DatabaseName, DefaultCollectionName);
66 }
67
75 public MongoDBProvider(string HostName, int Port, string DatabaseName, string DefaultCollectionName)
76 {
77 MongoClientSettings Settings = new MongoClientSettings()
78 {
79 Server = new MongoServerAddress(HostName, Port)
80 };
81
82 this.Init(Settings, DatabaseName, DefaultCollectionName);
83 }
84
91 public MongoDBProvider(MongoClientSettings Settings, string DatabaseName, string DefaultCollectionName)
92 {
93 this.Init(Settings, DatabaseName, DefaultCollectionName);
94 }
95
96 private void Init(MongoClientSettings Settings, string DatabaseName, string DefaultCollectionName)
97 {
98 this.id = Guid.NewGuid().ToString().Replace("-", string.Empty);
99 this.client = new MongoClient(Settings);
100 this.database = this.client.GetDatabase(DatabaseName);
101
102 this.defaultCollectionName = DefaultCollectionName;
103 this.defaultCollection = this.GetCollection(this.defaultCollectionName);
104
105 ConstructorInfo DefaultConstructor;
107
109 {
110 try
111 {
112 DefaultConstructor = Types.GetDefaultConstructor(T);
113 if (DefaultConstructor is null)
114 continue;
115
116 S = DefaultConstructor.Invoke(Types.NoParameters) as IObjectSerializer;
117 if (S is null)
118 continue;
119 }
120 catch (Exception)
121 {
122 continue;
123 }
124
125 this.serializers[S.ValueType] = S;
126 }
127
128 this.serializers[typeof(GenericObject)] = new GenericObjectSerializer(this, false);
129 this.serializers[typeof(object)] = new GenericObjectSerializer(this, true);
130 }
131
135 public string Id => this.id;
136
140 public int ObjectIdByteCount => 12;
141
147 public IMongoCollection<BsonDocument> GetCollection(string CollectionName)
148 {
149 IMongoCollection<BsonDocument> Result;
150
151 lock (this.collections)
152 {
153 if (CollectionName == this.lastCollectionName)
154 Result = this.lastCollection;
155 else
156 {
157 if (!this.collections.TryGetValue(CollectionName, out Result))
158 {
159 Result = this.database.GetCollection<BsonDocument>(CollectionName);
160 this.collections[CollectionName] = Result;
161 }
162
163 this.lastCollection = Result;
164 this.lastCollectionName = CollectionName;
165 }
166 }
167
168 return Result;
169 }
170
174 public MongoClient Client => this.client;
175
179 public string DefaultCollectionName => this.defaultCollectionName;
180
184 public IMongoCollection<BsonDocument> DefaultCollection => this.defaultCollection;
185
192 {
193 IObjectSerializer Result;
194 TypeInfo TI = Type.GetTypeInfo();
195
196 lock (this.collections)
197 {
198 if (this.serializers.TryGetValue(Type, out Result))
199 return Result;
200
201 if (TI.IsEnum)
202 Result = new EnumSerializer(Type);
203 else if (Type.IsArray)
204 {
205 Type ElementType = Type.GetElementType();
206 Type T = Waher.Runtime.Inventory.Types.GetType(typeof(ByteArraySerializer).FullName.Replace("ByteArray", "Array"));
207 Type SerializerType = T.MakeGenericType(new Type[] { ElementType });
208 Result = (IObjectSerializer)Activator.CreateInstance(SerializerType, this);
209 }
210 else if (TI.IsGenericType)
211 {
212 Type GT = Type.GetGenericTypeDefinition();
213 if (GT == typeof(Nullable<>))
214 {
215 Type NullableType = Type.GenericTypeArguments[0];
216
217 if (NullableType.GetTypeInfo().IsEnum)
218 Result = new Serialization.NullableTypes.NullableEnumSerializer(NullableType);
219 else
220 Result = null;
221 }
222 else
223 Result = null;
224 }
225 else
226 Result = null;
227
228 if (!(Result is null))
229 {
230 this.serializers[Type] = Result;
231 this.serializerAdded.Set();
232
233 return Result;
234 }
235 }
236
237 try
238 {
239 Result = new ObjectSerializer(Type, this);
240
241 lock (this.collections)
242 {
243 this.serializers[Type] = Result;
244 this.serializerAdded.Set();
245 }
246 }
247 catch (FileLoadException ex)
248 {
249 // Serializer in the process of being generated from another task or thread.
250
251 while (true)
252 {
253 if (!this.serializerAdded.WaitOne(1000))
254 ExceptionDispatchInfo.Capture(ex).Throw();
255
256 lock (this.collections)
257 {
258 if (this.serializers.TryGetValue(Type, out Result))
259 return Result;
260 }
261 }
262 }
263
264 return Result;
265 }
266
273 {
274 return this.GetObjectSerializerEx(Object.GetType());
275 }
276
283 {
284 if (!(this.GetObjectSerializer(Type) is ObjectSerializer Serializer))
285 throw new Exception("Objects of type " + Type.FullName + " must be embedded.");
286
287 return Serializer;
288 }
289
294 public async Task Insert(object Object)
295 {
296 ObjectSerializer Serializer = this.GetObjectSerializerEx(Object);
297 string CollectionName = Serializer.CollectionName(Object);
298 IMongoCollection<BsonDocument> Collection;
299
300 if (string.IsNullOrEmpty(CollectionName))
301 Collection = this.defaultCollection;
302 else
303 Collection = this.GetCollection(CollectionName);
304
305 if (Serializer.HasObjectIdField)
306 {
307 if (Serializer.HasObjectId(Object))
308 throw new Exception("Object already has an Object ID. If updating an object, use the Update method.");
309 else
310 await Serializer.GetObjectId(Object, true);
311 }
312 else
313 {
314 BsonDocument Doc = Object.ToBsonDocument(Object.GetType(), Serializer);
315 await Collection.InsertOneAsync(Doc);
316 }
317 }
318
323 public Task Insert(params object[] Objects)
324 {
325 return this.Insert((IEnumerable<object>)Objects);
326 }
327
332 public async Task Insert(IEnumerable<object> Objects)
333 {
334 Dictionary<string, KeyValuePair<IMongoCollection<BsonDocument>, LinkedList<BsonDocument>>> DocumentsPerCollection =
335 new Dictionary<string, KeyValuePair<IMongoCollection<BsonDocument>, LinkedList<BsonDocument>>>();
336 Type Type;
337 Type LastType = null;
338 ObjectSerializer Serializer = null;
339 string CollectionName;
340 string LastCollectionName = null;
341 IMongoCollection<BsonDocument> Collection;
342 IMongoCollection<BsonDocument> LastCollection = null;
343 LinkedList<BsonDocument> Documents = null;
344 BsonDocument Document;
345
346 foreach (object Object in Objects)
347 {
348 Type = Object.GetType();
349
350 if (Type != LastType)
351 {
352 Serializer = this.GetObjectSerializerEx(Type);
353 CollectionName = Serializer.CollectionName(Object);
354 LastType = Type;
355
356 if (CollectionName == LastCollectionName)
357 Collection = LastCollection;
358 else
359 {
360 LastCollectionName = CollectionName;
361
362 if (string.IsNullOrEmpty(CollectionName))
363 CollectionName = this.defaultCollectionName;
364
365 if (DocumentsPerCollection.TryGetValue(CollectionName, out KeyValuePair<IMongoCollection<BsonDocument>, LinkedList<BsonDocument>> P))
366 Collection = P.Key;
367 else
368 {
369 Collection = this.GetCollection(CollectionName);
370 P = new KeyValuePair<IMongoCollection<BsonDocument>, LinkedList<BsonDocument>>(Collection, new LinkedList<BsonDocument>());
371 DocumentsPerCollection[CollectionName] = P;
372 }
373
374 Documents = P.Value;
375 LastCollection = Collection;
376 }
377 }
378
379 Document = Object.ToBsonDocument(Type, Serializer);
380 Documents.AddLast(Document);
381 }
382
383 foreach (KeyValuePair<IMongoCollection<BsonDocument>, LinkedList<BsonDocument>> P2 in DocumentsPerCollection.Values)
384 await P2.Key.InsertManyAsync(P2.Value);
385 }
386
392 public Task InsertLazy(object Object, ObjectCallback Callback)
393 => this.Process(Object, this.Insert(Object), Callback);
394
400 public Task InsertLazy(object[] Objects, ObjectsCallback Callback)
401 => this.Process(Objects, this.Insert(Objects), Callback);
402
408 public Task InsertLazy(IEnumerable<object> Objects, ObjectsCallback Callback)
409 => this.Process(Objects, this.Insert(Objects), Callback);
410
420 public Task<IEnumerable<T>> Find<T>(int Offset, int MaxCount, params string[] SortOrder)
421 where T : class
422 {
423 return this.Find<T>(Offset, MaxCount, (Filter)null, SortOrder);
424 }
425
436 public Task<IEnumerable<T>> Find<T>(int Offset, int MaxCount, Filter Filter, params string[] SortOrder)
437 where T : class
438 {
439 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(T));
440 string CollectionName = Serializer.CollectionName(null);
441 IMongoCollection<BsonDocument> Collection;
442 FilterDefinition<BsonDocument> BsonFilter;
443
444 if (string.IsNullOrEmpty(CollectionName))
445 Collection = this.defaultCollection;
446 else
447 Collection = this.GetCollection(CollectionName);
448
449 if (Filter is null)
450 BsonFilter = new BsonDocument();
451 else
452 BsonFilter = this.Convert(Filter, Serializer);
453
454 return this.Find<T>(Serializer, Collection, Offset, MaxCount, BsonFilter, null, SortOrder);
455 }
456
468 public Task<IEnumerable<T>> Find<T>(int Offset, int MaxCount, Filter Filter,
469 T ContinueAfter, params string[] SortOrder)
470 where T : class
471 {
472 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(T));
473 string CollectionName = Serializer.CollectionName(null);
474 IMongoCollection<BsonDocument> Collection;
475 FilterDefinition<BsonDocument> BsonFilter;
476
477 if (string.IsNullOrEmpty(CollectionName))
478 Collection = this.defaultCollection;
479 else
480 Collection = this.GetCollection(CollectionName);
481
482 if (Filter is null)
483 BsonFilter = new BsonDocument();
484 else
485 BsonFilter = this.Convert(Filter, Serializer);
486
487 return this.Find(Serializer, Collection, Offset, MaxCount, BsonFilter, ContinueAfter, SortOrder);
488 }
489
500 public Task<IEnumerable<T>> Find<T>(int Offset, int MaxCount, FilterDefinition<BsonDocument> BsonFilter,
501 params string[] SortOrder)
502 where T : class
503 {
504 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(T));
505 string CollectionName = Serializer.CollectionName(null);
506 IMongoCollection<BsonDocument> Collection;
507
508 if (string.IsNullOrEmpty(CollectionName))
509 Collection = this.defaultCollection;
510 else
511 Collection = this.GetCollection(CollectionName);
512
513 return this.Find<T>(Serializer, Collection, Offset, MaxCount, BsonFilter, null, SortOrder);
514 }
515
526 public Task<IEnumerable<T>> Find<T>(string CollectionName, int Offset, int MaxCount, Filter Filter, params string[] SortOrder)
527 where T : class
528 {
529 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(T));
530 IMongoCollection<BsonDocument> Collection;
531 FilterDefinition<BsonDocument> BsonFilter;
532
533 if (string.IsNullOrEmpty(CollectionName))
534 Collection = this.defaultCollection;
535 else
536 Collection = this.GetCollection(CollectionName);
537
538 if (Filter is null)
539 BsonFilter = new BsonDocument();
540 else
541 BsonFilter = this.Convert(Filter, Serializer);
542
543 return this.Find<T>(Serializer, Collection, Offset, MaxCount, BsonFilter, null, SortOrder);
544 }
545
557 public Task<IEnumerable<T>> Find<T>(string CollectionName, int Offset, int MaxCount, Filter Filter,
558 T ContinueAfter, params string[] SortOrder)
559 where T : class
560 {
561 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(T));
562 IMongoCollection<BsonDocument> Collection;
563 FilterDefinition<BsonDocument> BsonFilter;
564
565 if (string.IsNullOrEmpty(CollectionName))
566 Collection = this.defaultCollection;
567 else
568 Collection = this.GetCollection(CollectionName);
569
570 if (Filter is null)
571 BsonFilter = new BsonDocument();
572 else
573 BsonFilter = this.Convert(Filter, Serializer);
574
575 return this.Find(Serializer, Collection, Offset, MaxCount, BsonFilter, ContinueAfter, SortOrder);
576 }
577
587 public Task<IEnumerable<object>> Find(string CollectionName, int Offset, int MaxCount, params string[] SortOrder)
588 {
589 return this.Find(CollectionName, Offset, MaxCount, null, SortOrder);
590 }
591
602 public Task<IEnumerable<object>> Find(string CollectionName, int Offset, int MaxCount, Filter Filter, params string[] SortOrder)
603 {
604 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(object));
605 IMongoCollection<BsonDocument> Collection;
606 FilterDefinition<BsonDocument> BsonFilter;
607
608 if (string.IsNullOrEmpty(CollectionName))
609 Collection = this.defaultCollection;
610 else
611 Collection = this.GetCollection(CollectionName);
612
613 if (Filter is null)
614 BsonFilter = new BsonDocument();
615 else
616 BsonFilter = this.Convert(Filter, Serializer);
617
618 return this.Find<object>(Serializer, Collection, Offset, MaxCount, BsonFilter, SortOrder);
619 }
620
621 private async Task<IEnumerable<T>> Find<T>(ObjectSerializer Serializer, IMongoCollection<BsonDocument> Collection,
622 int Offset, int MaxCount, FilterDefinition<BsonDocument> BsonFilter, T ContinueAfter, params string[] SortOrder)
623 where T : class
624 {
625 if (!(ContinueAfter is null))
626 throw new NotImplementedException("Paginated searches not implemented in MongoDB provider.");
627
628 IFindFluent<BsonDocument, BsonDocument> ResultSet = Collection.Find(BsonFilter);
629
630 if (SortOrder.Length > 0)
631 {
632 SortDefinition<BsonDocument> SortDefinition = null;
633
634 foreach (string SortBy in SortOrder)
635 {
636 if (SortDefinition is null)
637 {
638 if (SortBy.StartsWith("-"))
639 SortDefinition = Builders<BsonDocument>.Sort.Descending(Serializer.ToShortName(SortBy.Substring(1)));
640 else
641 SortDefinition = Builders<BsonDocument>.Sort.Ascending(Serializer.ToShortName(SortBy));
642 }
643 else
644 {
645 if (SortBy.StartsWith("-"))
646 SortDefinition = SortDefinition.Descending(Serializer.ToShortName(SortBy.Substring(1)));
647 else
648 SortDefinition = SortDefinition.Ascending(Serializer.ToShortName(SortBy));
649 }
650 }
651
652 ResultSet = ResultSet.Sort(SortDefinition);
653 }
654
655 if (Offset > 0)
656 ResultSet = ResultSet.Skip(Offset);
657
658 if (MaxCount < int.MaxValue)
659 ResultSet = ResultSet.Limit(MaxCount);
660
661 IAsyncCursor<BsonDocument> Cursor = await ResultSet.ToCursorAsync();
662 LinkedList<T> Result = new LinkedList<T>();
663 BsonDeserializationArgs Args = new BsonDeserializationArgs()
664 {
665 NominalType = typeof(T)
666 };
667
668 while (await Cursor.MoveNextAsync())
669 {
670 foreach (BsonDocument Document in Cursor.Current)
671 {
672 BsonDocumentReader Reader = new BsonDocumentReader(Document);
673 BsonDeserializationContext Context = BsonDeserializationContext.CreateRoot(Reader);
674
675 if (Serializer.Deserialize(Context, Args) is T Obj)
676 Result.AddLast(Obj);
677 }
678 }
679
680 return Result;
681 }
682
683 internal FilterDefinition<BsonDocument> Convert(Filter Filter, ObjectSerializer Serializer)
684 {
686 {
687 Filter[] ChildFilters = FilterChildren.ChildFilters;
688 int i, c = ChildFilters.Length;
689 FilterDefinition<BsonDocument>[] Children = new FilterDefinition<BsonDocument>[c];
690
691 for (i = 0; i < c; i++)
692 Children[i] = this.Convert(ChildFilters[i], Serializer);
693
694 if (Filter is FilterAnd)
695 return Builders<BsonDocument>.Filter.And(Children);
696 else if (Filter is FilterOr)
697 return Builders<BsonDocument>.Filter.Or(Children);
698 else
699 throw this.UnknownFilterType(Filter);
700 }
701 else if (Filter is FilterChild FilterChild)
702 {
703 FilterDefinition<BsonDocument> Child = this.Convert(FilterChild.ChildFilter, Serializer);
704
705 if (Filter is FilterNot)
706 return Builders<BsonDocument>.Filter.Not(Child);
707 else
708 throw this.UnknownFilterType(Filter);
709 }
711 {
712 object Value = FilterFieldValue.Value;
713 string FieldName = Serializer.ToShortName(FilterFieldValue.FieldName, ref Value);
714 bool HasType = Serializer.TryGetFieldType(FilterFieldValue.FieldName, null, out Type FieldType);
715 bool IsDefaultValue = Serializer.IsDefaultValue(FilterFieldValue.FieldName, Value);
716
718 {
719 if (IsDefaultValue)
720 return Builders<BsonDocument>.Filter.Eq<string>(FieldName, null);
721 else if (Value is string s)
722 {
723 if (HasType && FieldType == typeof(CaseInsensitiveString))
724 return Builders<BsonDocument>.Filter.Eq<string>(FieldName + "_L", s);
725 else
726 return Builders<BsonDocument>.Filter.Eq<string>(FieldName, s);
727 }
728 else if (Value is CaseInsensitiveString cis)
729 return Builders<BsonDocument>.Filter.Eq<string>(FieldName + "_L", cis.LowerCase);
730 else if (Value is sbyte i8)
731 return Builders<BsonDocument>.Filter.Eq<int>(FieldName, i8);
732 else if (Value is short i16)
733 return Builders<BsonDocument>.Filter.Eq<int>(FieldName, i16);
734 else if (Value is int i32)
735 return Builders<BsonDocument>.Filter.Eq<int>(FieldName, i32);
736 else if (Value is long i64)
737 return Builders<BsonDocument>.Filter.Eq<long>(FieldName, i64);
738 else if (Value is byte ui8)
739 return Builders<BsonDocument>.Filter.Eq<int>(FieldName, ui8);
740 else if (Value is ushort ui16)
741 return Builders<BsonDocument>.Filter.Eq<int>(FieldName, ui16);
742 else if (Value is uint ui32)
743 return Builders<BsonDocument>.Filter.Eq<long>(FieldName, ui32);
744 else if (Value is ulong ui64)
745 return Builders<BsonDocument>.Filter.Eq<Decimal128>(FieldName, ui64);
746 else if (Value is double d)
747 return Builders<BsonDocument>.Filter.Eq<double>(FieldName, d);
748 else if (Value is float f)
749 return Builders<BsonDocument>.Filter.Eq<double>(FieldName, f);
750 else if (Value is decimal d2)
751 return Builders<BsonDocument>.Filter.Eq<Decimal128>(FieldName, d2);
752 else if (Value is bool b)
753 return Builders<BsonDocument>.Filter.Eq<bool>(FieldName, b);
754 else if (Value is DateTime DT)
755 return Builders<BsonDocument>.Filter.Eq<long>(FieldName, (long)(DT - ObjectSerializer.UnixEpoch).TotalMilliseconds);
756 else if (Value is DateTimeOffset DTO)
757 {
758 return Builders<BsonDocument>.Filter.And(
759 Builders<BsonDocument>.Filter.Eq<long>(FieldName + ".tp", (long)(DTO.DateTime - ObjectSerializer.UnixEpoch).TotalMilliseconds),
760 Builders<BsonDocument>.Filter.Eq<string>(FieldName + ".tz", DTO.Offset.ToString()));
761 }
762 else if (Value is TimeSpan TS)
763 return Builders<BsonDocument>.Filter.Eq<string>(FieldName, TS.ToString());
764 else if (Value is Guid Guid)
765 return Builders<BsonDocument>.Filter.Eq<string>(FieldName, Guid.ToString());
766 else if (Value is ObjectId ObjectId)
767 return Builders<BsonDocument>.Filter.Eq<ObjectId>(FieldName, ObjectId);
768 else
769 throw this.UnhandledFilterValueDataType(Serializer.ValueType.FullName, FieldName, Value);
770 }
771 else if (Filter is FilterFieldNotEqualTo)
772 {
773 if (IsDefaultValue)
774 return Builders<BsonDocument>.Filter.Ne<string>(FieldName, null);
775 else if (Value is string s)
776 {
777 if (HasType && FieldType == typeof(CaseInsensitiveString))
778 return Builders<BsonDocument>.Filter.Ne<string>(FieldName + "_L", s);
779 else
780 return Builders<BsonDocument>.Filter.Ne<string>(FieldName, s);
781 }
782 else if (Value is CaseInsensitiveString cis)
783 return Builders<BsonDocument>.Filter.Ne<string>(FieldName + "_L", cis.LowerCase);
784 else if (Value is sbyte i8)
785 return Builders<BsonDocument>.Filter.Ne<int>(FieldName, i8);
786 else if (Value is short i16)
787 return Builders<BsonDocument>.Filter.Ne<int>(FieldName, i16);
788 else if (Value is int i32)
789 return Builders<BsonDocument>.Filter.Ne<int>(FieldName, i32);
790 else if (Value is long i64)
791 return Builders<BsonDocument>.Filter.Ne<long>(FieldName, i64);
792 else if (Value is byte ui8)
793 return Builders<BsonDocument>.Filter.Ne<int>(FieldName, ui8);
794 else if (Value is ushort ui16)
795 return Builders<BsonDocument>.Filter.Ne<int>(FieldName, ui16);
796 else if (Value is uint ui32)
797 return Builders<BsonDocument>.Filter.Ne<long>(FieldName, ui32);
798 else if (Value is ulong ui64)
799 return Builders<BsonDocument>.Filter.Ne<Decimal128>(FieldName, ui64);
800 else if (Value is double d)
801 return Builders<BsonDocument>.Filter.Ne<double>(FieldName, d);
802 else if (Value is float f)
803 return Builders<BsonDocument>.Filter.Ne<double>(FieldName, f);
804 else if (Value is decimal d2)
805 return Builders<BsonDocument>.Filter.Ne<Decimal128>(FieldName, d2);
806 else if (Value is bool b)
807 return Builders<BsonDocument>.Filter.Ne<bool>(FieldName, b);
808 else if (Value is DateTime DT)
809 return Builders<BsonDocument>.Filter.Ne<long>(FieldName, (long)(DT - ObjectSerializer.UnixEpoch).TotalMilliseconds);
810 else if (Value is DateTimeOffset DTO)
811 {
812 return Builders<BsonDocument>.Filter.Or(
813 Builders<BsonDocument>.Filter.Ne<long>(FieldName + ".tp", (long)(DTO.DateTime - ObjectSerializer.UnixEpoch).TotalMilliseconds),
814 Builders<BsonDocument>.Filter.Ne<string>(FieldName + ".tz", DTO.Offset.ToString()));
815 }
816 else if (Value is TimeSpan TS)
817 return Builders<BsonDocument>.Filter.Ne<string>(FieldName, TS.ToString());
818 else if (Value is Guid Guid)
819 return Builders<BsonDocument>.Filter.Ne<string>(FieldName, Guid.ToString());
820 else if (Value is ObjectId ObjectId)
821 return Builders<BsonDocument>.Filter.Ne<ObjectId>(FieldName, ObjectId);
822 else
823 throw this.UnhandledFilterValueDataType(Serializer.ValueType.FullName, FieldName, Value);
824 }
825 else if (Filter is FilterFieldGreaterThan)
826 {
827 if (Value is string s)
828 {
829 if (HasType && FieldType == typeof(CaseInsensitiveString))
830 return Builders<BsonDocument>.Filter.Gt<string>(FieldName + "_L", s);
831 else
832 return Builders<BsonDocument>.Filter.Gt<string>(FieldName, s);
833 }
834 else if (Value is CaseInsensitiveString cis)
835 return Builders<BsonDocument>.Filter.Gt<string>(FieldName + "_L", cis.LowerCase);
836 else if (Value is sbyte i8)
837 return Builders<BsonDocument>.Filter.Gt<int>(FieldName, i8);
838 else if (Value is short i16)
839 return Builders<BsonDocument>.Filter.Gt<int>(FieldName, i16);
840 else if (Value is int i32)
841 return Builders<BsonDocument>.Filter.Gt<int>(FieldName, i32);
842 else if (Value is long i64)
843 return Builders<BsonDocument>.Filter.Gt<long>(FieldName, i64);
844 else if (Value is byte ui8)
845 return Builders<BsonDocument>.Filter.Gt<int>(FieldName, ui8);
846 else if (Value is ushort ui16)
847 return Builders<BsonDocument>.Filter.Gt<int>(FieldName, ui16);
848 else if (Value is uint ui32)
849 return Builders<BsonDocument>.Filter.Gt<long>(FieldName, ui32);
850 else if (Value is ulong ui64)
851 return Builders<BsonDocument>.Filter.Gt<Decimal128>(FieldName, ui64);
852 else if (Value is double d)
853 return Builders<BsonDocument>.Filter.Gt<double>(FieldName, d);
854 else if (Value is float f)
855 return Builders<BsonDocument>.Filter.Gt<double>(FieldName, f);
856 else if (Value is decimal d2)
857 return Builders<BsonDocument>.Filter.Gt<Decimal128>(FieldName, d2);
858 else if (Value is bool b)
859 return Builders<BsonDocument>.Filter.Gt<bool>(FieldName, b);
860 else if (Value is DateTime DT)
861 return Builders<BsonDocument>.Filter.Gt<long>(FieldName, (long)(DT - ObjectSerializer.UnixEpoch).TotalMilliseconds);
862 else if (Value is TimeSpan TS)
863 return Builders<BsonDocument>.Filter.Gt<string>(FieldName, TS.ToString());
864 else if (Value is Guid Guid)
865 return Builders<BsonDocument>.Filter.Gt<string>(FieldName, Guid.ToString());
866 else if (Value is ObjectId ObjectId)
867 return Builders<BsonDocument>.Filter.Gt<ObjectId>(FieldName, ObjectId);
868 else
869 throw this.UnhandledFilterValueDataType(Serializer.ValueType.FullName, FieldName, Value);
870 }
872 {
873 if (IsDefaultValue)
874 {
875 return this.Convert(new FilterOr(new FilterFieldGreaterThan(FieldName, Value),
876 new FilterFieldEqualTo(FieldName, Value)), Serializer);
877 }
878 else if (Value is string s)
879 {
880 if (HasType && FieldType == typeof(CaseInsensitiveString))
881 return Builders<BsonDocument>.Filter.Gte<string>(FieldName + "_L", s);
882 else
883 return Builders<BsonDocument>.Filter.Gte<string>(FieldName, s);
884 }
885 else if (Value is CaseInsensitiveString cis)
886 return Builders<BsonDocument>.Filter.Gte<string>(FieldName + "_L", cis.LowerCase);
887 else if (Value is sbyte i8)
888 return Builders<BsonDocument>.Filter.Gte<int>(FieldName, i8);
889 else if (Value is short i16)
890 return Builders<BsonDocument>.Filter.Gte<int>(FieldName, i16);
891 else if (Value is int i32)
892 return Builders<BsonDocument>.Filter.Gte<int>(FieldName, i32);
893 else if (Value is long i64)
894 return Builders<BsonDocument>.Filter.Gte<long>(FieldName, i64);
895 else if (Value is byte ui8)
896 return Builders<BsonDocument>.Filter.Gte<int>(FieldName, ui8);
897 else if (Value is ushort ui16)
898 return Builders<BsonDocument>.Filter.Gte<int>(FieldName, ui16);
899 else if (Value is uint ui32)
900 return Builders<BsonDocument>.Filter.Gte<long>(FieldName, ui32);
901 else if (Value is ulong ui64)
902 return Builders<BsonDocument>.Filter.Gte<Decimal128>(FieldName, ui64);
903 else if (Value is double d)
904 return Builders<BsonDocument>.Filter.Gte<double>(FieldName, d);
905 else if (Value is float f)
906 return Builders<BsonDocument>.Filter.Gte<double>(FieldName, f);
907 else if (Value is decimal d2)
908 return Builders<BsonDocument>.Filter.Gte<Decimal128>(FieldName, d2);
909 else if (Value is bool b)
910 return Builders<BsonDocument>.Filter.Gte<bool>(FieldName, b);
911 else if (Value is DateTime DT)
912 return Builders<BsonDocument>.Filter.Gte<long>(FieldName, (long)(DT - ObjectSerializer.UnixEpoch).TotalMilliseconds);
913 else if (Value is TimeSpan TS)
914 return Builders<BsonDocument>.Filter.Gte<string>(FieldName, TS.ToString());
915 else if (Value is Guid Guid)
916 return Builders<BsonDocument>.Filter.Gte<string>(FieldName, Guid.ToString());
917 else if (Value is ObjectId ObjectId)
918 return Builders<BsonDocument>.Filter.Gte<ObjectId>(FieldName, ObjectId);
919 else
920 throw this.UnhandledFilterValueDataType(Serializer.ValueType.FullName, FieldName, Value);
921 }
922 else if (Filter is FilterFieldLesserThan)
923 {
924 if (Value is string s)
925 {
926 if (HasType && FieldType == typeof(CaseInsensitiveString))
927 return Builders<BsonDocument>.Filter.Lt<string>(FieldName + "_L", s);
928 else
929 return Builders<BsonDocument>.Filter.Lt<string>(FieldName, s);
930 }
931 else if (Value is CaseInsensitiveString cis)
932 return Builders<BsonDocument>.Filter.Lt<string>(FieldName + "_L", cis.LowerCase);
933 else if (Value is sbyte i8)
934 return Builders<BsonDocument>.Filter.Lt<int>(FieldName, i8);
935 else if (Value is short i16)
936 return Builders<BsonDocument>.Filter.Lt<int>(FieldName, i16);
937 else if (Value is int i32)
938 return Builders<BsonDocument>.Filter.Lt<int>(FieldName, i32);
939 else if (Value is long i64)
940 return Builders<BsonDocument>.Filter.Lt<long>(FieldName, i64);
941 else if (Value is byte ui8)
942 return Builders<BsonDocument>.Filter.Lt<int>(FieldName, ui8);
943 else if (Value is ushort ui16)
944 return Builders<BsonDocument>.Filter.Lt<int>(FieldName, ui16);
945 else if (Value is uint ui32)
946 return Builders<BsonDocument>.Filter.Lt<long>(FieldName, ui32);
947 else if (Value is ulong ui64)
948 return Builders<BsonDocument>.Filter.Lt<Decimal128>(FieldName, ui64);
949 else if (Value is double d)
950 return Builders<BsonDocument>.Filter.Lt<double>(FieldName, d);
951 else if (Value is float f)
952 return Builders<BsonDocument>.Filter.Lt<double>(FieldName, f);
953 else if (Value is decimal d2)
954 return Builders<BsonDocument>.Filter.Lt<Decimal128>(FieldName, d2);
955 else if (Value is bool b)
956 return Builders<BsonDocument>.Filter.Lt<bool>(FieldName, b);
957 else if (Value is DateTime DT)
958 return Builders<BsonDocument>.Filter.Lt<long>(FieldName, (long)(DT - ObjectSerializer.UnixEpoch).TotalMilliseconds);
959 else if (Value is TimeSpan TS)
960 return Builders<BsonDocument>.Filter.Lt<string>(FieldName, TS.ToString());
961 else if (Value is Guid Guid)
962 return Builders<BsonDocument>.Filter.Lt<string>(FieldName, Guid.ToString());
963 else if (Value is ObjectId ObjectId)
964 return Builders<BsonDocument>.Filter.Lt<ObjectId>(FieldName, ObjectId);
965 else
966 throw this.UnhandledFilterValueDataType(Serializer.ValueType.FullName, FieldName, Value);
967 }
969 {
970 if (IsDefaultValue)
971 {
972 return this.Convert(new FilterOr(new FilterFieldLesserThan(FieldName, Value),
973 new FilterFieldEqualTo(FieldName, Value)), Serializer);
974 }
975 else if (Value is string s)
976 {
977 if (HasType && FieldType == typeof(CaseInsensitiveString))
978 return Builders<BsonDocument>.Filter.Lte<string>(FieldName + "_L", s);
979 else
980 return Builders<BsonDocument>.Filter.Lte<string>(FieldName, s);
981 }
982 else if (Value is CaseInsensitiveString cis)
983 return Builders<BsonDocument>.Filter.Lte<string>(FieldName + "_L", cis.LowerCase);
984 else if (Value is sbyte i8)
985 return Builders<BsonDocument>.Filter.Lte<int>(FieldName, i8);
986 else if (Value is short i16)
987 return Builders<BsonDocument>.Filter.Lte<int>(FieldName, i16);
988 else if (Value is int i32)
989 return Builders<BsonDocument>.Filter.Lte<int>(FieldName, i32);
990 else if (Value is long i64)
991 return Builders<BsonDocument>.Filter.Lte<long>(FieldName, i64);
992 else if (Value is byte ui8)
993 return Builders<BsonDocument>.Filter.Lte<int>(FieldName, ui8);
994 else if (Value is ushort ui16)
995 return Builders<BsonDocument>.Filter.Lte<int>(FieldName, ui16);
996 else if (Value is uint ui32)
997 return Builders<BsonDocument>.Filter.Lte<long>(FieldName, ui32);
998 else if (Value is ulong ui64)
999 return Builders<BsonDocument>.Filter.Lte<Decimal128>(FieldName, ui64);
1000 else if (Value is double d)
1001 return Builders<BsonDocument>.Filter.Lte<double>(FieldName, d);
1002 else if (Value is float f)
1003 return Builders<BsonDocument>.Filter.Lte<double>(FieldName, f);
1004 else if (Value is decimal d2)
1005 return Builders<BsonDocument>.Filter.Lte<Decimal128>(FieldName, d2);
1006 else if (Value is bool b)
1007 return Builders<BsonDocument>.Filter.Lte<bool>(FieldName, b);
1008 else if (Value is DateTime DT)
1009 return Builders<BsonDocument>.Filter.Lte<long>(FieldName, (long)(DT - ObjectSerializer.UnixEpoch).TotalMilliseconds);
1010 else if (Value is TimeSpan TS)
1011 return Builders<BsonDocument>.Filter.Lte<string>(FieldName, TS.ToString());
1012 else if (Value is Guid Guid)
1013 return Builders<BsonDocument>.Filter.Lte<string>(FieldName, Guid.ToString());
1014 else if (Value is ObjectId ObjectId)
1015 return Builders<BsonDocument>.Filter.Lte<ObjectId>(FieldName, ObjectId);
1016 else
1017 throw this.UnhandledFilterValueDataType(Serializer.ValueType.FullName, FieldName, Value);
1018 }
1019 else
1020 throw this.UnknownFilterType(Filter);
1021 }
1022 else
1023 {
1025 {
1026 return Builders<BsonDocument>.Filter.Regex(Serializer.ToShortName(FilterFieldLikeRegEx.FieldName),
1028 }
1029 else
1030 throw this.UnknownFilterType(Filter);
1031 }
1032 }
1033
1034 private Exception UnknownFilterType(Filter Filter)
1035 {
1036 return new NotSupportedException("Filters of type " + Filter.GetType().FullName + " not supported.");
1037 }
1038
1039 private Exception UnhandledFilterValueDataType(string TypeName, string FieldName, object Value)
1040 {
1041 if (Value is null)
1042 {
1043 return new NotSupportedException("Null filter values for field " + TypeName + "." + FieldName +
1044 " not supported.");
1045 }
1046 else
1047 {
1048 return new NotSupportedException("Filter values of type " + Value.GetType().FullName +
1049 " for field " + TypeName + "." + FieldName + " not supported.");
1050 }
1051 }
1052
1061 public async Task<IPage<T>> FindFirst<T>(int PageSize, params string[] SortOrder)
1062 where T : class
1063 {
1064 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(T));
1065 IEnumerable<T> Items = await this.Find<T>(0, PageSize, SortOrder);
1066 return new Page<T>(PageSize, null, null, SortOrder, Items, Serializer, this);
1067 }
1068
1078 public async Task<IPage<T>> FindFirst<T>(int PageSize, Filter Filter, params string[] SortOrder)
1079 where T : class
1080 {
1081 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(T));
1082 IEnumerable<T> Items = await this.Find<T>(0, PageSize, Filter, SortOrder);
1083 return new Page<T>(PageSize, null, Filter, SortOrder, Items, Serializer, this);
1084 }
1085
1094 public async Task<IPage<object>> FindFirst(string Collection, int PageSize, params string[] SortOrder)
1095 {
1096 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(object));
1097 IEnumerable<object> Items = await this.Find(Collection, 0, PageSize, SortOrder);
1098 return new Page<object>(PageSize, Collection, null, SortOrder, Items, Serializer, this);
1099 }
1100
1110 public async Task<IPage<object>> FindFirst(string Collection, int PageSize, Filter Filter, params string[] SortOrder)
1111 {
1112 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(object));
1113 IEnumerable<object> Items = await this.Find(Collection, 0, PageSize, Filter, SortOrder);
1114 return new Page<object>(PageSize, Collection, Filter, SortOrder, Items, Serializer, this);
1115 }
1116
1127 public async Task<IPage<T>> FindFirst<T>(string Collection, int PageSize, Filter Filter, params string[] SortOrder)
1128 where T : class
1129 {
1130 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(T));
1131 IEnumerable<T> Items = await this.Find<T>(Collection, 0, PageSize, Filter, SortOrder);
1132 return new Page<T>(PageSize, Collection, Filter, SortOrder, Items, Serializer, this);
1133 }
1134
1141 public Task<IPage<T>> FindNext<T>(IPage<T> Page)
1142 where T : class
1143 {
1144 if (Page is Page<T> CurrentPage)
1145 return CurrentPage.FindNext();
1146 else
1147 throw new IOException("Incompatible page.");
1148 }
1149
1155 public Task<IPage<object>> FindNext(IPage<object> Page)
1156 {
1157 if (Page is Page<object> CurrentPage)
1158 return CurrentPage.FindNext();
1159 else
1160 throw new IOException("Incompatible page.");
1161 }
1162
1169 public Task<T> TryLoadObject<T>(object ObjectId)
1170 where T : class
1171 {
1172 ObjectId OID;
1173
1174 if (ObjectId is ObjectId ObjId)
1175 OID = ObjId;
1176 else if (ObjectId is string s)
1177 OID = new ObjectId(s);
1178 else if (ObjectId is byte[] A)
1179 OID = new ObjectId(A);
1180 else if (ObjectId is Guid Guid)
1182 else
1183 throw new NotSupportedException("Unsupported type for Object ID: " + ObjectId.GetType().FullName);
1184
1185 return this.TryLoadObject<T>(OID);
1186 }
1187
1194 public async Task<T> TryLoadObject<T>(ObjectId ObjectId)
1195 where T : class
1196 {
1197 string Key = typeof(T).FullName + " " + ObjectId.ToString();
1198
1199 if (this.loadCache.TryGetValue(Key, out object Obj) && Obj is T Result)
1200 return Result;
1201
1202 ObjectSerializer S = this.GetObjectSerializerEx(typeof(T));
1203 IEnumerable<T> ReferencedObjects = await this.Find<T>(0, 2, new FilterFieldEqualTo(S.ObjectIdMemberName, ObjectId));
1204 T First = default;
1205
1206 foreach (T Item in ReferencedObjects)
1207 {
1208 if (First is null)
1209 First = Item;
1210 else
1211 throw new Exception("Multiple objects of type T found with object ID " + ObjectId.ToString());
1212 }
1213
1214 if (!(First is null))
1215 this.loadCache.Add(Key, First); // Speeds up readout if reading multiple objects referencing a few common sub-objects.
1216
1217 return First;
1218 }
1219
1227 public Task<T> TryLoadObject<T>(string CollectionName, object ObjectId)
1228 where T : class
1229 {
1230 ObjectId OID;
1231
1232 if (ObjectId is ObjectId ObjId)
1233 OID = ObjId;
1234 else if (ObjectId is string s)
1235 OID = new ObjectId(s);
1236 else if (ObjectId is byte[] A)
1237 OID = new ObjectId(A);
1238 else if (ObjectId is Guid Guid)
1240 else
1241 throw new NotSupportedException("Unsupported type for Object ID: " + ObjectId.GetType().FullName);
1242
1243 return this.TryLoadObject<T>(CollectionName, OID);
1244 }
1245
1253 public async Task<T> TryLoadObject<T>(string CollectionName, ObjectId ObjectId)
1254 where T : class
1255 {
1256 string Key = typeof(T).FullName + " " + ObjectId.ToString();
1257
1258 if (this.loadCache.TryGetValue(Key, out object Obj) && Obj is T Result)
1259 return Result;
1260
1261 ObjectSerializer S = this.GetObjectSerializerEx(typeof(T));
1262 IEnumerable<T> ReferencedObjects = await this.Find<T>(CollectionName, 0, 2, new FilterFieldEqualTo(S.ObjectIdMemberName, ObjectId));
1263 T First = default;
1264
1265 foreach (T Item in ReferencedObjects)
1266 {
1267 if (First is null)
1268 First = Item;
1269 else
1270 throw new Exception("Multiple objects of type T found with object ID " + ObjectId.ToString());
1271 }
1272
1273 if (!(First is null))
1274 this.loadCache.Add(Key, First); // Speeds up readout if reading multiple objects referencing a few common sub-objects.
1275
1276 return First;
1277 }
1278
1285 public async Task<object> TryLoadObject(string CollectionName, object ObjectId)
1286 {
1287 ObjectId OID;
1288
1289 if (ObjectId is ObjectId ObjId)
1290 OID = ObjId;
1291 else if (ObjectId is string s)
1292 OID = new ObjectId(s);
1293 else if (ObjectId is byte[] A)
1294 OID = new ObjectId(A);
1295 else if (ObjectId is Guid Guid)
1297 else
1298 throw new NotSupportedException("Unsupported type for Object ID: " + ObjectId.GetType().FullName);
1299
1300 ObjectSerializer S = this.GetObjectSerializerEx(typeof(object));
1301 IEnumerable<object> ReferencedObjects = await this.Find(CollectionName, 0, 2, new FilterFieldEqualTo(S.ObjectIdMemberName, OID));
1302 object First = null;
1303
1304 foreach (object Item in ReferencedObjects)
1305 {
1306 if (First is null)
1307 First = Item;
1308 else
1309 throw new Exception("Multiple objects of type T found with object ID " + ObjectId.ToString());
1310 }
1311
1312 return First;
1313 }
1314
1315 private readonly Cache<string, object> loadCache = new Cache<string, object>(10000, new TimeSpan(0, 0, 10), new TimeSpan(0, 0, 5), true); // TODO: Make parameters configurable.
1316
1321 public async Task Update(object Object)
1322 {
1323 ObjectSerializer Serializer = this.GetObjectSerializerEx(Object);
1324 ObjectId ObjectId = await Serializer.GetObjectId(Object, false);
1325 string CollectionName = Serializer.CollectionName(Object);
1326 IMongoCollection<BsonDocument> Collection;
1327
1328 if (string.IsNullOrEmpty(CollectionName))
1329 Collection = this.defaultCollection;
1330 else
1331 Collection = this.GetCollection(CollectionName);
1332
1333 BsonDocument Doc = Object.ToBsonDocument(Object.GetType(), Serializer);
1334 await Collection.ReplaceOneAsync(Builders<BsonDocument>.Filter.Eq<ObjectId>("_id", ObjectId), Doc);
1335 }
1336
1341 public Task Update(params object[] Objects)
1342 {
1343 return this.Update((IEnumerable<object>)Objects);
1344 }
1345
1350 public async Task Update(IEnumerable<object> Objects)
1351 {
1352 foreach (object Obj in Objects)
1353 await this.Update(Obj);
1354 }
1355
1361 public Task UpdateLazy(object Object, ObjectCallback Callback)
1362 => this.Process(Object, this.Update(Object), Callback);
1363
1369 public Task UpdateLazy(object[] Objects, ObjectsCallback Callback)
1370 => this.Process(Objects, this.Update(Objects), Callback);
1371
1377 public Task UpdateLazy(IEnumerable<object> Objects, ObjectsCallback Callback)
1378 => this.Process(Objects, this.Update(Objects), Callback);
1379
1384 public async Task Delete(object Object)
1385 {
1386 ObjectSerializer Serializer = this.GetObjectSerializerEx(Object);
1387 ObjectId ObjectId = await Serializer.GetObjectId(Object, false);
1388 string CollectionName = Serializer.CollectionName(Object);
1389 IMongoCollection<BsonDocument> Collection;
1390
1391 if (string.IsNullOrEmpty(CollectionName))
1392 Collection = this.defaultCollection;
1393 else
1394 Collection = this.GetCollection(CollectionName);
1395
1396 await Collection.DeleteOneAsync(Builders<BsonDocument>.Filter.Eq<ObjectId>("_id", ObjectId));
1397 }
1398
1403 public Task Delete(params object[] Objects)
1404 {
1405 return this.Delete((IEnumerable<object>)Objects);
1406 }
1407
1412 public async Task Delete(IEnumerable<object> Objects)
1413 {
1414 foreach (object Obj in Objects)
1415 await this.Delete(Obj);
1416 }
1417
1418 private async Task Process(object Object, Task Op, ObjectCallback Callback)
1419 {
1420 await Op;
1421 if (!(Callback is null))
1422 Callback(Object);
1423 }
1424
1425 private async Task Process(IEnumerable<object> Objects, Task Op, ObjectsCallback Callback)
1426 {
1427 await Op;
1428
1429 if (!(Callback is null))
1430 Callback(Objects);
1431 }
1432
1438 public Task DeleteLazy(object Object, ObjectCallback Callback)
1439 => this.Process(Object, this.Delete(Object), Callback);
1440
1446 public Task DeleteLazy(object[] Objects, ObjectsCallback Callback)
1447 => this.Process(Objects, this.Delete(Objects), Callback);
1448
1454 public Task DeleteLazy(IEnumerable<object> Objects, ObjectsCallback Callback)
1455 => this.Process(Objects, this.Delete(Objects), Callback);
1456
1466 public async Task<IEnumerable<T>> FindDelete<T>(int Offset, int MaxCount, params string[] SortOrder)
1467 where T : class
1468 {
1469 IEnumerable<T> Result = await this.Find<T>(Offset, MaxCount, SortOrder);
1470 await this.Delete(Result);
1471 return Result;
1472 }
1473
1484 public async Task<IEnumerable<T>> FindDelete<T>(int Offset, int MaxCount, Filter Filter, params string[] SortOrder)
1485 where T : class
1486 {
1487 IEnumerable<T> Result = await this.Find<T>(Offset, MaxCount, Filter, SortOrder);
1488 await this.Delete(Result);
1489 return Result;
1490 }
1491
1501 public async Task<IEnumerable<object>> FindDelete(string Collection, int Offset, int MaxCount, params string[] SortOrder)
1502 {
1503 IEnumerable<object> Result = await this.Find(Collection, Offset, MaxCount, SortOrder);
1504 await this.Delete(Result);
1505 return Result;
1506 }
1507
1518 public async Task<IEnumerable<object>> FindDelete(string Collection, int Offset, int MaxCount, Filter Filter, params string[] SortOrder)
1519 {
1520 IEnumerable<object> Result = await this.Find(Collection, Offset, MaxCount, Filter, SortOrder);
1521 await this.Delete(Result);
1522 return Result;
1523 }
1524
1534 public async Task DeleteLazy<T>(int Offset, int MaxCount, string[] SortOrder, ObjectsCallback Callback)
1535 where T : class
1536 {
1537 IEnumerable<T> Objects = await this.FindDelete<T>(Offset, MaxCount, SortOrder);
1538 if (!(Callback is null))
1539 Callback(Objects);
1540 }
1541
1552 public async Task DeleteLazy<T>(int Offset, int MaxCount, Filter Filter, string[] SortOrder, ObjectsCallback Callback)
1553 where T : class
1554 {
1555 IEnumerable<T> Objects = await this.FindDelete<T>(Offset, MaxCount, Filter, SortOrder);
1556 if (!(Callback is null))
1557 Callback(Objects);
1558 }
1559
1569 public async Task DeleteLazy(string Collection, int Offset, int MaxCount, string[] SortOrder, ObjectsCallback Callback)
1570 {
1571 IEnumerable<object> Objects = await this.FindDelete(Collection, Offset, MaxCount, SortOrder);
1572 if (!(Callback is null))
1573 Callback(Objects);
1574 }
1575
1586 public async Task DeleteLazy(string Collection, int Offset, int MaxCount, Filter Filter, string[] SortOrder, ObjectsCallback Callback)
1587 {
1588 IEnumerable<object> Objects = await this.FindDelete(Collection, Offset, MaxCount, Filter, SortOrder);
1589 if (!(Callback is null))
1590 Callback(Objects);
1591 }
1592
1598 public Task Clear(string CollectionName)
1599 {
1600 IMongoCollection<BsonDocument> Collection = this.GetCollection(CollectionName);
1601 return Collection.DeleteManyAsync(FilterDefinition<BsonDocument>.Empty);
1602 }
1603
1610 public Task AddIndex(string CollectionName, string[] FieldNames)
1611 {
1612 IMongoCollection<BsonDocument> Collection;
1613 List<BsonDocument> Indices;
1614
1615 if (string.IsNullOrEmpty(CollectionName))
1616 Collection = this.DefaultCollection;
1617 else
1618 Collection = this.GetCollection(CollectionName);
1619
1620 IAsyncCursor<BsonDocument> Cursor = Collection.Indexes.List();
1621 Indices = Cursor.ToList<BsonDocument>();
1622
1623 return ObjectSerializer.CheckIndexExists(Collection, Indices, FieldNames, null);
1624 }
1625
1632 public Task RemoveIndex(string CollectionName, string[] FieldNames)
1633 {
1634 IMongoCollection<BsonDocument> Collection;
1635 List<BsonDocument> Indices;
1636
1637 if (string.IsNullOrEmpty(CollectionName))
1638 Collection = this.DefaultCollection;
1639 else
1640 Collection = this.GetCollection(CollectionName);
1641
1642 IAsyncCursor<BsonDocument> Cursor = Collection.Indexes.List();
1643 Indices = Cursor.ToList<BsonDocument>();
1644
1645 return ObjectSerializer.RemoveIndex(Collection, Indices, FieldNames);
1646 }
1647
1655 public Task<string[]> Analyze(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData)
1656 {
1657 return this.Analyze(Output, XsltPath, ProgramDataFolder, ExportData, false);
1658 }
1659
1668 public Task<string[]> Analyze(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData, ProfilerThread Thread)
1669 {
1670 return this.Analyze(Output, XsltPath, ProgramDataFolder, ExportData, false, Thread);
1671 }
1672
1680 public Task<string[]> Repair(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData)
1681 {
1682 return this.Analyze(Output, XsltPath, ProgramDataFolder, ExportData, true);
1683 }
1684
1693 public Task<string[]> Repair(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData, ProfilerThread Thread)
1694 {
1695 return this.Analyze(Output, XsltPath, ProgramDataFolder, ExportData, true, Thread);
1696 }
1697
1706 public Task<string[]> Analyze(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData, bool Repair)
1707 {
1708 return this.Analyze(null, XsltPath, ProgramDataFolder, ExportData, Repair, null);
1709 }
1710
1720 public async Task<string[]> Analyze(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData, bool Repair,
1721 ProfilerThread Thread)
1722 {
1723 Thread?.Start();
1724 Output.WriteStartDocument();
1725
1726 if (!string.IsNullOrEmpty(XsltPath))
1727 {
1728 if (File.Exists(XsltPath))
1729 {
1730 try
1731 {
1732 byte[] XsltBin = File.ReadAllBytes(XsltPath);
1733
1734 Output.WriteProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"data:text/xsl;base64," +
1735 System.Convert.ToBase64String(XsltBin) + "\"");
1736 }
1737 catch (Exception)
1738 {
1739 Output.WriteProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"" + Encode(XsltPath) + "\"");
1740 }
1741 }
1742 else
1743 Output.WriteProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"" + Encode(XsltPath) + "\"");
1744 }
1745
1746 Output.WriteStartElement("DatabaseStatistics", "http://waher.se/Schema/Persistence/Statistics.xsd");
1747
1748 foreach (string CollectionName in (await this.database.ListCollectionNamesAsync()).ToEnumerable())
1749 {
1750 Thread?.NewState(CollectionName);
1751
1752 IMongoCollection<BsonDocument> Collection = this.database.GetCollection<BsonDocument>(CollectionName);
1753
1754 Output.WriteStartElement("File");
1755 Output.WriteAttributeString("id", Collection.CollectionNamespace.FullName);
1756 Output.WriteAttributeString("collectionName", CollectionName);
1757 Output.WriteAttributeString("count", (await Collection.CountDocumentsAsync(Builders<BsonDocument>.Filter.Empty)).ToString());
1758
1759 if (!(Collection.Settings.WriteEncoding is null))
1760 Output.WriteAttributeString("encoding", Collection.Settings.WriteEncoding.WebName);
1761
1762 if (Collection.Settings.WriteConcern.WTimeout.HasValue)
1763 Output.WriteAttributeString("timeoutMs", ((int)Collection.Settings.WriteConcern.WTimeout.Value.TotalMilliseconds).ToString());
1764
1765 foreach (BsonDocument Index in (await Collection.Indexes.ListAsync()).ToEnumerable())
1766 {
1767 List<string> FieldNames = new List<string>();
1768
1769 Output.WriteStartElement("Index");
1770
1771 foreach (BsonElement E in Index.Elements)
1772 {
1773 switch (E.Name)
1774 {
1775 case "key":
1776 foreach (BsonElement E2 in E.Value.AsBsonDocument.Elements)
1777 {
1778 if (E2.Value.AsInt32 < 0)
1779 FieldNames.Add("-" + E2.Name);
1780 else
1781 FieldNames.Add(E2.Name);
1782 }
1783 break;
1784
1785 case "name":
1786 Output.WriteAttributeString("id", E.Value.AsString);
1787 break;
1788 }
1789 }
1790
1791 foreach (string Field in FieldNames)
1792 Output.WriteElementString("Field", Field);
1793
1794 Output.WriteEndElement();
1795 }
1796
1797 Output.WriteEndElement();
1798 }
1799
1800 Output.WriteEndElement();
1801 Output.WriteEndDocument();
1802
1803 Thread?.Idle();
1804 Thread?.Stop();
1805
1806 return new string[0];
1807 }
1808
1814 public Task<string[]> Repair(params string[] CollectionNames)
1815 {
1816 return Task.FromResult<string[]>(new string[0]);
1817 }
1818
1825 public Task<string[]> Repair(ProfilerThread Thread, params string[] CollectionNames)
1826 {
1827 return Task.FromResult<string[]>(new string[0]);
1828 }
1829
1830 private static string Encode(string s)
1831 {
1832 return s.
1833 Replace("&", "&amp;").
1834 Replace("<", "&lt;").
1835 Replace(">", "&gt;").
1836 Replace("\"", "&quot;").
1837 Replace("'", "&apos;");
1838 }
1839
1846 public Task<bool> Export(IDatabaseExport Output, string[] CollectionNames)
1847 {
1848 return this.Export(Output, CollectionNames, null);
1849 }
1850
1858 public async Task<bool> Export(IDatabaseExport Output, string[] CollectionNames, ProfilerThread Thread)
1859 {
1860 bool Continue;
1861
1862 Thread?.Start();
1863 if (!await Output.StartDatabase())
1864 return false;
1865 try
1866 {
1868 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(GenericObject));
1869 BsonDeserializationArgs Args = new BsonDeserializationArgs()
1870 {
1871 NominalType = typeof(GenericObject)
1872 };
1873
1874 foreach (string CollectionName in (await this.database.ListCollectionNamesAsync()).ToEnumerable())
1875 {
1876 if (!(CollectionNames is null) && Array.IndexOf(CollectionNames, CollectionName) < 0)
1877 continue;
1878
1879 if (!(Filter is null) && !Filter.CanExportCollection(CollectionName))
1880 continue;
1881
1882 Thread?.NewState(CollectionName);
1883
1884 IMongoCollection<BsonDocument> Collection = this.database.GetCollection<BsonDocument>(CollectionName);
1885
1886 if (!await Output.StartCollection(CollectionName))
1887 return false;
1888 try
1889 {
1890 foreach (BsonDocument Index in (await Collection.Indexes.ListAsync()).ToEnumerable())
1891 {
1892 if (!await Output.StartIndex())
1893 return false;
1894
1895 foreach (BsonElement E in Index.Elements)
1896 {
1897 if (E.Name == "key")
1898 {
1899 foreach (BsonElement E2 in E.Value.AsBsonDocument.Elements)
1900 {
1901 if (!await Output.ReportIndexField(E2.Name, E2.Value.AsInt32 > 0))
1902 return false;
1903 }
1904
1905 break;
1906 }
1907 }
1908
1909 if (!await Output.EndIndex())
1910 return false;
1911 }
1912
1913 foreach (BsonDocument Doc in (await Collection.FindAsync<BsonDocument>(Builders<BsonDocument>.Filter.Empty)).ToEnumerable())
1914 {
1915 BsonDocumentReader Reader = new BsonDocumentReader(Doc);
1916 BsonDeserializationContext Context = BsonDeserializationContext.CreateRoot(Reader);
1917
1918 object Object = Serializer.Deserialize(Context, Args);
1919
1920 if (Object is GenericObject Obj)
1921 {
1922 if (!(Filter is null) && !Filter.CanExportObject(Obj))
1923 continue;
1924
1925 if (await Output.StartObject(Obj.ObjectId.ToString(), Obj.TypeName) is null)
1926 return false;
1927 try
1928 {
1929 foreach (KeyValuePair<string, object> P in Obj)
1930 {
1931 if (P.Value is ObjectId ObjectId)
1932 {
1933 if (!await Output.ReportProperty(P.Key, GeneratedObjectSerializerBase.ObjectIdToGuid(ObjectId)))
1934 return false;
1935 }
1936 else
1937 {
1938 if (!await Output.ReportProperty(P.Key, P.Value))
1939 return false;
1940 }
1941 }
1942 }
1943 catch (Exception ex)
1944 {
1945 Thread?.Exception(ex);
1946 if (!await this.ReportException(ex, Output))
1947 return false;
1948 }
1949 finally
1950 {
1951 Continue = await Output.EndObject();
1952 }
1953
1954 if (!Continue)
1955 return false;
1956 }
1957 else if (!(Object is null))
1958 {
1959 if (!await Output.ReportError("Unable to load object " + Doc["_id"].AsString + "."))
1960 return false;
1961 }
1962 }
1963 }
1964 catch (Exception ex)
1965 {
1966 Thread?.Exception(ex);
1967 if (!await this.ReportException(ex, Output))
1968 return false;
1969 }
1970 finally
1971 {
1972 Continue = await Output.EndCollection();
1973 }
1974
1975 if (!Continue)
1976 return false;
1977 }
1978 }
1979 catch (Exception ex)
1980 {
1981 Thread?.Exception(ex);
1982 if (!await this.ReportException(ex, Output))
1983 return false;
1984 }
1985 finally
1986 {
1987 Continue = await Output.EndDatabase();
1988 Thread?.Idle();
1989 Thread?.Stop();
1990 }
1991
1992 return Continue;
1993 }
1994
1995 private async Task<bool> ReportException(Exception ex, IDatabaseExport Output)
1996 {
1997 ex = Log.UnnestException(ex);
1998
1999 if (ex is AggregateException ex2)
2000 {
2001 foreach (Exception ex3 in ex2.InnerExceptions)
2002 {
2003 if (!await Output.ReportException(ex3))
2004 return false;
2005 }
2006
2007 return true;
2008 }
2009 else
2010 return await Output.ReportException(ex);
2011 }
2012
2020 public Task Iterate<T>(IDatabaseIteration<T> Recipient, string[] CollectionNames)
2021 where T : class
2022 {
2023 return this.Iterate(Recipient, CollectionNames, null);
2024 }
2025
2034 public async Task Iterate<T>(IDatabaseIteration<T> Recipient, string[] CollectionNames, ProfilerThread Thread)
2035 where T : class
2036 {
2037 Thread?.Start();
2038 await Recipient.StartDatabase();
2039 try
2040 {
2041 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(T));
2042 BsonDeserializationArgs Args = new BsonDeserializationArgs()
2043 {
2044 NominalType = typeof(GenericObject)
2045 };
2046
2047 foreach (string CollectionName in (await this.database.ListCollectionNamesAsync()).ToEnumerable())
2048 {
2049 if (!(CollectionNames is null) && Array.IndexOf(CollectionNames, CollectionName) < 0)
2050 continue;
2051
2052 Thread?.NewState(CollectionName);
2053
2054 IMongoCollection<BsonDocument> Collection = this.database.GetCollection<BsonDocument>(CollectionName);
2055
2056 await Recipient.StartCollection(CollectionName);
2057 try
2058 {
2059 foreach (BsonDocument Doc in (await Collection.FindAsync<BsonDocument>(Builders<BsonDocument>.Filter.Empty)).ToEnumerable())
2060 {
2061 BsonDocumentReader Reader = new BsonDocumentReader(Doc);
2062 BsonDeserializationContext Context = BsonDeserializationContext.CreateRoot(Reader);
2063
2064 object Object = Serializer.Deserialize(Context, Args);
2065
2066 if (Object is T Obj)
2067 await Recipient.ProcessObject(Obj);
2068 else if (!(Object is null))
2069 {
2070 ObjectId ObjectId = await Serializer.GetObjectId(Object, false);
2071 if (ObjectId != ObjectId.Empty)
2072 await Recipient.IncompatibleObject(ObjectId);
2073 }
2074 }
2075 }
2076 catch (Exception ex)
2077 {
2078 Thread?.Exception(ex);
2079 this.ReportException(ex, Recipient);
2080 }
2081 finally
2082 {
2083 await Recipient.EndCollection();
2084 }
2085 }
2086 }
2087 catch (Exception ex)
2088 {
2089 Thread?.Exception(ex);
2090 this.ReportException(ex, Recipient);
2091 }
2092 finally
2093 {
2094 await Recipient.EndDatabase();
2095 Thread?.Idle();
2096 Thread?.Stop();
2097 }
2098 }
2099
2100 private void ReportException<T>(Exception ex, IDatabaseIteration<T> Recipient)
2101 where T : class
2102 {
2103 ex = Events.Log.UnnestException(ex);
2104
2105 if (ex is AggregateException ex2)
2106 {
2107 foreach (Exception ex3 in ex2.InnerExceptions)
2108 Recipient.ReportException(ex3);
2109 }
2110 else
2111 Recipient.ReportException(ex);
2112 }
2113
2117 public Task StartBulk()
2118 {
2119 return Task.CompletedTask;
2120 }
2121
2125 public Task EndBulk()
2126 {
2127 return Task.CompletedTask;
2128 }
2129
2133 public Task Start()
2134 {
2135 return Task.CompletedTask;
2136 }
2137
2141 public Task Stop()
2142 {
2143 return Task.CompletedTask;
2144 }
2145
2149 public Task Flush()
2150 {
2151 return Task.CompletedTask;
2152 }
2153
2159 public Task<IPersistentDictionary> GetDictionary(string Collection)
2160 {
2161 return Task.FromResult<IPersistentDictionary>(new StringDictionary("DICT_" + Collection, this)); // TODO
2162 }
2163
2168 public async Task<string[]> GetDictionaries()
2169 {
2170 List<string> Collections = new List<string>();
2171
2172 foreach (string CollectionName in (await this.database.ListCollectionNamesAsync()).ToEnumerable())
2173 {
2174 if (CollectionName.StartsWith("DICT_"))
2175 Collections.Add(CollectionName);
2176 }
2177
2178 return Collections.ToArray();
2179 }
2180
2185 public async Task<string[]> GetCollections()
2186 {
2187 List<string> Collections = new List<string>();
2188
2189 foreach (string CollectionName in (await this.database.ListCollectionNamesAsync()).ToEnumerable())
2190 {
2191 if (!CollectionName.StartsWith("DICT_"))
2192 Collections.Add(CollectionName);
2193 }
2194
2195 return Collections.ToArray();
2196 }
2197
2203 public Task<string> GetCollection(Type Type)
2204 {
2205 ObjectSerializer Serializer = this.GetObjectSerializerEx(Type);
2206 return Task.FromResult<string>(Serializer.CollectionName(null));
2207 }
2208
2214 public Task<string> GetCollection(Object Object)
2215 {
2216 ObjectSerializer Serializer = this.GetObjectSerializerEx(Object);
2217 return Task.FromResult<string>(Serializer.CollectionName(Object));
2218 }
2219
2227 public async Task<bool> IsLabel(string CollectionName, string Label)
2228 {
2229 IMongoCollection<BsonDocument> Collection = this.GetCollection(CollectionName);
2230 FilterDefinition<BsonDocument> BsonFilter = Builders<BsonDocument>.Filter.Ne<string>(Label, null);
2231 IFindFluent<BsonDocument, BsonDocument> ResultSet = Collection.Find<BsonDocument>(BsonFilter);
2232
2233 return !(await ResultSet.SingleAsync<BsonDocument>() is null);
2234 }
2235
2240 public Task<string[]> GetLabels(string Collection)
2241 {
2242 throw new NotImplementedException();
2243 }
2244
2250 public async Task<object> TryGetObjectId(object Object)
2251 {
2252 if (Object is null)
2253 return null;
2254
2255 IObjectSerializer Serializer = this.GetObjectSerializer(Object.GetType());
2256 if (Serializer is ObjectSerializer SerializerEx &&
2257 SerializerEx.HasObjectId(Object))
2258 {
2259 return await SerializerEx.GetObjectId(Object, false);
2260 }
2261 else
2262 return null;
2263 }
2264
2269 public Task DropCollection(string CollectionName)
2270 {
2271 lock (this.collections)
2272 {
2273 this.collections.Remove(CollectionName);
2274
2275 if (CollectionName == this.lastCollectionName)
2276 {
2277 this.lastCollection = null;
2278 this.lastCollectionName = string.Empty;
2279 }
2280 }
2281
2282 return this.database.DropCollectionAsync(CollectionName);
2283 }
2284
2290 public Task<GenericObject> Generalize(object Object)
2291 {
2292 if (Object is null)
2293 return Task.FromResult<GenericObject>(null);
2294
2295 ObjectSerializer Serializer = this.GetObjectSerializerEx(Object);
2296 string CollectionName = Serializer.CollectionName(Object);
2297
2298 BsonDocument Doc = Object.ToBsonDocument(Object.GetType(), Serializer);
2299
2300 ObjectSerializer Deserializer = this.GetObjectSerializerEx(typeof(GenericObject));
2301
2302 BsonDocumentReader Reader = new BsonDocumentReader(Doc);
2303 BsonDeserializationContext Context = BsonDeserializationContext.CreateRoot(Reader);
2304 BsonDeserializationArgs Args = new BsonDeserializationArgs()
2305 {
2306 NominalType = typeof(GenericObject)
2307 };
2308
2309 if (Deserializer.Deserialize(Context, Args) is GenericObject Obj)
2310 {
2311 Obj.ArchivingTime = Serializer.GetArchivingTimeDays(Object);
2312 return Task.FromResult(Obj);
2313 }
2314 else
2315 throw new InvalidOperationException("Unable to generalize object.");
2316 }
2317
2323 public Task<object> Specialize(GenericObject Object)
2324 {
2325 if (Object is null)
2326 return Task.FromResult<object>(null);
2327
2328 Type T = Types.GetType(Object.TypeName);
2329 if (T is null)
2330 return Task.FromResult<object>(Object);
2331
2332 ObjectSerializer Serializer = this.GetObjectSerializerEx(typeof(GenericObject));
2333 string CollectionName = Serializer.CollectionName(Object);
2334
2335 BsonDocument Doc = Object.ToBsonDocument(Object.GetType(), Serializer);
2336
2337 Serializer = this.GetObjectSerializerEx(T);
2338
2339 BsonDocumentReader Reader = new BsonDocumentReader(Doc);
2340 BsonDeserializationContext Context = BsonDeserializationContext.CreateRoot(Reader);
2341 BsonDeserializationArgs Args = new BsonDeserializationArgs()
2342 {
2343 NominalType = typeof(GenericObject)
2344 };
2345
2346 return Task.FromResult<object>(Serializer.Deserialize(Context, Args));
2347 }
2348
2353 public string[] GetExcludedCollections()
2354 {
2355 SortedDictionary<string, bool> Sorted = new SortedDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
2356
2357 lock (this.collections)
2358 {
2359 foreach (IObjectSerializer Serializer in this.serializers.Values)
2360 {
2362 Sorted[ObjectSerializer.CollectionNameConstant] = true;
2363 }
2364 }
2365
2366 string[] Result = new string[Sorted.Count];
2367 Sorted.Keys.CopyTo(Result, 0);
2368
2369 return Result;
2370 }
2371
2372 // TODO:
2373 // * Created field
2374 // * Updated field
2375 // * RegEx fields
2376 // * JavaScript fields
2377 // * Binary fields (BLOBS)
2378 // * Image fields
2379 // * Collection indices.
2380 // * Dictionary<string,T> fields.
2381 // * SortedDictionary<string,T> fields.
2382 // * Aggregates
2383 // * Case insensitive strings.
2384 }
2385}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:818
Represents a case-insensitive string.
This filter selects objects that conform to all child-filters provided.
Definition: FilterAnd.cs:10
Abstract base class for filters having a single child-filters.
Definition: FilterChild.cs:7
Abstract base class for filters having a variable number of child-filters.
This filter selects objects that have a named field equal to a given value.
This filter selects objects that have a named field greater or equal to a given value.
This filter selects objects that have a named field greater than a given value.
This filter selects objects that have a named field lesser or equal to a given value.
This filter selects objects that have a named field lesser than a given value.
This filter selects objects that have a named field matching a given regular expression.
This filter selects objects that have a named field not equal to a given value.
Abstract base class for all field filters operating on a constant value.
Base class for all filter classes.
Definition: Filter.cs:15
Filter()
Base class for all filter classes.
Definition: Filter.cs:22
This filter selects objects that does not conform to the child-filter provided.
Definition: FilterNot.cs:7
This filter selects objects that conform to any of the child-filters provided.
Definition: FilterOr.cs:10
Task Delete(params object[] Objects)
Deletes a collection of objects in the database.
Task Clear(string CollectionName)
Clears a collection of all objects.
Task UpdateLazy(object Object, ObjectCallback Callback)
Updates an object in the database, if unlocked. If locked, object will be updated at next opportunity...
Task< string[]> Analyze(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData, ProfilerThread Thread)
Analyzes the database and exports findings to XML.
MongoDBProvider(string DatabaseName, string DefaultCollectionName)
MongoDB database provider, for a local MongoDB database.
Task< string[]> Repair(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData, ProfilerThread Thread)
Analyzes the database and repairs it if necessary. Results are exported to XML.
async Task< object > TryLoadObject(string CollectionName, object ObjectId)
Tries to load an object given its Object ID ObjectId and its collection name CollectionName .
Task< IPage< T > > FindNext< T >(IPage< T > Page)
Finds the next page of objects of a given class T .
Task< T > TryLoadObject< T >(object ObjectId)
Tries to load an object given its Object ID ObjectId and its base type T .
async Task< bool > IsLabel(string CollectionName, string Label)
Checks if a string is a label in a given collection.
Task< string[]> Repair(params string[] CollectionNames)
Repairs a set of collections.
async Task DeleteLazy< T >(int Offset, int MaxCount, string[] SortOrder, ObjectsCallback Callback)
Finds objects of a given class T and deletes them in the same atomic operation.
Task DropCollection(string CollectionName)
Drops a collection, if it exist.
Task< string[]> Analyze(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData)
Analyzes the database and exports findings to XML.
int ObjectIdByteCount
Number of bytes used by an Object ID.
Task StartBulk()
Starts bulk-proccessing of data. Must be followed by a call to EndBulk.
Task< bool > Export(IDatabaseExport Output, string[] CollectionNames)
Performs an export of the database.
Task< string > GetCollection(Object Object)
Gets the collection corresponding to a given object.
Task Iterate< T >(IDatabaseIteration< T > Recipient, string[] CollectionNames)
Performs an iteration of contents of the entire database.
string DefaultCollectionName
Default collection name.
Task RemoveIndex(string CollectionName, string[] FieldNames)
Removes an index from a collection, if one exist.
async Task< string[]> Analyze(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData, bool Repair, ProfilerThread Thread)
Analyzes the database and exports findings to XML.
Task DeleteLazy(object Object, ObjectCallback Callback)
Deletes an object in the database, if unlocked. If locked, object will be deleted at next opportunity...
Task Update(params object[] Objects)
Updates a collection of objects in the database.
async Task Update(object Object)
Updates an object in the database.
IMongoCollection< BsonDocument > DefaultCollection
Default collection.
Task DeleteLazy(IEnumerable< object > Objects, ObjectsCallback Callback)
Deletes a collection of objects in the database, if unlocked. If locked, objects will be deleted at n...
async Task< IPage< object > > FindFirst(string Collection, int PageSize, params string[] SortOrder)
Finds the first page of objects in a given collection.
async Task Insert(IEnumerable< object > Objects)
Inserts a collection of objects into the database.
Task Insert(params object[] Objects)
Inserts a collection of objects into the database.
async Task< string[]> GetDictionaries()
Gets an array of available dictionary collections.
Task< IPersistentDictionary > GetDictionary(string Collection)
Gets a persistent dictionary containing objects in a collection.
string Id
An ID of the files provider. It's unique, and constant during the life-time of the MongoDBProvider cl...
async Task< IEnumerable< object > > FindDelete(string Collection, int Offset, int MaxCount, Filter Filter, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
Task AddIndex(string CollectionName, string[] FieldNames)
Adds an index to a collection, if one does not already exist.
Task UpdateLazy(IEnumerable< object > Objects, ObjectsCallback Callback)
Updates a collection of objects in the database, if unlocked. If locked, objects will be updated at n...
async Task Delete(object Object)
Deletes an object in the database.
MongoDBProvider(string HostName, int Port, string DatabaseName, string DefaultCollectionName)
MongoDB database provider.
Task< string > GetCollection(Type Type)
Gets the collection corresponding to a given type.
async Task< IEnumerable< T > > FindDelete< T >(int Offset, int MaxCount, params string[] SortOrder)
Finds objects of a given class T and deletes them in the same atomic operation.
Task DeleteLazy(object[] Objects, ObjectsCallback Callback)
Deletes a collection of objects in the database, if unlocked. If locked, objects will be deleted at n...
MongoDBProvider(MongoClientSettings Settings, string DatabaseName, string DefaultCollectionName)
MongoDB database provider.
async Task DeleteLazy(string Collection, int Offset, int MaxCount, Filter Filter, string[] SortOrder, ObjectsCallback Callback)
Finds objects in a given collection and deletes them in the same atomic operation.
IObjectSerializer GetObjectSerializer(Type Type)
Returns a serializer for a given type.
Task< string[]> GetLabels(string Collection)
Gets an array of available labels for a collection.
Task Stop()
Called when processing ends.
async Task< IPage< T > > FindFirst< T >(int PageSize, params string[] SortOrder)
Finds the first page of objects of a given class T .
async Task< object > TryGetObjectId(object Object)
Tries to get the Object ID of an object, if it exists.
Task< IPage< object > > FindNext(IPage< object > Page)
Finds the next page of objects in a given collection.
ObjectSerializer GetObjectSerializerEx(object Object)
Gets the object serializer corresponding to a specific object.
Task< IEnumerable< T > > Find< T >(int Offset, int MaxCount, params string[] SortOrder)
Finds objects of a given class T .
Task< object > Specialize(GenericObject Object)
Creates a specialized representation of a generic object.
Task Start()
Called when processing starts.
async Task< IPage< object > > FindFirst(string Collection, int PageSize, Filter Filter, params string[] SortOrder)
Finds the first page of objects in a given collection.
MongoDBProvider(string HostName, string DatabaseName, string DefaultCollectionName)
MongoDB database provider.
string[] GetExcludedCollections()
Gets an array of collections that should be excluded from backups.
async Task< IEnumerable< object > > FindDelete(string Collection, int Offset, int MaxCount, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
Task UpdateLazy(object[] Objects, ObjectsCallback Callback)
Updates a collection of objects in the database, if unlocked. If locked, objects will be updated at n...
Task< string[]> Analyze(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData, bool Repair)
Analyzes the database and exports findings to XML.
Task InsertLazy(IEnumerable< object > Objects, ObjectsCallback Callback)
Inserts an object into the database, if unlocked. If locked, object will be inserted at next opportun...
MongoClient Client
Underlying MongoDB client.
Task Flush()
Persists any pending changes.
Task< IEnumerable< object > > Find(string CollectionName, int Offset, int MaxCount, Filter Filter, params string[] SortOrder)
Finds objects in a given collection.
async Task Insert(object Object)
Inserts an object into the database.
Task EndBulk()
Ends bulk-processing of data. Must be called once for every call to StartBulk.
Task InsertLazy(object Object, ObjectCallback Callback)
Inserts an object into the database, if unlocked. If locked, object will be inserted at next opportun...
Task< string[]> Repair(ProfilerThread Thread, params string[] CollectionNames)
Repairs a set of collections.
Task< string[]> Repair(XmlWriter Output, string XsltPath, string ProgramDataFolder, bool ExportData)
Analyzes the database and repairs it if necessary. Results are exported to XML.
Task< GenericObject > Generalize(object Object)
Creates a generalized representation of an object.
async Task Update(IEnumerable< object > Objects)
Updates a collection of objects in the database.
async Task Delete(IEnumerable< object > Objects)
Deletes a collection of objects in the database.
Task< IEnumerable< object > > Find(string CollectionName, int Offset, int MaxCount, params string[] SortOrder)
Finds objects in a given collection.
async Task< string[]> GetCollections()
Gets an array of available collections.
async Task< bool > Export(IDatabaseExport Output, string[] CollectionNames, ProfilerThread Thread)
Performs an export of the database.
Task InsertLazy(object[] Objects, ObjectsCallback Callback)
Inserts an object into the database, if unlocked. If locked, object will be inserted at next opportun...
IMongoCollection< BsonDocument > GetCollection(string CollectionName)
Gets a collection.
ObjectSerializer GetObjectSerializerEx(Type Type)
Gets the object serializer corresponding to a specific object.
async Task DeleteLazy(string Collection, int Offset, int MaxCount, string[] SortOrder, ObjectsCallback Callback)
Finds objects in a given collection and deletes them in the same atomic operation.
Contains a page of items.
Definition: Page.cs:17
static Guid ObjectIdToGuid(ObjectId ObjectId)
Converts a MongoDB Object ID to a GUID
static ObjectId GuidToObjectId(Guid Guid)
Converts a GUID to a MongoDB Object ID
Serializes a type to BSON, taking into account attributes defined in Waher.Persistence....
virtual string CollectionName(object Object)
Name of collection objects of this type is to be stored in, if available. If not available,...
virtual bool IsDefaultValue(string FieldName, object Value)
Checks if a given field value corresponds to the default value for the corresponding field.
virtual bool TryGetFieldType(string FieldName, object Object, out Type FieldType)
Gets the type of a field or property of an object, given its name.
virtual async Task< ObjectId > GetObjectId(object Value, bool InsertIfNotFound)
Gets the Object ID for a given object.
string ToShortName(string FieldName)
Converts a field name to its corresponding short name. If no explicit short name is available,...
virtual string ObjectIdMemberName
Mamber name of the field or property holding the Object ID, if any. If there are no such member,...
virtual bool HasObjectId(object Value)
If the class has an Object ID.
virtual bool HasObjectIdField
If the class has an Object ID field.
object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
Deserializes a value.
bool BackupCollection
If the corresponding collection should be backed up or not.
static readonly DateTime UnixEpoch
UNIX Epoch, started at 1970-01-01, 00:00:00 (GMT)
This class manages a string dictionary in a persisted storage.
Generic object. Contains a sequence of properties.
Implements an in-memory cache.
Definition: Cache.cs:15
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Definition: Cache.cs:203
void Add(KeyType Key, ValueType Value)
Adds an item to the cache.
Definition: Cache.cs:338
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static Type GetType(string FullName)
Gets a type, given its full name.
Definition: Types.cs:41
static object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:548
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:84
static ConstructorInfo GetDefaultConstructor(Type Type)
Gets the default constructor of a type, if one exists.
Definition: Types.cs:1630
Class that keeps track of events and timing for one thread.
void Exception(System.Exception Exception)
Exception occurred
void NewState(string State)
Thread changes state.
Interface for database providers that can be plugged into the static Database class.
Interface for paginated results.
Definition: IPage.cs:11
Persistent dictionary that can contain more entries than possible in the internal memory.
Type ValueType
What type of object is being serialized.
Interface for database exports that filter objects.
Task< bool > EndCollection()
Is called when a collection is finished.
Task< bool > ReportException(Exception Exception)
Is called when an exception has occurred.
Task< bool > ReportProperty(string PropertyName, object PropertyValue)
Is called when a property is reported.
Task< bool > EndObject()
Is called when an object is finished.
Task< bool > EndIndex()
Is called when an index in a collection is finished.
Task< bool > ReportError(string Message)
Is called when an error is reported.
Task< bool > ReportIndexField(string FieldName, bool Ascending)
Is called when a field in an index is reported.
Task< bool > StartDatabase()
Is called when export of database is started.
Task< bool > StartIndex()
Is called when an index in a collection is started.
Task< bool > EndDatabase()
Is called when export of database is finished.
Task< string > StartObject(string ObjectId, string TypeName)
Is called when an object is started.
Task< bool > StartCollection(string CollectionName)
Is called when a collection is started.
Interface for iterations of database contents.
delegate void ObjectCallback(object Object)
Method called when a process has completed for an object.
delegate void ObjectsCallback(IEnumerable< object > Objects)
Method called when a process has completed for a set of objects.
Definition: App.xaml.cs:4