-
Notifications
You must be signed in to change notification settings - Fork 5
/
MongoMapper.cs
452 lines (326 loc) · 13.9 KB
/
MongoMapper.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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Security.Policy;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Serialization;
using EtoolTech.MongoDB.Mapper.Configuration;
using EtoolTech.MongoDB.Mapper.Exceptions;
using EtoolTech.MongoDB.Mapper.Interfaces;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
namespace EtoolTech.MongoDB.Mapper
{
[Serializable]
public abstract class MongoMapper<T> : IMongoMapperOriginable<T>,
IMongoMapperRelationable<T>,
IMongoMapperWriteable<T>,
IMongoMapperIdeable,
IMongoMapperVersionable,
ISupportInitialize
{
#region Constants and Fields
private static readonly IChildsManager ChildsManager = Mapper.ChildsManager.Instance;
private static readonly IEvents<T> Events = Mapper.Events<T>.Instance;
private static readonly IRelations<T> Relations = Mapper.Relations<T>.Instance;
private readonly Type _classType;
private readonly Dictionary<string, object> _relationBuffer = new Dictionary<string, object>();
private byte[] _originalObject;
private object _tOriginalObjet;
#endregion
#region Constructors and Destructors
protected MongoMapper()
{
_classType = GetType();
BsonDefaults.MaxDocumentSize = ConfigManager.MaxDocumentSize(_classType.Name) * 1024 * 1024;
MongoMapperHelper.RebuildClass(_classType, false);
}
#endregion
#region Delegates
public delegate void OnAfterCompleteEventHandler(object sender, EventArgs e);
public delegate void OnAfterDeleteEventHandler(object sender, EventArgs e);
public delegate void OnAfterInsertEventHandler(object sender, EventArgs e);
public delegate void OnAfterModifyEventHandler(object sender, EventArgs e);
public delegate void OnBeforeDeleteEventHandler(object sender, EventArgs e);
public delegate void OnBeforeInsertEventHandler(object sender, EventArgs e);
public delegate void OnBeforeModifyEventHandler(object sender, EventArgs e);
public delegate void OnObjectInitEventHandler(object sender, EventArgs e);
public delegate void OnObjectCompleteEventHandler(object sender, EventArgs e);
#endregion
#region Public Events
public event OnAfterCompleteEventHandler OnAfterComplete;
public event OnAfterDeleteEventHandler OnAfterDelete;
public event OnAfterInsertEventHandler OnAfterInsert;
public event OnAfterModifyEventHandler OnAfterModify;
public event OnBeforeDeleteEventHandler OnBeforeDelete;
public event OnBeforeInsertEventHandler OnBeforeInsert;
public event OnBeforeModifyEventHandler OnBeforeModify;
public event OnObjectInitEventHandler OnObjectInit;
public event OnObjectCompleteEventHandler OnObjectComplete;
#endregion
#region Public Properties
#region Builders
public FilterDefinitionBuilder<T> Filter { get { return Builders<T>.Filter; } }
public SortDefinitionBuilder<T> Sort { get { return Builders<T>.Sort; } }
public UpdateDefinitionBuilder<T> Update { get { return Builders<T>.Update; } }
public IndexKeysDefinitionBuilder<T> Index { get { return Builders<T>.IndexKeys; } }
public ProjectionDefinitionBuilder<T> Project { get { return Builders<T>.Projection; } }
#endregion
#region IMongoMapperIdeable Members
[BsonId(IdGenerator = typeof (MongoMapperIdGenerator))]
[XmlIgnore]
public long m_id { get; set; }
#endregion
#region IMongoMapperVersionable Members
[XmlIgnore]
public long m_dv { get; set; }
public bool IsLastVersion()
{
return IsLastVersion(false);
}
public bool IsLastVersion(bool Force)
{
if (!Force && String.IsNullOrEmpty(ConfigManager.GetClientSettings(_classType.Name).ReplicaSetName))
return true;
if (m_id == default(long))
{
m_id = Finder.Instance.FindIdByKey<T>(_classType, GetPrimaryKeyValues());
}
var query = Builders<BsonDocument>.Filter.Eq("_id", m_id);
var result = CollectionsManager.GetCollection<BsonDocument>(_classType.Name).Find(Builders<BsonDocument>.Filter.And(query)).Project(Builders<BsonDocument>.Projection.Include("m_dv")).Limit(1).ToListAsync().Result;
return result.First()["m_dv"].AsInt64 == m_dv;
}
public void FillFromLastVersion()
{
FillFromLastVersion(false);
}
public void FillFromLastVersion(bool Force)
{
if (!Force && String.IsNullOrEmpty(ConfigManager.GetClientSettings(_classType.Name).ReplicaSetName)) return;
if (m_id == default(long))
{
m_id = Finder.Instance.FindIdByKey<T>(_classType, GetPrimaryKeyValues());
}
var query = Builders<BsonDocument>.Filter.Eq("_id", m_id);
var result = CollectionsManager.GetCollection<BsonDocument>(_classType.Name).Find(Builders<BsonDocument>.Filter.And(query)).Limit(1).ToListAsync().Result;
ReflectionUtility.CopyObject(BsonSerializer.Deserialize<T>(result.First()), this);
}
#endregion
#endregion
#region Properties
[BsonIgnore]
private byte[] OriginalObject
{
get { return _originalObject; }
set
{
_originalObject = value;
_tOriginalObjet = null;
}
}
[BsonExtraElements]
public IDictionary<string, object> ExtraElements { get; set; }
#endregion
#region Public Methods
#region IMongoMapperOriginable Members
public T GetOriginalObject()
{
if (!ConfigManager.EnableOriginalObject(_classType.Name))
{
throw new NotImplementedException("OriginalObject: This functionality is disabled, enable EnableOriginalObject in the App.config");
}
if (m_id == default(long) || OriginalObject == null || OriginalObject.Length == 0)
{
return (Activator.CreateInstance<T>());
}
return GetOriginalT();
}
public T GetOriginalT()
{
if (_tOriginalObjet != null)
{
return (T) _tOriginalObjet;
}
_tOriginalObjet = ObjectSerializer.ToObjectSerialize<T>(OriginalObject);
return (T) _tOriginalObjet;
}
public void SaveOriginal(bool Force = false)
{
if (OriginalIsEmpty(Force) && m_id != default(long) &&
ConfigManager.EnableOriginalObject(_classType.Name))
{
OriginalObject = ObjectSerializer.ToByteArray(this);
}
}
#endregion
#region IMongoMapperRelationable Members
public void EnsureDownRelations()
{
Relations.EnsureDownRelations(this, _classType, Finder.Instance);
}
public void EnsureUpRelations()
{
Relations.EnsureUpRelations(this, _classType, Finder.Instance);
}
#endregion
#region IMongoMapperWriteable Members
public void Delete()
{
Events.BeforeDeleteDocument(this, OnBeforeDelete, _classType);
EnsureDownRelations();
DeleteDocument();
Events.AfterDeleteDocument(this, OnAfterDelete, OnAfterComplete, _classType);
}
public Task DeleteAsync()
{
Events.BeforeDeleteDocument(this, OnBeforeDelete, _classType);
EnsureDownRelations();
var task = Writer.Instance.DeleteAsync(_classType.Name, _classType, this);
Events.AfterDeleteDocument(this, OnAfterDelete, OnAfterComplete, _classType);
return task;
}
public void DeleteDocument()
{
Writer.Instance.Delete(_classType.Name, _classType, this);
}
public void InsertDocument()
{
Events.BeforeInsertDocument(this, OnBeforeInsert, _classType);
EnsureUpRelations();
Writer.Instance.Insert<T>(_classType.Name, _classType, (T)(object)this);
Events.AfterInsertDocument(this, OnAfterInsert, OnAfterComplete, _classType);
}
public int Save()
{
int result = -1;
PropertyValidator.Validate(this, _classType);
ChildsManager.ManageChilds(this);
if (m_id == default(long))
{
long id = Finder.Instance.FindIdByKey<T>(_classType, GetPrimaryKeyValues());
if (id == default(long))
{
InsertDocument();
result = 0;
}
else
{
//Si llega aqui ya existe un registro con esa key y no es el que tenemos cargado
if (ConfigManager.ExceptionOnDuplicateKey(_classType.Name))
{
throw new DuplicateKeyException();
}
UpdateDocument(id);
result = 1;
}
}
else
{
UpdateDocument(m_id);
result = 1;
}
//Si salvan y no se pide el objeto otra vez la string queda vacia, la llenamos aqui
//TODO: No estoy muy convencido de esto revisar ...
SaveOriginal();
return result;
}
public Task<int> SaveAsync()
{
return Task.Factory.StartNew(() => this.Save());
}
public void ServerUpdate(UpdateDefinition<T> Update, bool Refill = true)
{
if (m_id == default(long))
{
m_id = Finder.Instance.FindIdByKey<T>(_classType, GetPrimaryKeyValues());
}
var query = Builders<T>.Filter.Eq("_id", m_id);
var result = CollectionsManager.GetCollection<T>(_classType.Name).FindOneAndUpdateAsync(
query,
Update,
new FindOneAndUpdateOptions<T>()
{
IsUpsert = true,
ReturnDocument = Refill ? ReturnDocument.After : ReturnDocument.Before
}
).Result;
if (Refill)
{
ReflectionUtility.CopyObject<T>(result, this);
}
}
public Task ServerUpdateAsync(UpdateDefinition<T> Update, bool Refill = true)
{
return Task.Factory.StartNew(() => this.ServerUpdateAsync(Update, Refill));
}
public void ServerSetValue(string FieldName, object Value, bool Refill = true)
{
this.ServerUpdate(this.Update.Set(FieldName, Value), Refill);
}
public Task ServerSetValueAsync(string FieldName, object Value, bool Refill = true)
{
return this.ServerUpdateAsync(this.Update.Set(FieldName, Value), Refill);
}
public void UpdateDocument(long Id)
{
Events.BeforeUpdateDocument(this, OnBeforeModify, _classType);
EnsureUpRelations();
m_id = Id;
Writer.Instance.Update(_classType.Name, _classType, this);
Events.AfterUpdateDocument(this, OnAfterModify, OnAfterComplete, _classType);
}
#endregion
public static T FindByKey(params object[] values)
{
return Finder.Instance.FindByKey<T>(values);
}
public static IMongoCollection<T> GetCollection()
{
return CollectionsManager.GetCollection<T>(typeof (T).Name);
}
public static IEnumerable<BsonDocument> Aggregate(params BsonDocument[] Operations)
{
var agg = CollectionsManager.GetCollection<BsonDocument>((typeof(T).Name)).Aggregate();
agg = Operations.Aggregate(agg, (Current, Pipe) => Current.AppendStage<BsonDocument>(Pipe));
return agg.ToListAsync().Result;
}
public static IEnumerable<BsonDocument> Aggregate(params string[] JsonStringOperations)
{
var operations = JsonStringOperations.Select(ObjectSerializer.JsonStringToBsonDocument).ToList();
return Aggregate(operations.ToArray());
}
public static void ServerDelete(FilterDefinition<T> Query)
{
var result = CollectionsManager.GetCollection<T>(typeof (T).Name).FindOneAndDeleteAsync(Query).GetAwaiter().GetResult();
}
private bool OriginalIsEmpty(bool force = false)
{
if (force) return true;
return _originalObject == null || _originalObject.Length == 0;
}
#endregion
#region Methods
private Dictionary<string, object> GetPrimaryKeyValues()
{
return MongoMapperHelper.GetPrimaryKey(_classType).ToDictionary(
KeyField => KeyField, KeyField => ReflectionUtility.GetPropertyValue(this, KeyField));
}
#endregion
#region ISupportInitialize Members
public void BeginInit()
{
Events.ObjectInit(this, OnObjectInit, _classType);
}
public void EndInit()
{
var mongoMapperOriginable = this as IMongoMapperOriginable<T>;
(mongoMapperOriginable).SaveOriginal(false);
Events.ObjectComplete(this, OnObjectComplete, _classType);
}
#endregion
}
}