using System.Text.Json; using Elsa.Features.Abstractions; using Elsa.Features.Services; using Elsa.KeyValues.Entities; using Elsa.MongoDb.Contracts; using Elsa.MongoDb.NamingStrategies; using Elsa.MongoDb.Options; using Elsa.MongoDb.Serializers; using Elsa.Workflows.Memory; using Elsa.Workflows.Runtime.Entities; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; using MongoDB.Driver.Core.Extensions.DiagnosticSources; namespace Elsa.MongoDb.Features; /// /// Configures MongoDb. /// public class MongoDbFeature : FeatureBase { /// public MongoDbFeature(IModule module) : base(module) { } /// /// The MongoDB connection string. /// public string ConnectionString { get; set; } = default!; /// /// A delegate that configures MongoDb. /// public Action Options { get; set; } = _ => { }; /// /// A delegate that creates an instance of an implementation of . /// public Func CollectionNamingStrategy { get; set; } = sp => sp.GetRequiredService(); /// public override void Apply() { Services.Configure(Options); var mongoUrl = new MongoUrl(ConnectionString); Services.AddSingleton(sp => CreateMongoClient(sp, mongoUrl)); Services.AddScoped(sp => CreateDatabase(sp, mongoUrl)); Services.TryAddScoped(); Services.AddScoped(CollectionNamingStrategy); RegisterSerializers(); RegisterClassMaps(); } private static void RegisterSerializers() { TryRegisterSerializerOrSkipWhenExist(typeof(object), new PolymorphicSerializer()); TryRegisterSerializerOrSkipWhenExist(typeof(Type), new TypeSerializer()); TryRegisterSerializerOrSkipWhenExist(typeof(Variable), new VariableSerializer()); TryRegisterSerializerOrSkipWhenExist(typeof(Version), new VersionSerializer()); TryRegisterSerializerOrSkipWhenExist(typeof(JsonElement), new JsonElementSerializer()); } private static void RegisterClassMaps() { BsonClassMap.TryRegisterClassMap(cm => { cm.AutoMap(); // Automatically map other properties cm .MapIdProperty(b => b.BookmarkId) .SetSerializer(new StringSerializer(BsonType.String)); }); BsonClassMap.TryRegisterClassMap(map => { map.AutoMap(); map.SetIgnoreExtraElements(true); // Needed for missing ID property }); } private static void TryRegisterSerializerOrSkipWhenExist(Type type, IBsonSerializer serializer) { try { BsonSerializer.TryRegisterSerializer(type, serializer); } catch (BsonSerializationException ex) { } } private static IMongoClient CreateMongoClient(IServiceProvider sp, MongoUrl mongoUrl) { var options = sp.GetRequiredService>().Value; var settings = MongoClientSettings.FromUrl(mongoUrl); settings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber()); settings.ApplicationName = GetApplicationName(settings); settings.WriteConcern = options.WriteConcern; settings.ReadConcern = options.ReadConcern; settings.ReadPreference = options.ReadPreference; settings.RetryReads = options.RetryReads; settings.RetryWrites = options.RetryWrites; settings.SslSettings = options.SslSettings; return new MongoClient(settings); } private static IMongoDatabase CreateDatabase(IServiceProvider sp, MongoUrl mongoUrl) { var client = sp.GetRequiredService(); return client.GetDatabase(mongoUrl.DatabaseName); } private static string GetApplicationName(MongoClientSettings settings) => string.IsNullOrWhiteSpace(settings.ApplicationName) ? "elsa_workflows" : settings.ApplicationName; }