Refactor persistence configuration (memory + EF core)

This commit is contained in:
Sipke Schoorstra 2022-05-03 12:32:31 +02:00
parent 1afe3a62a0
commit 5f83198677
12 changed files with 112 additions and 61 deletions

View file

@ -1,5 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Computables/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Configurer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=materializer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Materializers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pomelo/@EntryIndexedValue">True</s:Boolean>

View file

@ -19,11 +19,13 @@ public class ElsaOptionsConfigurator
public IServiceCollection Services { get; }
public T Configure<T>(Action<T>? configure = default) where T : class, IConfigurator, new()
public T Configure<T>(Action<T>? configure = default) where T : class, IConfigurator, new() => Configure<T>(() => new T(), configure);
public T Configure<T>(Func<T> factory, Action<T>? configure = default) where T : class, IConfigurator
{
if (_configurators.FirstOrDefault(x => x is T) is not T configurator)
{
configurator = new T();
configurator = factory();
_configurators.Add(configurator);
}

View file

@ -1,11 +1,13 @@
using Microsoft.Extensions.DependencyInjection;
using Elsa.Options;
using Elsa.Persistence.Options;
namespace Elsa.Persistence.Extensions;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddPersistenceServices(this IServiceCollection services)
public static ElsaOptionsConfigurator UsePersistence(this ElsaOptionsConfigurator configurator, Action<PersistenceOptions>? configure = default)
{
return services;
configurator.Configure(() => new PersistenceOptions(configurator), configure);
return configurator;
}
}

View file

