Ad XML comments

This commit is contained in:
Sipke Schoorstra 2023-05-28 16:40:10 +02:00
parent c99280b792
commit 8d8a8679f7
9 changed files with 92 additions and 11 deletions

View file

@ -1,4 +1,5 @@
using Elsa.EntityFrameworkCore.Extensions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
@ -7,17 +8,18 @@ namespace Elsa.EntityFrameworkCore.Common;
/// <summary>
/// An optional base class to implement with some opinions on certain converters to install for certain DB providers.
/// </summary>
[PublicAPI]
public abstract class ElsaDbContextBase : DbContext
{
/// <summary>
/// The schema used by Elsa.
/// </summary>
public static string ElsaSchema = "Elsa";
public static string ElsaSchema { get; set; } = "Elsa";
/// <summary>
/// The table used to store the migrations history.
/// </summary>
public static string MigrationsHistoryTable = "__EFMigrationsHistory";
public static string MigrationsHistoryTable { get; set; } = "__EFMigrationsHistory";
/// <summary>
/// Initializes a new instance of the <see cref="ElsaDbContextBase"/> class.
@ -25,6 +27,8 @@ public abstract class ElsaDbContextBase : DbContext
protected ElsaDbContextBase(DbContextOptions options) : base(options)
{
var elsaDbContextOptions = options.FindExtension<ElsaDbContextOptionsExtension>()?.Options;
// ReSharper disable once VirtualMemberCallInConstructor
Schema = !string.IsNullOrWhiteSpace(elsaDbContextOptions?.SchemaName) ? elsaDbContextOptions.SchemaName : ElsaSchema;
}

View file

@ -1,8 +1,25 @@
using JetBrains.Annotations;
namespace Elsa.EntityFrameworkCore.Common;
/// <summary>
/// Provides options for configuring Elsa's Entity Framework Core integration.
/// </summary>
[PublicAPI]
public class ElsaDbContextOptions
{
/// <summary>
/// The schema used by Elsa.
/// </summary>
public string? SchemaName { get; set; }
/// <summary>
/// The table used to store the migrations history.
/// </summary>
public string? MigrationsHistoryTableName { get; set; }
/// <summary>
/// The assembly name containing the migrations.
/// </summary>
public string? MigrationsAssemblyName { get; set; }
}

View file

@ -3,15 +3,19 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
namespace Elsa.EntityFrameworkCore.Common;
/// <summary>
/// Provides options for configuring Elsa's Entity Framework Core integration.
/// </summary>
public static class ElsaDbContextOptionsExtensions
{
public static DbContextOptionsBuilder UseElsaDbContextOptions(
this DbContextOptionsBuilder optionsBuilder,
ElsaDbContextOptions? options)
/// <summary>
/// Installs a custom extension for Elsa's Entity Framework Core integration.
/// </summary>
/// <param name="optionsBuilder">The options builder to install the extension on.</param>
/// <param name="options">The options to install.</param>
public static DbContextOptionsBuilder UseElsaDbContextOptions(this DbContextOptionsBuilder optionsBuilder, ElsaDbContextOptions? options)
{
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(
new ElsaDbContextOptionsExtension(options));
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(new ElsaDbContextOptionsExtension(options));
return optionsBuilder;
}
}

View file

@ -6,27 +6,48 @@ using Microsoft.Extensions.DependencyInjection;
namespace Elsa.EntityFrameworkCore.Common;
/// <summary>
/// Base class for features that require Entity Framework Core.
/// </summary>
/// <typeparam name="TDbContext">The type of the database context.</typeparam>
public abstract class PersistenceFeatureBase<TDbContext> : FeatureBase where TDbContext : DbContext
{
/// <inheritdoc />
protected PersistenceFeatureBase(IModule module) : base(module)
{
}
/// <summary>
/// Gets or sets a value indicating whether to use context pooling.
/// </summary>
public bool UseContextPooling { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to run migrations.
/// </summary>
public bool RunMigrations { get; set; } = true;
/// <summary>
/// Gets or sets the lifetime of the <see cref="IDbContextFactory{TContext}"/>. Defaults to <see cref="ServiceLifetime.Singleton"/>.
/// </summary>
public ServiceLifetime DbContextFactoryLifetime { get; set; } = ServiceLifetime.Singleton;
/// <summary>
/// Gets or sets the callback used to configure the <see cref="DbContextOptionsBuilder"/>.
/// </summary>
public Action<IServiceProvider, DbContextOptionsBuilder> DbContextOptionsBuilder = (_, options) => options
.UseSqlite("Data Source=elsa.sqlite.db;Cache=Shared;", sqlite => sqlite
.MigrationsAssembly("Elsa.EntityFrameworkCore.Sqlite")
.MigrationsHistoryTable(ElsaDbContextBase.MigrationsHistoryTable, ElsaDbContextBase.ElsaSchema));
/// <inheritdoc />
public override void ConfigureHostedServices()
{
if (RunMigrations)
Module.ConfigureHostedService<RunMigrationsHostedService<TDbContext>>(-100); // Migrations need to run before other hosted services that depend on DB access.
}
/// <inheritdoc />
public override void Apply()
{
if (UseContextPooling)
@ -35,6 +56,11 @@ public abstract class PersistenceFeatureBase<TDbContext> : FeatureBase where TDb
Services.AddDbContextFactory<TDbContext>(DbContextOptionsBuilder, DbContextFactoryLifetime);
}
/// <summary>
/// Adds a store to the service collection.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TStore">The type of the store.</typeparam>
protected void AddStore<TEntity, TStore>() where TEntity : class where TStore : class
{
Services
@ -43,6 +69,11 @@ public abstract class PersistenceFeatureBase<TDbContext> : FeatureBase where TDb
;
}
/// <summary>
/// Adds an entity store to the service collection.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TStore">The type of the store.</typeparam>
protected void AddEntityStore<TEntity, TStore>() where TEntity : Entity where TStore : class
{
Services

View file

@ -9,8 +9,13 @@ namespace Elsa.EntityFrameworkCore.Common;
public class RunMigrationsHostedService<TDbContext> : IHostedService where TDbContext : DbContext
{
private readonly IDbContextFactory<TDbContext> _dbContextFactory;
/// <summary>
/// Initializes a new instance of the <see cref="RunMigrationsHostedService{TDbContext}"/> class.
/// </summary>
public RunMigrationsHostedService(IDbContextFactory<TDbContext> dbContextFactoryFactory) => _dbContextFactory = dbContextFactoryFactory;
/// <inheritdoc />
public async Task StartAsync(CancellationToken cancellationToken)
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
@ -18,5 +23,6 @@ public class RunMigrationsHostedService<TDbContext> : IHostedService where TDbCo
await dbContext.DisposeAsync();
}
/// <inheritdoc />
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

View file

@ -3,8 +3,18 @@ using Elsa.Extensions;
namespace Elsa.EntityFrameworkCore.Extensions;
/// <summary>
/// Provides extension methods for <see cref="Expression"/> objects.
/// </summary>
public static class ExpressionExtensions
{
/// <summary>
/// Builds an expression that checks if the specified property is contained in the specified list of entities.
/// </summary>
/// <param name="keySelector">The key selector.</param>
/// <param name="entities">The entities.</param>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <returns>The expression.</returns>
public static Expression<Func<TEntity, bool>> BuildContainsExpression<TEntity>(this Expression<Func<TEntity, string>> keySelector, IEnumerable<TEntity> entities) where TEntity : class
{
var compiledKeySelector = keySelector.Compile();
@ -21,7 +31,14 @@ public static class ExpressionExtensions
return Expression.Lambda<Func<TEntity, bool>>(body, param);
}
public static Expression<Func<TEntity, bool>> BuildEqualsExpresion<TEntity>(this Expression<Func<TEntity, string>> keySelector, TEntity entity)
/// <summary>
/// Builds an expression that checks if the specified property is equal to the specified entity's property.
/// </summary>
/// <param name="keySelector">The key selector.</param>
/// <param name="entity">The entity.</param>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <returns>The expression.</returns>
public static Expression<Func<TEntity, bool>> BuildEqualsExpression<TEntity>(this Expression<Func<TEntity, string>> keySelector, TEntity entity)
{
var keyName = keySelector.GetProperty()!.Name;

View file

@ -1,5 +1,6 @@
using System.Linq.Expressions;
using Elsa.Common.Models;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
namespace Elsa.EntityFrameworkCore.Extensions;
@ -7,6 +8,7 @@ namespace Elsa.EntityFrameworkCore.Extensions;
/// <summary>
/// Provides extensions to <see cref="IQueryable{T}"/>.
/// </summary>
[PublicAPI]
public static class QueryableExtensions
{
/// <summary>

View file

@ -18,7 +18,7 @@ services.AddElsa(elsa => elsa
management.UseEntityFrameworkCore();
management.UseDslIntegration(dsl =>
{
dsl.MapActivityFunction("println", nameof(WriteLine), new[] { nameof(WriteLine.Text) });
dsl.MapActivityFunction("print", nameof(WriteLine), new[] { nameof(WriteLine.Text) });
dsl.MapActivityFunction("http_listen", nameof(HttpEndpoint), new[] { nameof(HttpEndpoint.Path), nameof(HttpEndpoint.SupportedMethods) }, activity => activity.SetCanStartWorkflow(true));
dsl.MapActivityFunction("http_write", nameof(WriteHttpResponse), new[] { nameof(WriteHttpResponse.StatusCode), nameof(WriteHttpResponse.Content) });
});

View file

@ -2,7 +2,7 @@
http_listen("hello-world-functional-dsl", ["GET"]);
// Print a message to the console.
println("Hello functional DSL World!");
print("Hello functional DSL World!");
// Write an HTTP response.
http_write("200", "Hello functional DSL World!");