From eb388caed19ff058cf42165a22f40d41a42024b0 Mon Sep 17 00:00:00 2001 From: axeleron007 Date: Fri, 18 Jun 2021 16:22:57 +0300 Subject: [PATCH] Setup Elsa Server project with Features #1032 Setup Elsa Server project with Features. --- .../Elsa.Activities.Console/ConsoleStartup.cs | 14 + .../Elsa.Activities.Console/IsExternalInit.cs | 17 + .../Elsa.Activities.Webhooks.csproj | 7 +- .../WebhookServiceCollectionExtensions.cs | 232 ++++++++++ .../WebhookStartup.cs | 16 + ...ks.Persistence.EntityFramework.Core.csproj | 1 - .../WebhookServiceCollectionExtensions.cs | 428 +++++++++--------- .../Attributes/FeatureAttribute.cs | 23 + .../Extensions/AssemblyExtensions.cs | 2 + src/core/Elsa.Core/ElsaOptionsBuilder.cs | 29 ++ .../Elsa.Samples.Server.Host.csproj | 1 + .../Elsa.Samples.Server.Host/Startup.cs | 19 +- .../appsettings.Development.json | 3 + .../Elsa.Testing.Shared.csproj | 2 +- 14 files changed, 575 insertions(+), 219 deletions(-) create mode 100644 src/activities/Elsa.Activities.Console/ConsoleStartup.cs create mode 100644 src/activities/Elsa.Activities.Console/IsExternalInit.cs create mode 100644 src/activities/webhooks/Elsa.Activities.Webhooks/Extensions/WebhookServiceCollectionExtensions.cs create mode 100644 src/activities/webhooks/Elsa.Activities.Webhooks/WebhookStartup.cs create mode 100644 src/core/Elsa.Abstractions/Attributes/FeatureAttribute.cs diff --git a/src/activities/Elsa.Activities.Console/ConsoleStartup.cs b/src/activities/Elsa.Activities.Console/ConsoleStartup.cs new file mode 100644 index 000000000..d76e3a4f3 --- /dev/null +++ b/src/activities/Elsa.Activities.Console/ConsoleStartup.cs @@ -0,0 +1,14 @@ +using Elsa.Attributes; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Activities.Console +{ + [Feature("Console")] + public class ConsoleStartup + { + public void ConfigureElsa(ElsaOptionsBuilder elsa) + { + elsa.AddConsoleActivities(); + } + } +} diff --git a/src/activities/Elsa.Activities.Console/IsExternalInit.cs b/src/activities/Elsa.Activities.Console/IsExternalInit.cs new file mode 100644 index 000000000..8ce226317 --- /dev/null +++ b/src/activities/Elsa.Activities.Console/IsExternalInit.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; + +// ReSharper disable once CheckNamespace +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal static class IsExternalInit + { + } +} \ No newline at end of file diff --git a/src/activities/webhooks/Elsa.Activities.Webhooks/Elsa.Activities.Webhooks.csproj b/src/activities/webhooks/Elsa.Activities.Webhooks/Elsa.Activities.Webhooks.csproj index 31ba5a08f..911e7bf4d 100644 --- a/src/activities/webhooks/Elsa.Activities.Webhooks/Elsa.Activities.Webhooks.csproj +++ b/src/activities/webhooks/Elsa.Activities.Webhooks/Elsa.Activities.Webhooks.csproj @@ -1,4 +1,4 @@ - + @@ -35,6 +35,11 @@ + + + + + diff --git a/src/activities/webhooks/Elsa.Activities.Webhooks/Extensions/WebhookServiceCollectionExtensions.cs b/src/activities/webhooks/Elsa.Activities.Webhooks/Extensions/WebhookServiceCollectionExtensions.cs new file mode 100644 index 000000000..2d8177efb --- /dev/null +++ b/src/activities/webhooks/Elsa.Activities.Webhooks/Extensions/WebhookServiceCollectionExtensions.cs @@ -0,0 +1,232 @@ +using System; +using Elsa.Runtime; +using Elsa.Webhooks.Persistence.EntityFramework.Core; +using Elsa.Webhooks.Persistence.EntityFramework.Core.Services; +using Elsa.Webhooks.Persistence.EntityFramework.Core.StartupTasks; +using Elsa.Webhooks.Persistence.EntityFramework.Core.Stores; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Elsa.Activities.Webhooks.Extensions +{ + public static class WebhookServiceCollectionExtensions + { + /// + /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. + /// + /// + /// + /// Pooled DB Context instances is a performance optimisation which is documented in more detail at + /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. + /// + /// + /// An Elsa options builder + /// A configuration builder callback + /// If true then database migrations will be auto-executed on startup + /// The Elsa options builder, so calls may be chained + public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, + Action configure, + bool autoRunMigrations = true) => + webhookOptions.UseEntityFrameworkPersistence(configure, autoRunMigrations); + + /// + /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. + /// + /// + /// + /// Pooled DB Context instances is a performance optimisation which is documented in more detail at + /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. + /// + /// + /// An Elsa options builder + /// A configuration builder callback + /// If true then database migrations will be auto-executed on startup + /// The concrete type of to use. + /// The Elsa options builder, so calls may be chained + public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, + Action configure, + bool autoRunMigrations = true) where TWebhookContext : WebhookContext => + webhookOptions.UseEntityFrameworkPersistence((_, builder) => configure(builder), autoRunMigrations); + + /// + /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. + /// + /// + /// + /// Pooled DB Context instances is a performance optimisation which is documented in more detail at + /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. + /// + /// + /// An Elsa options builder + /// A configuration builder callback, which also provides access to a service provider + /// If true then database migrations will be auto-executed on startup + /// The Elsa options builder, so calls may be chained + public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, + Action configure, + bool autoRunMigrations = true) => + webhookOptions.UseEntityFrameworkPersistence(configure, autoRunMigrations); + + /// + /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. + /// + /// + /// + /// Pooled DB Context instances is a performance optimisation which is documented in more detail at + /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. + /// + /// + /// An Elsa options builder + /// A configuration builder callback, which also provides access to a service provider + /// If true then database migrations will be auto-executed on startup + /// The concrete type of to use. + /// The Elsa options builder, so calls may be chained + public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, + Action configure, + bool autoRunMigrations = true) where TWebhookContext : WebhookContext => + UseEntityFrameworkPersistence(webhookOptions, configure, autoRunMigrations, true, ServiceLifetime.Singleton); + + /// + /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. + /// + /// + /// + /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant + /// application, where re-use of DB Context objects is impractical. + /// + /// + /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of + /// the DB Context may differ, it is not feasible to try to automatically migrate them. + /// Your application is ultimately responsible for executing the contents of the class in a manner + /// which is suitable for your use-case. + /// + /// + /// An Elsa options builder + /// A configuration builder callback + /// The service lifetime which will be used for each DB Context instance + /// If true then database migrations will be auto-executed on startup + /// The Elsa options builder, so calls may be chained + public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, + Action configure, + ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, + bool autoRunMigrations = false) => + webhookOptions.UseNonPooledEntityFrameworkPersistence(configure, serviceLifetime, autoRunMigrations); + + /// + /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. + /// + /// + /// + /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant + /// application, where re-use of DB Context objects is impractical. + /// + /// + /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of + /// the DB Context may differ, it is not feasible to try to automatically migrate them. + /// Your application is ultimately responsible for executing the contents of the class in a manner + /// which is suitable for your use-case. + /// + /// + /// An Elsa options builder + /// A configuration builder callback + /// The service lifetime which will be used for each DB Context instance + /// If true then database migrations will be auto-executed on startup + /// The concrete type of to use. + /// The Elsa options builder, so calls may be chained + public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, + Action configure, + ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, + bool autoRunMigrations = false) where TWebhookContext : WebhookContext => + webhookOptions.UseNonPooledEntityFrameworkPersistence((_, builder) => configure(builder), serviceLifetime, autoRunMigrations); + + /// + /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. + /// + /// + /// + /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant + /// application, where re-use of DB Context objects is impractical. + /// + /// + /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of + /// the DB Context may differ, it is not feasible to try to automatically migrate them. + /// Your application is ultimately responsible for executing the contents of the class in a manner + /// which is suitable for your use-case. + /// + /// + /// An Elsa options builder + /// A configuration builder callback, which also provides access to a service provider + /// The service lifetime which will be used for each DB Context instance + /// If true then database migrations will be auto-executed on startup + /// The Elsa options builder, so calls may be chained + public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, + Action configure, + ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, + bool autoRunMigrations = false) => + webhookOptions.UseNonPooledEntityFrameworkPersistence(configure, serviceLifetime, autoRunMigrations); + + /// + /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. + /// + /// + /// + /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant + /// application, where re-use of DB Context objects is impractical. + /// + /// + /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of + /// the DB Context may differ, it is not feasible to try to automatically migrate them. + /// Your application is ultimately responsible for executing the contents of the class in a manner + /// which is suitable for your use-case. + /// + /// + /// An Elsa options builder + /// A configuration builder callback, which also provides access to a service provider + /// The service lifetime which will be used for each DB Context instance + /// If true then database migrations will be auto-executed on startup + /// The concrete type of to use. + /// The Elsa options builder, so calls may be chained + public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, + Action configure, + ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, + bool autoRunMigrations = false) where TWebhookContext : WebhookContext => + UseEntityFrameworkPersistence(webhookOptions, configure, autoRunMigrations, false, serviceLifetime); + + static WebhookOptionsBuilder UseEntityFrameworkPersistence(WebhookOptionsBuilder webhookOptions, + Action configure, + bool autoRunMigrations, + bool useContextPooling, + ServiceLifetime serviceLifetime) where TWebhookContext : WebhookContext + { + /* Auto-running migrations is intentionally unavailable when not using context pooling. + * When we aren't using pooling then it probably means that each DB Context is different + * in some manner. That could easily mean the connection strings (IE: Contexts might not + * all connect to the same DB). In that case, without further logic (which can't be + * pre-empted by Elsa), we can't be sure we're connecting to the right DBs when running + * migrations. + * + * It's much more sane just to explicitly not-support it and leave it to the app developer. + * They can run their own migrations in line with their own logic. + */ + + if (useContextPooling) + webhookOptions.Services.AddPooledDbContextFactory(configure); + else + webhookOptions.Services.AddDbContextFactory(configure, serviceLifetime); + + webhookOptions.Services + .AddSingleton>() + .AddScoped(); + + if (autoRunMigrations) + webhookOptions.Services.AddStartupTask(); + + webhookOptions.UseWebhookDefinitionStore(sp => sp.GetRequiredService()); + + return webhookOptions; + } + } +} diff --git a/src/activities/webhooks/Elsa.Activities.Webhooks/WebhookStartup.cs b/src/activities/webhooks/Elsa.Activities.Webhooks/WebhookStartup.cs new file mode 100644 index 000000000..fbd0c21c9 --- /dev/null +++ b/src/activities/webhooks/Elsa.Activities.Webhooks/WebhookStartup.cs @@ -0,0 +1,16 @@ +using Elsa.Activities.Webhooks.Extensions; +using Elsa.Attributes; +using Elsa.Webhooks.Persistence.EntityFramework.Sqlite; + +namespace Elsa.Activities.Webhooks +{ + [Feature("Webhook")] + public class WebhookStartup + { + //, IConfiguration configuration + public void ConfigureElsa(ElsaOptionsBuilder elsa) + { + elsa.AddWebhooks(webhooks => webhooks.UseEntityFrameworkPersistence(ef => ef.UseWebhookSqlite())); + } + } +} diff --git a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/Elsa.Webhooks.Persistence.EntityFramework.Core.csproj b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/Elsa.Webhooks.Persistence.EntityFramework.Core.csproj index c6b9f6818..f35f7aca7 100644 --- a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/Elsa.Webhooks.Persistence.EntityFramework.Core.csproj +++ b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/Elsa.Webhooks.Persistence.EntityFramework.Core.csproj @@ -19,7 +19,6 @@ - diff --git a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/Extensions/WebhookServiceCollectionExtensions.cs b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/Extensions/WebhookServiceCollectionExtensions.cs index 729ed83ee..e2f321776 100644 --- a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/Extensions/WebhookServiceCollectionExtensions.cs +++ b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/Extensions/WebhookServiceCollectionExtensions.cs @@ -1,228 +1,228 @@ -using System; -using Elsa.Webhooks.Persistence.EntityFramework.Core.Services; -using Elsa.Webhooks.Persistence.EntityFramework.Core.StartupTasks; -using Elsa.Webhooks.Persistence.EntityFramework.Core.Stores; -using Elsa.Activities.Webhooks; -using Elsa.Runtime; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; +//using System; +//using Elsa.Webhooks.Persistence.EntityFramework.Core.Services; +//using Elsa.Webhooks.Persistence.EntityFramework.Core.StartupTasks; +//using Elsa.Webhooks.Persistence.EntityFramework.Core.Stores; +//using Elsa.Activities.Webhooks; +//using Elsa.Runtime; +//using Microsoft.EntityFrameworkCore; +//using Microsoft.Extensions.DependencyInjection; -namespace Elsa.Webhooks.Persistence.EntityFramework.Core.Extensions -{ - public static class WebhookServiceCollectionExtensions - { - /// - /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. - /// - /// - /// - /// Pooled DB Context instances is a performance optimisation which is documented in more detail at - /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. - /// - /// - /// An Elsa options builder - /// A configuration builder callback - /// If true then database migrations will be auto-executed on startup - /// The Elsa options builder, so calls may be chained - public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, - Action configure, - bool autoRunMigrations = true) => - webhookOptions.UseEntityFrameworkPersistence(configure, autoRunMigrations); +//namespace Elsa.Webhooks.Persistence.EntityFramework.Core.Extensions +//{ +// public static class WebhookServiceCollectionExtensions +// { +// /// +// /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. +// /// +// /// +// /// +// /// Pooled DB Context instances is a performance optimisation which is documented in more detail at +// /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. +// /// +// /// +// /// An Elsa options builder +// /// A configuration builder callback +// /// If true then database migrations will be auto-executed on startup +// /// The Elsa options builder, so calls may be chained +// public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, +// Action configure, +// bool autoRunMigrations = true) => +// webhookOptions.UseEntityFrameworkPersistence(configure, autoRunMigrations); - /// - /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. - /// - /// - /// - /// Pooled DB Context instances is a performance optimisation which is documented in more detail at - /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. - /// - /// - /// An Elsa options builder - /// A configuration builder callback - /// If true then database migrations will be auto-executed on startup - /// The concrete type of to use. - /// The Elsa options builder, so calls may be chained - public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, - Action configure, - bool autoRunMigrations = true) where TWebhookContext : WebhookContext => - webhookOptions.UseEntityFrameworkPersistence((_, builder) => configure(builder), autoRunMigrations); +// /// +// /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. +// /// +// /// +// /// +// /// Pooled DB Context instances is a performance optimisation which is documented in more detail at +// /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. +// /// +// /// +// /// An Elsa options builder +// /// A configuration builder callback +// /// If true then database migrations will be auto-executed on startup +// /// The concrete type of to use. +// /// The Elsa options builder, so calls may be chained +// public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, +// Action configure, +// bool autoRunMigrations = true) where TWebhookContext : WebhookContext => +// webhookOptions.UseEntityFrameworkPersistence((_, builder) => configure(builder), autoRunMigrations); - /// - /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. - /// - /// - /// - /// Pooled DB Context instances is a performance optimisation which is documented in more detail at - /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. - /// - /// - /// An Elsa options builder - /// A configuration builder callback, which also provides access to a service provider - /// If true then database migrations will be auto-executed on startup - /// The Elsa options builder, so calls may be chained - public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, - Action configure, - bool autoRunMigrations = true) => - webhookOptions.UseEntityFrameworkPersistence(configure, autoRunMigrations); +// /// +// /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. +// /// +// /// +// /// +// /// Pooled DB Context instances is a performance optimisation which is documented in more detail at +// /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. +// /// +// /// +// /// An Elsa options builder +// /// A configuration builder callback, which also provides access to a service provider +// /// If true then database migrations will be auto-executed on startup +// /// The Elsa options builder, so calls may be chained +// public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, +// Action configure, +// bool autoRunMigrations = true) => +// webhookOptions.UseEntityFrameworkPersistence(configure, autoRunMigrations); - /// - /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. - /// - /// - /// - /// Pooled DB Context instances is a performance optimisation which is documented in more detail at - /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. - /// - /// - /// An Elsa options builder - /// A configuration builder callback, which also provides access to a service provider - /// If true then database migrations will be auto-executed on startup - /// The concrete type of to use. - /// The Elsa options builder, so calls may be chained - public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, - Action configure, - bool autoRunMigrations = true) where TWebhookContext : WebhookContext => - UseEntityFrameworkPersistence(webhookOptions, configure, autoRunMigrations, true, ServiceLifetime.Singleton); +// /// +// /// Configures Elsa to use Entity Framework Core for persistence, using pooled DB Context instances. +// /// +// /// +// /// +// /// Pooled DB Context instances is a performance optimisation which is documented in more detail at +// /// https://docs.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling. +// /// +// /// +// /// An Elsa options builder +// /// A configuration builder callback, which also provides access to a service provider +// /// If true then database migrations will be auto-executed on startup +// /// The concrete type of to use. +// /// The Elsa options builder, so calls may be chained +// public static WebhookOptionsBuilder UseEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, +// Action configure, +// bool autoRunMigrations = true) where TWebhookContext : WebhookContext => +// UseEntityFrameworkPersistence(webhookOptions, configure, autoRunMigrations, true, ServiceLifetime.Singleton); - /// - /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. - /// - /// - /// - /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant - /// application, where re-use of DB Context objects is impractical. - /// - /// - /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of - /// the DB Context may differ, it is not feasible to try to automatically migrate them. - /// Your application is ultimately responsible for executing the contents of the class in a manner - /// which is suitable for your use-case. - /// - /// - /// An Elsa options builder - /// A configuration builder callback - /// The service lifetime which will be used for each DB Context instance - /// If true then database migrations will be auto-executed on startup - /// The Elsa options builder, so calls may be chained - public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, - Action configure, - ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, - bool autoRunMigrations = false) => - webhookOptions.UseNonPooledEntityFrameworkPersistence(configure, serviceLifetime, autoRunMigrations); +// /// +// /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. +// /// +// /// +// /// +// /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant +// /// application, where re-use of DB Context objects is impractical. +// /// +// /// +// /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of +// /// the DB Context may differ, it is not feasible to try to automatically migrate them. +// /// Your application is ultimately responsible for executing the contents of the class in a manner +// /// which is suitable for your use-case. +// /// +// /// +// /// An Elsa options builder +// /// A configuration builder callback +// /// The service lifetime which will be used for each DB Context instance +// /// If true then database migrations will be auto-executed on startup +// /// The Elsa options builder, so calls may be chained +// public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, +// Action configure, +// ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, +// bool autoRunMigrations = false) => +// webhookOptions.UseNonPooledEntityFrameworkPersistence(configure, serviceLifetime, autoRunMigrations); - /// - /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. - /// - /// - /// - /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant - /// application, where re-use of DB Context objects is impractical. - /// - /// - /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of - /// the DB Context may differ, it is not feasible to try to automatically migrate them. - /// Your application is ultimately responsible for executing the contents of the class in a manner - /// which is suitable for your use-case. - /// - /// - /// An Elsa options builder - /// A configuration builder callback - /// The service lifetime which will be used for each DB Context instance - /// If true then database migrations will be auto-executed on startup - /// The concrete type of to use. - /// The Elsa options builder, so calls may be chained - public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, - Action configure, - ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, - bool autoRunMigrations = false) where TWebhookContext : WebhookContext => - webhookOptions.UseNonPooledEntityFrameworkPersistence((_, builder) => configure(builder), serviceLifetime, autoRunMigrations); +// /// +// /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. +// /// +// /// +// /// +// /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant +// /// application, where re-use of DB Context objects is impractical. +// /// +// /// +// /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of +// /// the DB Context may differ, it is not feasible to try to automatically migrate them. +// /// Your application is ultimately responsible for executing the contents of the class in a manner +// /// which is suitable for your use-case. +// /// +// /// +// /// An Elsa options builder +// /// A configuration builder callback +// /// The service lifetime which will be used for each DB Context instance +// /// If true then database migrations will be auto-executed on startup +// /// The concrete type of to use. +// /// The Elsa options builder, so calls may be chained +// public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, +// Action configure, +// ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, +// bool autoRunMigrations = false) where TWebhookContext : WebhookContext => +// webhookOptions.UseNonPooledEntityFrameworkPersistence((_, builder) => configure(builder), serviceLifetime, autoRunMigrations); - /// - /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. - /// - /// - /// - /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant - /// application, where re-use of DB Context objects is impractical. - /// - /// - /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of - /// the DB Context may differ, it is not feasible to try to automatically migrate them. - /// Your application is ultimately responsible for executing the contents of the class in a manner - /// which is suitable for your use-case. - /// - /// - /// An Elsa options builder - /// A configuration builder callback, which also provides access to a service provider - /// The service lifetime which will be used for each DB Context instance - /// If true then database migrations will be auto-executed on startup - /// The Elsa options builder, so calls may be chained - public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, - Action configure, - ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, - bool autoRunMigrations = false) => - webhookOptions.UseNonPooledEntityFrameworkPersistence(configure, serviceLifetime, autoRunMigrations); +// /// +// /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. +// /// +// /// +// /// +// /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant +// /// application, where re-use of DB Context objects is impractical. +// /// +// /// +// /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of +// /// the DB Context may differ, it is not feasible to try to automatically migrate them. +// /// Your application is ultimately responsible for executing the contents of the class in a manner +// /// which is suitable for your use-case. +// /// +// /// +// /// An Elsa options builder +// /// A configuration builder callback, which also provides access to a service provider +// /// The service lifetime which will be used for each DB Context instance +// /// If true then database migrations will be auto-executed on startup +// /// The Elsa options builder, so calls may be chained +// public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, +// Action configure, +// ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, +// bool autoRunMigrations = false) => +// webhookOptions.UseNonPooledEntityFrameworkPersistence(configure, serviceLifetime, autoRunMigrations); - /// - /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. - /// - /// - /// - /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant - /// application, where re-use of DB Context objects is impractical. - /// - /// - /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of - /// the DB Context may differ, it is not feasible to try to automatically migrate them. - /// Your application is ultimately responsible for executing the contents of the class in a manner - /// which is suitable for your use-case. - /// - /// - /// An Elsa options builder - /// A configuration builder callback, which also provides access to a service provider - /// The service lifetime which will be used for each DB Context instance - /// If true then database migrations will be auto-executed on startup - /// The concrete type of to use. - /// The Elsa options builder, so calls may be chained - public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, - Action configure, - ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, - bool autoRunMigrations = false) where TWebhookContext : WebhookContext => - UseEntityFrameworkPersistence(webhookOptions, configure, autoRunMigrations, false, serviceLifetime); +// /// +// /// Configures Elsa to use Entity Framework Core for persistence, without using pooled DB Context instances. +// /// +// /// +// /// +// /// Use this method when you do not wish to use DB connection pooling, such as when integrating with a multi-tenant +// /// application, where re-use of DB Context objects is impractical. +// /// +// /// +// /// Although auto-running of migrations is supported in this scenario, use this with caution. When pooling is not in use and each instance of +// /// the DB Context may differ, it is not feasible to try to automatically migrate them. +// /// Your application is ultimately responsible for executing the contents of the class in a manner +// /// which is suitable for your use-case. +// /// +// /// +// /// An Elsa options builder +// /// A configuration builder callback, which also provides access to a service provider +// /// The service lifetime which will be used for each DB Context instance +// /// If true then database migrations will be auto-executed on startup +// /// The concrete type of to use. +// /// The Elsa options builder, so calls may be chained +// public static WebhookOptionsBuilder UseNonPooledEntityFrameworkPersistence(this WebhookOptionsBuilder webhookOptions, +// Action configure, +// ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, +// bool autoRunMigrations = false) where TWebhookContext : WebhookContext => +// UseEntityFrameworkPersistence(webhookOptions, configure, autoRunMigrations, false, serviceLifetime); - static WebhookOptionsBuilder UseEntityFrameworkPersistence(WebhookOptionsBuilder webhookOptions, - Action configure, - bool autoRunMigrations, - bool useContextPooling, - ServiceLifetime serviceLifetime) where TWebhookContext : WebhookContext - { - /* Auto-running migrations is intentionally unavailable when not using context pooling. - * When we aren't using pooling then it probably means that each DB Context is different - * in some manner. That could easily mean the connection strings (IE: Contexts might not - * all connect to the same DB). In that case, without further logic (which can't be - * pre-empted by Elsa), we can't be sure we're connecting to the right DBs when running - * migrations. - * - * It's much more sane just to explicitly not-support it and leave it to the app developer. - * They can run their own migrations in line with their own logic. - */ +// static WebhookOptionsBuilder UseEntityFrameworkPersistence(WebhookOptionsBuilder webhookOptions, +// Action configure, +// bool autoRunMigrations, +// bool useContextPooling, +// ServiceLifetime serviceLifetime) where TWebhookContext : WebhookContext +// { +// /* Auto-running migrations is intentionally unavailable when not using context pooling. +// * When we aren't using pooling then it probably means that each DB Context is different +// * in some manner. That could easily mean the connection strings (IE: Contexts might not +// * all connect to the same DB). In that case, without further logic (which can't be +// * pre-empted by Elsa), we can't be sure we're connecting to the right DBs when running +// * migrations. +// * +// * It's much more sane just to explicitly not-support it and leave it to the app developer. +// * They can run their own migrations in line with their own logic. +// */ - if (useContextPooling) - webhookOptions.Services.AddPooledDbContextFactory(configure); - else - webhookOptions.Services.AddDbContextFactory(configure, serviceLifetime); +// if (useContextPooling) +// webhookOptions.Services.AddPooledDbContextFactory(configure); +// else +// webhookOptions.Services.AddDbContextFactory(configure, serviceLifetime); - webhookOptions.Services - .AddSingleton>() - .AddScoped(); +// webhookOptions.Services +// .AddSingleton>() +// .AddScoped(); - if (autoRunMigrations) - webhookOptions.Services.AddStartupTask(); +// if (autoRunMigrations) +// webhookOptions.Services.AddStartupTask(); - webhookOptions.UseWebhookDefinitionStore(sp => sp.GetRequiredService()); +// webhookOptions.UseWebhookDefinitionStore(sp => sp.GetRequiredService()); - return webhookOptions; - } - } -} +// return webhookOptions; +// } +// } +//} diff --git a/src/core/Elsa.Abstractions/Attributes/FeatureAttribute.cs b/src/core/Elsa.Abstractions/Attributes/FeatureAttribute.cs new file mode 100644 index 000000000..355c9d3a0 --- /dev/null +++ b/src/core/Elsa.Abstractions/Attributes/FeatureAttribute.cs @@ -0,0 +1,23 @@ +using System; + +namespace Elsa.Attributes +{ + /// + /// An attribute that can associate a service or component with + /// a specific feature by its name. This component will only + /// be used if the feature is enabled. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class FeatureAttribute : Attribute + { + public FeatureAttribute(string featureName) + { + FeatureName = featureName; + } + + /// + /// The name of the feature to assign the component to. + /// + public string FeatureName { get; set; } + } +} diff --git a/src/core/Elsa.Abstractions/Extensions/AssemblyExtensions.cs b/src/core/Elsa.Abstractions/Extensions/AssemblyExtensions.cs index 51a209f66..9eff65df2 100644 --- a/src/core/Elsa.Abstractions/Extensions/AssemblyExtensions.cs +++ b/src/core/Elsa.Abstractions/Extensions/AssemblyExtensions.cs @@ -10,7 +10,9 @@ namespace Elsa { public static IEnumerable GetAllWithInterface(this Assembly assembly, Type @interface) => assembly.GetTypes().Where(t => t.IsClass && t.IsAbstract == false && t.GetInterfaces().Contains(@interface)); public static IEnumerable GetAllWithBaseClass(this Assembly assembly, Type baseClass) => assembly.GetTypes().Where(baseClass.IsAssignableFrom); + //public static IEnumerable GetAllWithAttribute(this Assembly assembly, Type attribute) => assembly.GetTypes().Where(t => t.GetCustomAttributes(typeof(attribute), true).Length > 0); public static IEnumerable GetAllWithInterface(this Assembly assembly) => assembly.GetAllWithInterface(typeof(TType)); public static IEnumerable GetAllWithBaseClass(this Assembly assembly) => assembly.GetAllWithBaseClass(typeof(TType)); + //public static IEnumerable GetAllWithAttribute(this Assembly assembly) => assembly.GetAllWithAttribute(typeof(TType)); } } diff --git a/src/core/Elsa.Core/ElsaOptionsBuilder.cs b/src/core/Elsa.Core/ElsaOptionsBuilder.cs index 8a53fc742..387f8da96 100644 --- a/src/core/Elsa.Core/ElsaOptionsBuilder.cs +++ b/src/core/Elsa.Core/ElsaOptionsBuilder.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using Elsa.Attributes; using Elsa.Builders; using Elsa.Caching; using Elsa.Persistence; @@ -56,6 +57,34 @@ namespace Elsa return this; } + public ElsaOptionsBuilder AddFeatures(IEnumerable assemblies, IEnumerable features) + { + foreach (Assembly assembly in assemblies) + { + foreach (Type type in assembly.GetTypes()) + { + var attributes = type.GetCustomAttributes(typeof(FeatureAttribute), true); + + foreach (Attribute attribute in attributes) + { + if (!features.Contains(((FeatureAttribute)attribute).FeatureName)) continue; + + MethodInfo methodInfo = type.GetMethod("ConfigureElsa"); + + if (methodInfo != null) + { + object[] parametersArray = new object[] { this }; + object classInstance = Activator.CreateInstance(type, null); + methodInfo.Invoke(classInstance, parametersArray); + } + + } + } + } + + return this; + } + public ElsaOptionsBuilder AddActivity() where T : IActivity => AddActivity(typeof(T)); public ElsaOptionsBuilder AddActivity(Type activityType) diff --git a/src/samples/server/Elsa.Samples.Server.Host/Elsa.Samples.Server.Host.csproj b/src/samples/server/Elsa.Samples.Server.Host/Elsa.Samples.Server.Host.csproj index a1da00d9c..5fbadf014 100644 --- a/src/samples/server/Elsa.Samples.Server.Host/Elsa.Samples.Server.Host.csproj +++ b/src/samples/server/Elsa.Samples.Server.Host/Elsa.Samples.Server.Host.csproj @@ -10,6 +10,7 @@ + diff --git a/src/samples/server/Elsa.Samples.Server.Host/Startup.cs b/src/samples/server/Elsa.Samples.Server.Host/Startup.cs index b469b0107..497bae87a 100644 --- a/src/samples/server/Elsa.Samples.Server.Host/Startup.cs +++ b/src/samples/server/Elsa.Samples.Server.Host/Startup.cs @@ -1,16 +1,21 @@ using Elsa.Activities.Conductor.Extensions; using Elsa.Activities.UserTask.Extensions; using Elsa.Activities.Webhooks.Extensions; +using Elsa.Attributes; using Elsa.Persistence.EntityFramework.Core.Extensions; using Elsa.Persistence.EntityFramework.Sqlite; using Elsa.Samples.Server.Host.Activities; -using Elsa.Webhooks.Persistence.EntityFramework.Core.Extensions; +//using Elsa.Webhooks.Persistence.EntityFramework.Core.Extensions; using Elsa.Webhooks.Persistence.EntityFramework.Sqlite; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace Elsa.Samples.Server.Host { @@ -34,6 +39,9 @@ namespace Elsa.Samples.Server.Host services.AddControllers(); + //List assemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies().Select((item) => Assembly.Load(item)).ToList(); + List assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList(); + services .AddActivityPropertyOptionsProvider() .AddRuntimeSelectItemsProvider() @@ -46,7 +54,9 @@ namespace Elsa.Samples.Server.Host //.UseRabbitMq(Configuration.GetConnectionString("RabbitMq")) //.UseRebusCacheSignal() //.UseRedisCacheSignal() + .AddConsoleActivities() + .AddHttpActivities(elsaSection.GetSection("Http").Bind) .AddEmailActivities(elsaSection.GetSection("Smtp").Bind) .AddQuartzTemporalActivities() @@ -63,7 +73,9 @@ namespace Elsa.Samples.Server.Host .AddConductorActivities(options => elsaSection.GetSection("Conductor").Bind(options)) .AddActivitiesFrom() .AddWorkflowsFrom() - .AddWebhooks(webhooks => webhooks.UseEntityFrameworkPersistence(ef => ef.UseWebhookSqlite())) + .AddFeatures(assemblies, elsaSection.GetSection("Features").Get>()) + + //.AddWebhooks(webhooks => webhooks.UseEntityFrameworkPersistence(ef => ef.UseWebhookSqlite())) ); // Elsa API endpoints. @@ -71,6 +83,8 @@ namespace Elsa.Samples.Server.Host .AddElsaApiEndpoints() .AddElsaSwagger(); + //AddFeatures(); + // Allow arbitrary client browser apps to access the API for demo purposes only. // In a production environment, make sure to allow only origins you trust. services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().WithExposedHeaders("Content-Disposition"))); @@ -91,5 +105,6 @@ namespace Elsa.Samples.Server.Host .UseRouting() .UseEndpoints(endpoints => { endpoints.MapControllers(); }); } + } } \ No newline at end of file diff --git a/src/samples/server/Elsa.Samples.Server.Host/appsettings.Development.json b/src/samples/server/Elsa.Samples.Server.Host/appsettings.Development.json index 99281afad..71e1f2132 100644 --- a/src/samples/server/Elsa.Samples.Server.Host/appsettings.Development.json +++ b/src/samples/server/Elsa.Samples.Server.Host/appsettings.Development.json @@ -6,5 +6,8 @@ "System": "Information", "Microsoft": "Warning" } + }, + "Elsa": { + "Features": [ "Webhook", "Console" ] } } diff --git a/test/shared/Elsa.Testing.Shared/Elsa.Testing.Shared.csproj b/test/shared/Elsa.Testing.Shared/Elsa.Testing.Shared.csproj index aefda0a44..b13ba0ff5 100644 --- a/test/shared/Elsa.Testing.Shared/Elsa.Testing.Shared.csproj +++ b/test/shared/Elsa.Testing.Shared/Elsa.Testing.Shared.csproj @@ -1,4 +1,4 @@ - +