@ -1,3 +1,4 @@
using Elsa.Options;
using Elsa.Persistence.Implementations;
using Elsa.Persistence.Services;
using Elsa.Services;
@ -7,6 +8,13 @@ namespace Elsa.Persistence.Options;
public class PersistenceOptions : IConfigurator
{
public ElsaOptionsConfigurator ElsaOptionsConfigurator { get; }
public PersistenceOptions(ElsaOptionsConfigurator elsaOptionsConfigurator)
{
ElsaOptionsConfigurator = elsaOptionsConfigurator;
}
public Func<IServiceProvider, IWorkflowDefinitionStore> WorkflowDefinitionStore { get; set; } = _ => new NullWorkflowDefinitionStore();
public Func<IServiceProvider, IWorkflowInstanceStore> WorkflowInstanceStore { get; set; } = _ => new NullWorkflowInstanceStore();
public Func<IServiceProvider, IWorkflowBookmarkStore> WorkflowBookmarkStore { get; set; } = _ => new NullWorkflowBookmarkStore();

View file

@ -3,6 +3,7 @@ using Elsa.Management.Extensions;
using Elsa.Mediator.Extensions;
using Elsa.Modules.Activities.Extensions;
using Elsa.Options;
using Elsa.Persistence.Extensions;
using Elsa.Persistence.InMemory.Extensions;
using Elsa.Runtime.Extensions;
using Microsoft.Extensions.DependencyInjection;
@ -18,8 +19,8 @@ public static class ServiceCollectionExtensions
return services
.AddElsaCore(elsa =>
{
elsa.UseInMemoryPersistence();
elsa.AddActivityServices();
elsa.UsePersistence(persistence => persistence.UseInMemoryProvider());
elsa.UseActivityServices();
elsa.AddElsaRuntime();
elsa.AddElsaManagement();
configure?.Invoke(elsa);

View file

@ -8,7 +8,7 @@ public static class ServiceCollectionExtensions
/// <summary>
/// Registers required services for activities provided by this package.
/// </summary>
public static ActivityOptions AddActivityServices(this ElsaOptionsConfigurator configurator)
public static ActivityOptions UseActivityServices(this ElsaOptionsConfigurator configurator)
{
return configurator.Configure<ActivityOptions>();
}

View file

@ -1,11 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Elsa.Persistence.EntityFrameworkCore.Options;
using Microsoft.EntityFrameworkCore;
namespace Elsa.Persistence.EntityFrameworkCore.Sqlite
{
public static class DbContextOptionsBuilderExtensions
{
public static DbContextOptionsBuilder UseSqlite(this DbContextOptionsBuilder builder, string connectionString = "Data Source=elsa.sqlite.db;Cache=Shared;") => builder.UseSqlite(connectionString, db => db
.MigrationsAssembly(typeof(SqliteElsaContextFactory).Assembly.GetName().Name)
.MigrationsHistoryTable(ElsaDbContext.MigrationsHistoryTable, ElsaDbContext.ElsaSchema));
public static EFCorePersistenceOptions UseSqlite(this EFCorePersistenceOptions builder, string connectionString = "Data Source=elsa.sqlite.db;Cache=Shared;")
{
builder.ConfigureDbContextOptions((_, x) => x.UseSqlite(connectionString, db => db
.MigrationsAssembly(typeof(SqliteElsaContextFactory).Assembly.GetName().Name)
.MigrationsHistoryTable(ElsaDbContext.MigrationsHistoryTable, ElsaDbContext.ElsaSchema)));
return builder;
}
}
}

View file

@ -1,57 +1,20 @@
using Elsa.Persistence.Entities;
using Elsa.Persistence.EntityFrameworkCore.Handlers;
using Elsa.Persistence.EntityFrameworkCore.HostedServices;
using Elsa.Persistence.EntityFrameworkCore.Implementations;
using Elsa.Persistence.EntityFrameworkCore.Services;
using Elsa.Persistence.Services;
using Microsoft.EntityFrameworkCore;
using Elsa.Persistence.EntityFrameworkCore.Options;
using Elsa.Persistence.Options;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Persistence.EntityFrameworkCore.Extensions;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddEntityFrameworkCorePersistence(
this IServiceCollection services,
Action<IServiceProvider, DbContextOptionsBuilder> configure,
bool useContextPooling = true)
public static PersistenceOptions UseEntityFrameworkCoreProvider(this PersistenceOptions configurator, Action<EFCorePersistenceOptions> configure)
{
services.AddElsaDbContextFactory(configure, useContextPooling);
services
.AddSingleton<IStore<WorkflowDefinition>, EFCoreStore<WorkflowDefinition>>()
.AddSingleton<IStore<WorkflowInstance>, EFCoreStore<WorkflowInstance>>()
.AddSingleton<IStore<WorkflowBookmark>, EFCoreStore<WorkflowBookmark>>()
.AddSingleton<IStore<WorkflowTrigger>, EFCoreStore<WorkflowTrigger>>()
.AddSingleton<IStore<WorkflowExecutionLogRecord>, EFCoreStore<WorkflowExecutionLogRecord>>()
.AddSingleton<IWorkflowInstanceStore, EFCoreWorkflowInstanceStore>()
.AddSingleton<IWorkflowDefinitionStore, EFCoreWorkflowDefinitionStore>()
.AddSingleton<IWorkflowTriggerStore, EFCoreWorkflowTriggerStore>()
.AddSingleton<IWorkflowBookmarkStore, EFCoreWorkflowBookmarkStore>()
.AddSingleton<IWorkflowExecutionLogStore, EFCoreWorkflowExecutionLogStore>()
.AddSingleton<IEntitySerializer<WorkflowDefinition>, WorkflowDefinitionSerializer>()
.AddSingleton<IEntitySerializer<WorkflowInstance>, WorkflowInstanceSerializer>()
.AddSingleton<IEntitySerializer<WorkflowExecutionLogRecord>, WorkflowExecutionLogRecordSerializer>()
;
return services;
configurator.ElsaOptionsConfigurator.Configure(() => new EFCorePersistenceOptions(configurator), configure);
return configurator;
}
public static IServiceCollection AutoRunMigrations(this IServiceCollection services)
{
return services.AddHostedService<RunMigrations>();
}
private static IServiceCollection AddElsaDbContextFactory(this IServiceCollection services,
Action<IServiceProvider, DbContextOptionsBuilder> configure,
bool useContextPooling,
ServiceLifetime serviceLifetime = ServiceLifetime.Singleton)
{
if (useContextPooling)
services.AddPooledDbContextFactory<ElsaDbContext>(configure);
else
services.AddDbContextFactory<ElsaDbContext>(configure, serviceLifetime);
return services;
}
}

View file

@ -0,0 +1,67 @@
using Elsa.Persistence.Entities;
using Elsa.Persistence.EntityFrameworkCore.Handlers;
using Elsa.Persistence.EntityFrameworkCore.Implementations;
using Elsa.Persistence.EntityFrameworkCore.Services;
using Elsa.Persistence.Options;
using Elsa.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Persistence.EntityFrameworkCore.Options;
public class EFCorePersistenceOptions : IConfigurator
{
public EFCorePersistenceOptions(PersistenceOptions persistenceOptions)
{
PersistenceOptions = persistenceOptions;
persistenceOptions
.WithWorkflowDefinitionStore(sp => sp.GetRequiredService<EFCoreWorkflowDefinitionStore>())
.WithWorkflowInstanceStore(sp => sp.GetRequiredService<EFCoreWorkflowInstanceStore>())
.WithWorkflowBookmarkStore(sp => sp.GetRequiredService<EFCoreWorkflowBookmarkStore>())
.WithWorkflowTriggerStore(sp => sp.GetRequiredService<EFCoreWorkflowTriggerStore>())
.WithWorkflowExecutionLogStore(sp => sp.GetRequiredService<EFCoreWorkflowExecutionLogStore>())
;
}
public PersistenceOptions PersistenceOptions { get; }
public bool ContextPoolingIsEnabled { get; set; }
public ServiceLifetime DbContextFactoryLifetime { get; set; } = ServiceLifetime.Singleton;
public Action<IServiceProvider, DbContextOptionsBuilder> DbContextOptionsBuilderAction = (_, _) => { };
public EFCorePersistenceOptions WithContextPooling(bool enabled = true)
{
ContextPoolingIsEnabled = enabled;
return this;
}
public EFCorePersistenceOptions ConfigureDbContextOptions(Action<IServiceProvider, DbContextOptionsBuilder> configure)
{
DbContextOptionsBuilderAction = configure;
return this;
}
public void ConfigureServices(IServiceCollection services)
{
if (ContextPoolingIsEnabled)
services.AddPooledDbContextFactory<ElsaDbContext>(DbContextOptionsBuilderAction);
else
services.AddDbContextFactory<ElsaDbContext>(DbContextOptionsBuilderAction, DbContextFactoryLifetime);
services
.AddSingleton<IStore<WorkflowDefinition>, EFCoreStore<WorkflowDefinition>>()
.AddSingleton<IStore<WorkflowInstance>, EFCoreStore<WorkflowInstance>>()
.AddSingleton<IStore<WorkflowBookmark>, EFCoreStore<WorkflowBookmark>>()
.AddSingleton<IStore<WorkflowTrigger>, EFCoreStore<WorkflowTrigger>>()
.AddSingleton<IStore<WorkflowExecutionLogRecord>, EFCoreStore<WorkflowExecutionLogRecord>>()
.AddSingleton<EFCoreWorkflowInstanceStore>()
.AddSingleton<EFCoreWorkflowDefinitionStore>()
.AddSingleton<EFCoreWorkflowTriggerStore>()
.AddSingleton<EFCoreWorkflowBookmarkStore>()
.AddSingleton<EFCoreWorkflowExecutionLogStore>()
.AddSingleton<IEntitySerializer<WorkflowDefinition>, WorkflowDefinitionSerializer>()
.AddSingleton<IEntitySerializer<WorkflowInstance>, WorkflowInstanceSerializer>()
.AddSingleton<IEntitySerializer<WorkflowExecutionLogRecord>, WorkflowExecutionLogRecordSerializer>()
;
}
}

View file

@ -10,9 +10,9 @@ namespace Elsa.Persistence.InMemory.Extensions;
public static class ServiceCollectionExtensions
{
public static PersistenceOptions UseInMemoryPersistence(this ElsaOptionsConfigurator configurator)
public static PersistenceOptions UseInMemoryProvider(this PersistenceOptions configurator)
{
var services = configurator.Services;
var services = configurator.ElsaOptionsConfigurator.Services;
services
.AddSingleton<InMemoryStore<WorkflowDefinition>>()
@ -27,12 +27,12 @@ public static class ServiceCollectionExtensions
.AddSingleton<InMemoryWorkflowExecutionLogStore>()
;
return configurator.Configure<PersistenceOptions>()
return configurator.ElsaOptionsConfigurator.Configure(() => new PersistenceOptions(configurator.ElsaOptionsConfigurator), o => o
.WithWorkflowDefinitionStore(sp => sp.GetRequiredService<InMemoryWorkflowDefinitionStore>())
.WithWorkflowInstanceStore(sp => sp.GetRequiredService<InMemoryWorkflowInstanceStore>())
.WithWorkflowBookmarkStore(sp => sp.GetRequiredService<InMemoryWorkflowBookmarkStore>())
.WithWorkflowTriggerStore(sp => sp.GetRequiredService<InMemoryWorkflowTriggerStore>())
.WithWorkflowExecutionLogStore(sp => sp.GetRequiredService<InMemoryWorkflowExecutionLogStore>())
.WithWorkflowExecutionLogStore(sp => sp.GetRequiredService<InMemoryWorkflowExecutionLogStore>()))
;
}
}

View file

@ -16,6 +16,7 @@ using Elsa.Modules.Scheduling.Extensions;
using Elsa.Modules.WorkflowContexts.Extensions;
using Elsa.Persistence.EntityFrameworkCore.Extensions;
using Elsa.Persistence.EntityFrameworkCore.Sqlite;
using Elsa.Persistence.Extensions;
using Elsa.Pipelines.WorkflowExecution.Components;
using Elsa.Runtime.Extensions;
using Elsa.Samples.Web1.Activities;
@ -40,9 +41,8 @@ var sqlServerConnectionString = configuration.GetConnectionString("SqlServer");
// Add services.
services
.AutoRunMigrations()
.AddElsa()
.AddElsa(elsa => elsa.UsePersistence(p => p.UseEntityFrameworkCoreProvider(ef => ef.UseSqlite())))
//.AddProtoActorWorkflowHost()
.AddEntityFrameworkCorePersistence((_, ef) => ef.UseSqlite())
.AddJobServices(new QuartzJobSchedulerProvider(), new HangfireJobQueueProvider())
.AddSchedulingServices()
.AddHttpActivityServices()

View file

@ -94,7 +94,8 @@ class Program
var services = new ServiceCollection();
services
.AddElsa(elsa => elsa.Configure<ActivityOptions>().WithStandardOutStreamProvider(sp => new CustomOutStreamProvider(Console.Out)))
.AddElsa(elsa => elsa.Configure<ActivityOptions>()
.WithStandardOutStreamProvider(sp => new CustomOutStreamProvider(Console.Out)))
.AddLogging(logging => logging.AddConsole().SetMinimumLevel(LogLevel.Warning));
return services.BuildServiceProvider();