diff --git a/Source/Bifrost.MongoDb/Bifrost.MongoDB.csproj b/Source/Bifrost.MongoDb/Bifrost.MongoDB.csproj index 6e19a63fb..c6783daa4 100644 --- a/Source/Bifrost.MongoDb/Bifrost.MongoDB.csproj +++ b/Source/Bifrost.MongoDb/Bifrost.MongoDB.csproj @@ -5,7 +5,7 @@ AnyCPU 8.0.30703 2.0 - {63BBC1F3-E5E0-4902-BADF-1589F91C0BEA} + {6AA43B6E-8B56-4B00-AB6E-0F711C432BE8} Library Properties Bifrost.MongoDB @@ -79,6 +79,7 @@ + diff --git a/Source/Bifrost.MongoDb/Concepts/ConceptSerializationProvider.cs b/Source/Bifrost.MongoDb/Concepts/ConceptSerializationProvider.cs index 45e6a3236..ba6b6c94c 100644 --- a/Source/Bifrost.MongoDb/Concepts/ConceptSerializationProvider.cs +++ b/Source/Bifrost.MongoDb/Concepts/ConceptSerializationProvider.cs @@ -6,14 +6,14 @@ using Bifrost.Concepts; using MongoDB.Bson.Serialization; -namespace Bifrost.MongoDB.Concepts +namespace Bifrost.MongoDb.Concepts { public class ConceptSerializationProvider : IBsonSerializationProvider { public IBsonSerializer GetSerializer(Type type) { if (type.IsConcept()) - return new ConceptSerializer(); + return new ConceptSerializer(type); return null; } diff --git a/Source/Bifrost.MongoDb/Concepts/ConceptSerializer.cs b/Source/Bifrost.MongoDb/Concepts/ConceptSerializer.cs index dd9d8dde0..6a4f02407 100644 --- a/Source/Bifrost.MongoDb/Concepts/ConceptSerializer.cs +++ b/Source/Bifrost.MongoDb/Concepts/ConceptSerializer.cs @@ -10,41 +10,59 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Options; -namespace Bifrost.MongoDB.Concepts +namespace Bifrost.MongoDb.Concepts { public class ConceptSerializer : IBsonSerializer { - public object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options) + private Type _valueType; + public Type ValueType { + get + { + return _valueType; + } + } + + public ConceptSerializer(Type conceptType) + { + if (!conceptType.IsConcept()) + throw new ArgumentException("Type is not a concept.", nameof(conceptType)); + + _valueType = conceptType; + } + + public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + var bsonReader = context.Reader; + var actualType = args.NominalType; + object value = null; + var valueType = actualType.GetConceptValueType(); - if (valueType == typeof(Guid)) { + if (valueType == typeof(Guid)) + { var binaryData = bsonReader.ReadBinaryData(); value = binaryData.ToGuid(); - } else if (valueType == typeof(double)) - value = bsonReader.ReadDouble (); + } + else if (valueType == typeof(double)) + value = bsonReader.ReadDouble(); else if (valueType == typeof(float)) - value = (float)bsonReader.ReadDouble (); + value = (float)bsonReader.ReadDouble(); else if (valueType == typeof(Int32)) - value = bsonReader.ReadInt32 (); + value = bsonReader.ReadInt32(); else if (valueType == typeof(Int64)) - value = bsonReader.ReadInt64 (); + value = bsonReader.ReadInt64(); else if (valueType == typeof(bool)) - value = bsonReader.ReadBoolean (); + value = bsonReader.ReadBoolean(); else if (valueType == typeof(string)) - value = bsonReader.ReadString (); + value = bsonReader.ReadString(); else if (valueType == typeof(decimal)) - value = decimal.Parse (bsonReader.ReadString ()); - + value = decimal.Parse(bsonReader.ReadString()); + var concept = ConceptFactory.CreateConceptInstance(actualType, value); return concept; } - public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options) - { - return null; - } - public bool GetDocumentId(object document, out object id, out Type idNominalType, out IIdGenerator idGenerator) { id = null; @@ -52,40 +70,35 @@ public bool GetDocumentId(object document, out object id, out Type idNominalType idNominalType = null; return false; } - - public void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options) + + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) { - var underlyingValue = value.GetType().GetProperty("Value").GetValue(value, null); + var underlyingValue = value?.GetType().GetProperty("Value").GetValue(value, null); + var nominalType = args.NominalType; var underlyingValueType = nominalType.GetConceptValueType(); - if (underlyingValueType == typeof(Guid)) { - var guid = (Guid)underlyingValue; - var guidAsBytes = guid.ToByteArray (); - bsonWriter.WriteBinaryData (guidAsBytes, BsonBinarySubType.UuidLegacy, GuidRepresentation.CSharpLegacy); - } else if (underlyingValueType == typeof(double)) - bsonWriter.WriteDouble ((double)underlyingValue); + + var bsonWriter = context.Writer; + + if (underlyingValueType == typeof(Guid)) + { + var guid = (Guid) (underlyingValue ?? default(Guid)); + var guidAsBytes = guid.ToByteArray(); + bsonWriter.WriteBinaryData(new BsonBinaryData(guidAsBytes, BsonBinarySubType.UuidLegacy, GuidRepresentation.CSharpLegacy)); + } + else if (underlyingValueType == typeof(double)) + bsonWriter.WriteDouble((double) (underlyingValue ?? default(double))); else if (underlyingValueType == typeof(float)) - bsonWriter.WriteDouble ((double)underlyingValue); + bsonWriter.WriteDouble((double) (underlyingValue ?? default(double))); else if (underlyingValueType == typeof(Int32)) - bsonWriter.WriteInt32 ((Int32)underlyingValue); + bsonWriter.WriteInt32((Int32) (underlyingValue ?? default(Int32))); else if (underlyingValueType == typeof(Int64)) - bsonWriter.WriteInt64 ((Int64)underlyingValue); + bsonWriter.WriteInt64((Int64) (underlyingValue ?? default(Int64))); else if (underlyingValueType == typeof(bool)) - bsonWriter.WriteBoolean ((bool)underlyingValue); + bsonWriter.WriteBoolean((bool) (underlyingValue ?? default(bool))); else if (underlyingValueType == typeof(string)) - bsonWriter.WriteString ((string)(underlyingValue ?? string.Empty)); + bsonWriter.WriteString((string) (underlyingValue ?? string.Empty)); else if (underlyingValueType == typeof(decimal)) - bsonWriter.WriteString (underlyingValue.ToString()); + bsonWriter.WriteString(underlyingValue?.ToString() ?? default(decimal).ToString()); } - - public void SetDocumentId(object document, object id) - { - } - - public IBsonSerializationOptions GetDefaultSerializationOptions() - { - var options = new DocumentSerializationOptions(); - return options; - } - } } diff --git a/Source/Bifrost.MongoDb/ConfigurationExtensions.cs b/Source/Bifrost.MongoDb/ConfigurationExtensions.cs index 8f988ecac..e7ae31f8d 100644 --- a/Source/Bifrost.MongoDb/ConfigurationExtensions.cs +++ b/Source/Bifrost.MongoDb/ConfigurationExtensions.cs @@ -3,8 +3,8 @@ * Licensed under the MIT License. See LICENSE in the project root for license information. *--------------------------------------------------------------------------------------------*/ using System; -using Bifrost.MongoDB; -using Bifrost.MongoDB.Events; +using Bifrost.MongoDb; +using Bifrost.MongoDb.Events; namespace Bifrost.Configuration { @@ -26,6 +26,12 @@ public static EventStorageConfiguration WithUrl(this EventStorageConfiguration c return configuration; } + public static EventStorageConfiguration WithSSL(this EventStorageConfiguration configuration) + { + configuration.UseSSL = true; + return configuration; + } + public static EventStorageConfiguration WithDefaultDatabase(this EventStorageConfiguration configuration, string defaultDatabase) { @@ -52,6 +58,12 @@ public static EntityContextConfiguration WithUrl(this EntityContextConfiguration return configuration; } + public static EntityContextConfiguration WithSSL(this EntityContextConfiguration configuration) + { + configuration.UseSSL = true; + return configuration; + } + public static EntityContextConfiguration WithDefaultDatabase(this EntityContextConfiguration configuration, string defaultDatabase) { diff --git a/Source/Bifrost.MongoDb/EntityContext.cs b/Source/Bifrost.MongoDb/EntityContext.cs index e268e2540..fbaae71ca 100644 --- a/Source/Bifrost.MongoDb/EntityContext.cs +++ b/Source/Bifrost.MongoDb/EntityContext.cs @@ -7,85 +7,91 @@ using Bifrost.Concepts; using MongoDB.Driver; using MongoDB.Bson; -using MongoDB.Driver.Builders; +using System.Reflection; -namespace Bifrost.MongoDB +namespace Bifrost.MongoDb { - public class EntityContext : IEntityContext - { - EntityContextConnection _connection; - string _collectionName; - MongoCollection _collection; - - public EntityContext(EntityContextConnection connection) - { - _connection = connection; - _collectionName = typeof(T).Name; - if( !_connection.Database.CollectionExists(_collectionName) ) - _connection.Database.CreateCollection(_collectionName); - - _collection = _connection.Database.GetCollection(_collectionName); - } - - - public IQueryable Entities - { - get { return _collection.FindAll().AsQueryable(); } - } - - public void Attach(T entity) - { - } - - public void Insert(T entity) - { - _collection.Insert(entity); - } - - public void Update(T entity) - { - Save(entity); - } - - public void Delete(T entity) - { - } - - public void Save(T entity) - { - _collection.Save(entity); - } - - public void Commit() - { - } - - public void Dispose() - { - } - - - public T GetById(TProperty id) - { - var objectId = GetObjectId(id); - return _collection.FindOneById(objectId); - } - - BsonValue GetObjectId(TProperty id) - { - object idValue = id; - - if (id.IsConcept()) idValue = id.GetConceptValue(); - - var idAsValue = BsonValue.Create(idValue); - return idAsValue; - } - - - public void DeleteById(TProperty id) - { - var objectId = GetObjectId(id); - _collection.Remove(Query.EQ("_id", objectId)); - } - } + public class EntityContext : IEntityContext + { + EntityContextConnection _connection; + string _collectionName; + IMongoCollection _collection; + + public EntityContext(EntityContextConnection connection) + { + _connection = connection; + _collectionName = typeof(T).Name; + + _collection = _connection.Database.GetCollection(_collectionName); + } + + + public IQueryable Entities + { + get { return _collection.AsQueryable(); } + } + + public void Attach(T entity) + { + } + + public void Insert(T entity) + { + _collection.InsertOne(entity); + } + + public void Update(T entity) + { + Save(entity); + } + + public void Delete(T entity) + { + } + + public void Save(T entity) + { + var idProperty = GetIdProperty(entity); + + var filter = Builders.Filter.Eq("_id", idProperty.GetValue(entity)); + _collection.ReplaceOne(filter, entity, new UpdateOptions() { IsUpsert = true }); + } + + public void Commit() + { + } + + public void Dispose() + { + } + + PropertyInfo GetIdProperty(T entity) + { + return typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.Name.ToLowerInvariant() == "id").First(); + } + + + public T GetById(TProperty id) + { + var objectId = GetObjectId(id); + return _collection.Find(Builders.Filter.Eq("_id", objectId)).FirstOrDefault(); + } + + BsonValue GetObjectId(TProperty id) + { + object idValue = id; + + if (id.IsConcept()) idValue = id.GetConceptValue(); + + var idAsValue = BsonValue.Create(idValue); + return idAsValue; + } + + + public void DeleteById(TProperty id) + { + var objectId = GetObjectId(id); + _collection.DeleteOne(Builders.Filter.Eq("id", objectId)); + } + } } diff --git a/Source/Bifrost.MongoDb/EntityContextConfiguration.cs b/Source/Bifrost.MongoDb/EntityContextConfiguration.cs index 7ac20534b..ae0fea3ef 100644 --- a/Source/Bifrost.MongoDb/EntityContextConfiguration.cs +++ b/Source/Bifrost.MongoDb/EntityContextConfiguration.cs @@ -6,11 +6,12 @@ using Bifrost.Configuration; using Bifrost.Entities; -namespace Bifrost.MongoDB +namespace Bifrost.MongoDb { public class EntityContextConfiguration : IEntityContextConfiguration { public string Url { get; set; } + public bool UseSSL { get; set; } public string DefaultDatabase { get; set; } public Type EntityContextType { get { return typeof(EntityContext<>); } } diff --git a/Source/Bifrost.MongoDb/EntityContextConnection.cs b/Source/Bifrost.MongoDb/EntityContextConnection.cs index df5c9e5ae..036dfffa2 100644 --- a/Source/Bifrost.MongoDb/EntityContextConnection.cs +++ b/Source/Bifrost.MongoDb/EntityContextConnection.cs @@ -4,27 +4,33 @@ *--------------------------------------------------------------------------------------------*/ using Bifrost.Entities; using Bifrost.Execution; -using Bifrost.MongoDB.Concepts; +using Bifrost.MongoDb.Concepts; using MongoDB.Bson.Serialization; using MongoDB.Driver; -namespace Bifrost.MongoDB +namespace Bifrost.MongoDb { public class EntityContextConnection : IEntityContextConnection { - public string ConnectionString { get; private set; } - public string DatabaseName { get; private set; } - - public MongoServer Server { get; private set; } - public MongoDatabase Database { get; private set; } + public MongoClient Server { get; private set; } + public IMongoDatabase Database { get; private set; } public EntityContextConnection(EntityContextConfiguration configuration) { - ConnectionString = configuration.Url; - DatabaseName = configuration.DefaultDatabase; + var s = MongoClientSettings.FromUrl(new MongoUrl(configuration.Url)); + if (configuration.UseSSL) + { + s.UseSsl = true; + s.SslSettings = new SslSettings + { + EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12, + CheckCertificateRevocation = false + }; + } + + Server = new MongoClient(s); + Database = Server.GetDatabase(configuration.DefaultDatabase); - Server = MongoServer.Create(ConnectionString); - Database = Server.GetDatabase(DatabaseName); BsonSerializer.RegisterSerializationProvider(new ConceptSerializationProvider()); } diff --git a/Source/Bifrost.MongoDb/EventClassMap.cs b/Source/Bifrost.MongoDb/EventClassMap.cs index f072877f7..29d7e0359 100644 --- a/Source/Bifrost.MongoDb/EventClassMap.cs +++ b/Source/Bifrost.MongoDb/EventClassMap.cs @@ -5,7 +5,7 @@ using Bifrost.Events; using MongoDB.Bson.Serialization; -namespace Bifrost.MongoDB +namespace Bifrost.MongoDb { public class EventClassMap : BsonClassMap { diff --git a/Source/Bifrost.MongoDb/Events/BsonSerializationProvider.cs b/Source/Bifrost.MongoDb/Events/BsonSerializationProvider.cs index 798f2ba20..28da96512 100644 --- a/Source/Bifrost.MongoDb/Events/BsonSerializationProvider.cs +++ b/Source/Bifrost.MongoDb/Events/BsonSerializationProvider.cs @@ -6,7 +6,7 @@ using System.Reflection; using MongoDB.Bson.Serialization; -namespace Bifrost.MongoDB.Events +namespace Bifrost.MongoDb.Events { public class BsonSerializationProvider : IBsonSerializationProvider { diff --git a/Source/Bifrost.MongoDb/Events/EventSourceVersionSerializer.cs b/Source/Bifrost.MongoDb/Events/EventSourceVersionSerializer.cs index a3c3045d5..3092612b4 100644 --- a/Source/Bifrost.MongoDb/Events/EventSourceVersionSerializer.cs +++ b/Source/Bifrost.MongoDb/Events/EventSourceVersionSerializer.cs @@ -8,45 +8,30 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Options; -namespace Bifrost.MongoDB.Events +namespace Bifrost.MongoDb.Events { - public class EventSourceVersionSerializer : IBsonSerializer - { - public object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options) - { - var versionAsDouble = bsonReader.ReadDouble(); - var version = EventSourceVersion.FromCombined(versionAsDouble); - return version; - } - - public void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options) - { - var version = (EventSourceVersion)value; - var versionAsDouble = version.Combine(); - bsonWriter.WriteDouble(versionAsDouble); - } - - - public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options) - { - throw new NotImplementedException(); - } - - public bool GetDocumentId(object document, out object id, out Type idNominalType, out IIdGenerator idGenerator) - { - throw new NotImplementedException(); - } - - public void SetDocumentId(object document, object id) - { - throw new NotImplementedException(); - } - - public IBsonSerializationOptions GetDefaultSerializationOptions() - { - var options = new DocumentSerializationOptions(); - return options; - } - - } + public class EventSourceVersionSerializer : IBsonSerializer + { + public Type ValueType + { + get + { + return typeof(EventSourceVersion); + } + } + + public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + var versionAsDouble = context.Reader.ReadDouble(); + var version = EventSourceVersion.FromCombined(versionAsDouble); + return version; + } + + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) + { + var version = (EventSourceVersion)value; + var versionAsDouble = version.Combine(); + context.Writer.WriteDouble(versionAsDouble); + } + } } diff --git a/Source/Bifrost.MongoDb/Events/EventStorageConfiguration.cs b/Source/Bifrost.MongoDb/Events/EventStorageConfiguration.cs index 2b55c1348..6d49ff281 100644 --- a/Source/Bifrost.MongoDb/Events/EventStorageConfiguration.cs +++ b/Source/Bifrost.MongoDb/Events/EventStorageConfiguration.cs @@ -4,21 +4,15 @@ *--------------------------------------------------------------------------------------------*/ using System.Net; -namespace Bifrost.MongoDB.Events +namespace Bifrost.MongoDb.Events { /// /// Represents the configuration for /// public class EventStorageConfiguration { - /// - /// Gets or sets the Url for the mongo server - /// public string Url { get; set; } - - /// - /// Gets or sets the default database to use - /// + public bool UseSSL { get; set; } public string DefaultDatabase { get; set; } } } diff --git a/Source/Bifrost.MongoDb/Events/EventStore.cs b/Source/Bifrost.MongoDb/Events/EventStore.cs index d68d35fc5..d30b9c87f 100644 --- a/Source/Bifrost.MongoDb/Events/EventStore.cs +++ b/Source/Bifrost.MongoDb/Events/EventStore.cs @@ -9,162 +9,167 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver; -using MongoDB.Driver.Builders; -namespace Bifrost.MongoDB.Events +namespace Bifrost.MongoDb.Events { - public class EventStore : IEventStore - { - const string CollectionName = "Events"; - const string IncrementalKeysCollectionName = "_IncrementalKeys"; - const string EventType = "EventType"; - const string Generation = "Generation"; - const string Version = "Version"; - const string LogicalEventType = "LogicalEventType"; - const string CurrentKey = "CurrentKey"; - - EventStorageConfiguration _configuration; - MongoServer _server; - MongoDatabase _database; - MongoCollection _collection; - MongoCollection _incrementalKeysCollection; - IEventMigrationHierarchyManager _eventMigrationHierarchyManager; - - static EventStore() - { - BsonSerializer.RegisterSerializer(typeof(EventSourceVersion), new EventSourceVersionSerializer()); - } - - public EventStore(EventStorageConfiguration configuration, IEventMigrationHierarchyManager eventMigrationHierarchyManager) - { - _configuration = configuration; - _eventMigrationHierarchyManager = eventMigrationHierarchyManager; - Initialize(); - } - - void Initialize() - { - _server = MongoServer.Create(_configuration.Url); - _database = _server.GetDatabase(_configuration.DefaultDatabase); - if (!_database.CollectionExists(CollectionName)) - _database.CreateCollection(CollectionName); - - _collection = _database.GetCollection(CollectionName); - - if (!_database.CollectionExists(IncrementalKeysCollectionName)) - _database.CreateCollection(IncrementalKeysCollectionName); - - _incrementalKeysCollection = _database.GetCollection(IncrementalKeysCollectionName); - } - - public CommittedEventStream GetForEventSource(EventSource eventSource, Guid eventSourceId) - { - var eventSourceType = eventSource.GetType(); - var query = Query.And( - Query.EQ("EventSourceId", eventSourceId), - Query.EQ("EventSource", eventSourceType.AssemblyQualifiedName) - ); - - var cursor = _collection.FindAs(query); - var documents = cursor.ToArray(); - var events = ToEvents(documents); - var stream = new CommittedEventStream(eventSourceId); - stream.Append(events); - return stream; - } - - public CommittedEventStream Commit(UncommittedEventStream uncommittedEventStream) - { - var eventArray = uncommittedEventStream.ToArray(); - for (var eventIndex = 0; eventIndex < eventArray.Length; eventIndex++) - { - var @event = eventArray[eventIndex]; - @event.Id = GetNextEventId(); - var eventDocument = @event.ToBsonDocument(); - AddMetaData(@event, eventDocument); - _collection.Insert(eventDocument); - } - - var committedEventStream = new CommittedEventStream(uncommittedEventStream.EventSourceId); - committedEventStream.Append(uncommittedEventStream); - return committedEventStream; - } - - public EventSourceVersion GetLastCommittedVersion(EventSource eventSource, Guid eventSourceId) - { - var query = Query.EQ("EventSourceId", eventSourceId); - var sort = SortBy.Descending(Version); - var @event = _collection.FindAs(query).SetSortOrder(sort).FirstOrDefault(); - if (@event == null) - return EventSourceVersion.Zero; - - return EventSourceVersion.FromCombined(@event[Version].AsDouble); - } - - public IEnumerable GetBatch(int batchesToSkip, int batchSize) - { - var cursor = _collection.FindAllAs(); - cursor.SetSkip(batchSize * batchesToSkip); - cursor.SetLimit(batchSize); - var documents = cursor.ToArray(); - var events = ToEvents(documents); - return events; - } - - public IEnumerable GetAll() - { - var documents = _collection.FindAllAs().ToArray(); - var events = ToEvents(documents); - return events; - } - - int GetNextEventId() - { - var currentValue = 0; - var query = Query.EQ("_id", CollectionName); - var result = _incrementalKeysCollection.FindAndModify(query, SortBy.Null, Update.Inc(CurrentKey, 1), true); - if (result.ModifiedDocument == null) - { - var eventsCurrentValue = new BsonDocument(); - eventsCurrentValue["_id"] = CollectionName; - eventsCurrentValue[CurrentKey] = 1; - _incrementalKeysCollection.Insert(eventsCurrentValue); - currentValue = 1; - } - else - currentValue = result.ModifiedDocument[CurrentKey].AsInt32; - return currentValue; - } - - void AddMetaData(IEvent @event, BsonDocument eventDocument) - { - var eventType = @event.GetType(); - var logicalEventType = _eventMigrationHierarchyManager.GetLogicalTypeForEvent(eventType); - var migrationLevel = _eventMigrationHierarchyManager.GetCurrentMigrationLevelForLogicalEvent(logicalEventType); - eventDocument[EventType] = string.Format("{0}, {1}", eventType.FullName, eventType.Assembly.GetName().Name); - eventDocument[LogicalEventType] = string.Format("{0}, {1}", logicalEventType.FullName, logicalEventType.Assembly.GetName().Name); - eventDocument[Generation] = migrationLevel; - } - - void RemoveMetaData(BsonDocument document) - { - document.Remove(EventType); - document.Remove(LogicalEventType); - document.Remove(Generation); - } - - IEnumerable ToEvents(IEnumerable documents) - { - var events = new List(); - - foreach (var document in documents) - { - var eventType = Type.GetType(document[EventType].AsString); - RemoveMetaData(document); - var instance = BsonSerializer.Deserialize(document, eventType) as IEvent; - events.Add(instance); - } - return events; - } - } + public class EventStore : IEventStore + { + const string CollectionName = "Events"; + const string IncrementalKeysCollectionName = "_IncrementalKeys"; + const string EventType = "EventType"; + const string Generation = "Generation"; + const string Version = "Version"; + const string LogicalEventType = "LogicalEventType"; + const string CurrentKey = "CurrentKey"; + + EventStorageConfiguration _configuration; + MongoClient _server; + IMongoDatabase _database; + IMongoCollection _collection; + IMongoCollection _incrementalKeysCollection; + IEventMigrationHierarchyManager _eventMigrationHierarchyManager; + + static EventStore() + { + BsonSerializer.RegisterSerializer(typeof(EventSourceVersion), new EventSourceVersionSerializer()); + } + + public EventStore(EventStorageConfiguration configuration, IEventMigrationHierarchyManager eventMigrationHierarchyManager) + { + _configuration = configuration; + _eventMigrationHierarchyManager = eventMigrationHierarchyManager; + Initialize(); + } + + void Initialize() + { + var s = MongoClientSettings.FromUrl(new MongoUrl(_configuration.Url)); + if (_configuration.UseSSL) + { + s.UseSsl = true; + s.SslSettings = new SslSettings + { + EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12, + CheckCertificateRevocation = false + }; + } + _server = new MongoClient(s); + + + _database = _server.GetDatabase(_configuration.DefaultDatabase); + + _collection = _database.GetCollection(CollectionName); + + _incrementalKeysCollection = _database.GetCollection(IncrementalKeysCollectionName); + } + + public CommittedEventStream GetForEventSource(EventSource eventSource, Guid eventSourceId) + { + var eventSourceType = eventSource.GetType(); + var builder = Builders.Filter; + var filter = builder.Eq("EventSourceId", eventSourceId) & builder.Eq("EventSource", eventSourceType.AssemblyQualifiedName); + + var cursor = _collection.Find(filter); + var documents = cursor.ToList(); + var events = ToEvents(documents); + var stream = new CommittedEventStream(eventSourceId); + stream.Append(events); + return stream; + } + + public CommittedEventStream Commit(UncommittedEventStream uncommittedEventStream) + { + var eventArray = uncommittedEventStream.ToArray(); + for (var eventIndex = 0; eventIndex < eventArray.Length; eventIndex++) + { + var @event = eventArray[eventIndex]; + @event.Id = GetNextEventId(); + var eventDocument = @event.ToBsonDocument(); + AddMetaData(@event, eventDocument); + _collection.InsertOne(eventDocument); + } + + var committedEventStream = new CommittedEventStream(uncommittedEventStream.EventSourceId); + committedEventStream.Append(uncommittedEventStream); + return committedEventStream; + } + + public EventSourceVersion GetLastCommittedVersion(EventSource eventSource, Guid eventSourceId) + { + var filter = Builders.Filter.Eq("EventSourceId", eventSourceId); + var @event = _collection.Find(filter).SortBy(d => d.GetElement(Version)).FirstOrDefault(); + if (@event == null) + return EventSourceVersion.Zero; + + return EventSourceVersion.FromCombined(@event[Version].AsDouble); + } + + public IEnumerable GetBatch(int batchesToSkip, int batchSize) + { + var cursor = _collection.Find(new BsonDocument()); + cursor.Skip(batchSize * batchesToSkip); + cursor.Limit(batchSize); + var documents = cursor.ToList(); + var events = ToEvents(documents); + return events; + } + + public IEnumerable GetAll() + { + var documents = _collection.Find(new BsonDocument()).ToList(); + var events = ToEvents(documents); + return events; + } + + int GetNextEventId() + { + var currentValue = 0; + var query = Builders.Filter.Eq("_id", CollectionName); + var result = _incrementalKeysCollection.FindOneAndUpdate(query, Builders.Update.Inc(CurrentKey, 1), new FindOneAndUpdateOptions() { ReturnDocument = ReturnDocument.After, IsUpsert = true }); + + //TODO: Is this really necessary? The above is an upsert? + if (result?.AsBsonDocument == null) + { + var eventsCurrentValue = new BsonDocument(); + eventsCurrentValue["_id"] = CollectionName; + eventsCurrentValue[CurrentKey] = 1; + _incrementalKeysCollection.InsertOne(eventsCurrentValue); + currentValue = 1; + } + else + currentValue = result.AsBsonDocument[CurrentKey].AsInt32; + return currentValue; + } + + void AddMetaData(IEvent @event, BsonDocument eventDocument) + { + var eventType = @event.GetType(); + var logicalEventType = _eventMigrationHierarchyManager.GetLogicalTypeForEvent(eventType); + var migrationLevel = _eventMigrationHierarchyManager.GetCurrentMigrationLevelForLogicalEvent(logicalEventType); + eventDocument[EventType] = string.Format("{0}, {1}", eventType.FullName, eventType.Assembly.GetName().Name); + eventDocument[LogicalEventType] = string.Format("{0}, {1}", logicalEventType.FullName, logicalEventType.Assembly.GetName().Name); + eventDocument[Generation] = migrationLevel; + } + + void RemoveMetaData(BsonDocument document) + { + document.Remove(EventType); + document.Remove(LogicalEventType); + document.Remove(Generation); + } + + IEnumerable ToEvents(IEnumerable documents) + { + var events = new List(); + + foreach (var document in documents) + { + var eventType = Type.GetType(document[EventType].AsString); + RemoveMetaData(document); + var instance = BsonSerializer.Deserialize(document, eventType) as IEvent; + events.Add(instance); + } + return events; + } + } } diff --git a/Source/Bifrost.MongoDb/Events/EventSubscriptions.cs b/Source/Bifrost.MongoDb/Events/EventSubscriptions.cs index 548c43779..f3c24faf6 100644 --- a/Source/Bifrost.MongoDb/Events/EventSubscriptions.cs +++ b/Source/Bifrost.MongoDb/Events/EventSubscriptions.cs @@ -9,18 +9,18 @@ using Bifrost.Events; using MongoDB.Bson.Serialization; using MongoDB.Driver; -using MongoDB.Driver.Builders; +using MongoDB.Bson; -namespace Bifrost.MongoDB.Events +namespace Bifrost.MongoDb.Events { public class EventSubscriptions : IEventSubscriptions { const string CollectionName = "EventSubscriptions"; EventStorageConfiguration _configuration; - MongoServer _server; - MongoDatabase _database; - MongoCollection _collection; + MongoClient _server; + IMongoDatabase _database; + IMongoCollection _collection; static EventSubscriptions() { @@ -37,10 +37,20 @@ public EventSubscriptions(EventStorageConfiguration configuration) void Initialize() { - _server = MongoServer.Create(_configuration.Url); - _database = _server.GetDatabase(_configuration.DefaultDatabase); - if (!_database.CollectionExists(CollectionName)) - _database.CreateCollection(CollectionName); + var s = MongoClientSettings.FromUrl(new MongoUrl(_configuration.Url)); + if (_configuration.UseSSL) + { + s.UseSsl = true; + s.SslSettings = new SslSettings + { + EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12, + CheckCertificateRevocation = false + }; + } + + _server = new MongoClient(s); + + _database = _server.GetDatabase(_configuration.DefaultDatabase); _collection = _database.GetCollection(CollectionName); } @@ -48,18 +58,21 @@ void Initialize() public IEnumerable GetAll() { - return _collection.FindAll().ToArray(); + return _collection.Find(new BsonDocument()).ToList(); } public void Save(EventSubscription subscription) { - _collection.Save(subscription); + var filter = Builders.Filter.Eq(s => s.Id, subscription.Id); + //var update = Builders.Update. + _collection.ReplaceOne(filter, subscription, new UpdateOptions() { IsUpsert = true }); } public void ResetLastEventForAllSubscriptions() { - var update = Update.Set("LastEventId",0); - _collection.Update(Query.Null, update, UpdateFlags.Multi); + var update = Builders.Update.Set(s => s.LastEventId, 0); + + _collection.UpdateMany(new BsonDocument(), update); } } } diff --git a/Source/Bifrost.MongoDb/Events/MethodInfoSerializer.cs b/Source/Bifrost.MongoDb/Events/MethodInfoSerializer.cs index 47bdd2d47..1428dc1c5 100644 --- a/Source/Bifrost.MongoDb/Events/MethodInfoSerializer.cs +++ b/Source/Bifrost.MongoDb/Events/MethodInfoSerializer.cs @@ -10,79 +10,68 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Options; -namespace Bifrost.MongoDB.Events +namespace Bifrost.MongoDb.Events { - public class MethodInfoSerializer : IBsonSerializer - { - public object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options) - { - bsonReader.ReadStartDocument(); - - bsonReader.ReadName(); - var typeName = bsonReader.ReadString(); - bsonReader.ReadName(); - var methodSignature = bsonReader.ReadString(); - - bsonReader.ReadEndDocument(); - - var type = Type.GetType(typeName); - if (type != null) - { - var method = type.GetMethods().Where(m => GetMethodSignature(m) == methodSignature).SingleOrDefault(); - return method; - } - - return null; - } - - public void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options) - { - var method = (MethodInfo)value; - - bsonWriter.WriteStartDocument(); - bsonWriter.WriteName("Type"); - bsonWriter.WriteString(method.DeclaringType.AssemblyQualifiedName); - bsonWriter.WriteName("Method"); - bsonWriter.WriteString(GetMethodSignature(method)); - - bsonWriter.WriteEndDocument(); - - } - - string GetMethodSignature(MethodInfo method) - { - var builder = new StringBuilder(); - builder.Append(method.Name); - builder.Append("("); - - foreach (var parameter in method.GetParameters()) - builder.AppendFormat("{0} {1}", parameter.ParameterType.Name, parameter.Name); - - builder.Append(")"); - return builder.ToString(); - } - - - public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options) - { - throw new NotImplementedException(); - } - - public bool GetDocumentId(object document, out object id, out Type idNominalType, out IIdGenerator idGenerator) - { - throw new NotImplementedException(); - } - - public void SetDocumentId(object document, object id) - { - throw new NotImplementedException(); - } - - public IBsonSerializationOptions GetDefaultSerializationOptions() - { - var options = new DocumentSerializationOptions(); - return options; - } - - } + public class MethodInfoSerializer : IBsonSerializer + { + public Type ValueType + { + get + { + return typeof(MethodInfo); + } + } + + public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + var bsonReader = context.Reader; + bsonReader.ReadStartDocument(); + + bsonReader.ReadName(); + var typeName = bsonReader.ReadString(); + bsonReader.ReadName(); + var methodSignature = bsonReader.ReadString(); + + bsonReader.ReadEndDocument(); + + var type = Type.GetType(typeName); + if (type != null) + { + var method = type.GetMethods().Where(m => GetMethodSignature(m) == methodSignature).SingleOrDefault(); + return method; + } + + return null; + } + + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) + { + var method = (MethodInfo)value; + var bsonWriter = context.Writer; + + bsonWriter.WriteStartDocument(); + bsonWriter.WriteName("Type"); + bsonWriter.WriteString(method.DeclaringType.AssemblyQualifiedName); + bsonWriter.WriteName("Method"); + bsonWriter.WriteString(GetMethodSignature(method)); + + bsonWriter.WriteEndDocument(); + } + + string GetMethodSignature(MethodInfo method) + { + var builder = new StringBuilder(); + builder.Append(method.Name); + builder.Append("("); + + foreach (var parameter in method.GetParameters()) + builder.AppendFormat("{0} {1}", parameter.ParameterType.Name, parameter.Name); + + builder.Append(")"); + return builder.ToString(); + } + + + + } } diff --git a/Source/Bifrost.MongoDb/Events/TypeSerializer.cs b/Source/Bifrost.MongoDb/Events/TypeSerializer.cs index ed3147071..2f73dcb38 100644 --- a/Source/Bifrost.MongoDb/Events/TypeSerializer.cs +++ b/Source/Bifrost.MongoDb/Events/TypeSerializer.cs @@ -7,44 +7,30 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Options; -namespace Bifrost.MongoDB.Events +namespace Bifrost.MongoDb.Events { - public class TypeSerializer : IBsonSerializer - { - public object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options) - { - var typeName = bsonReader.ReadString(); - var type = Type.GetType(typeName); - return type; - } - - public void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options) - { - var type = (Type)value; - bsonWriter.WriteString(type.AssemblyQualifiedName); - } - - - - public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options) - { - throw new NotImplementedException(); - } - - public bool GetDocumentId(object document, out object id, out Type idNominalType, out IIdGenerator idGenerator) - { - throw new NotImplementedException(); - } - - public void SetDocumentId(object document, object id) - { - throw new NotImplementedException(); - } - - public IBsonSerializationOptions GetDefaultSerializationOptions() - { - var options = new DocumentSerializationOptions(); - return options; - } - } + public class TypeSerializer : IBsonSerializer + { + public Type ValueType + { + get + { + return typeof(Type); + } + } + public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + var bsonReader = context.Reader; + var typeName = bsonReader.ReadString(); + var type = Type.GetType(typeName); + return type; + } + + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) + { + var type = (Type)value; + var bsonWriter = context.Writer; + bsonWriter.WriteString(type.AssemblyQualifiedName); + } + } } diff --git a/Source/Bifrost.MongoDb/packages.config b/Source/Bifrost.MongoDb/packages.config index 5a514137b..75ea6d2ef 100644 --- a/Source/Bifrost.MongoDb/packages.config +++ b/Source/Bifrost.MongoDb/packages.config @@ -1,4 +1,25 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Bifrost.MongoDb/project.json b/Source/Bifrost.MongoDb/project.json index 52a560256..5c9ffea6d 100644 --- a/Source/Bifrost.MongoDb/project.json +++ b/Source/Bifrost.MongoDb/project.json @@ -40,8 +40,8 @@ "debugType": "portable" }, "dependencies": { - "Bifrost": "1.0.0-*", - "mongocsharpdriver": "1.9.2" + "Bifrost": "1.0.0-*", + "MongoDB.Driver": "2.4.2" }, "runtimes": { "win": {}