Merge branch 'develop' into feature/elsa-2.0
# Conflicts: # src/persistence/Elsa.Persistence.YesSql/StartupTasks/InitializeStoreTask.cs
This commit is contained in:
commit
452516bdfa
|
|
@ -1,70 +1,70 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Elsa.Serialization.Handlers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Serialization.JsonNet;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Converters
|
||||
{
|
||||
public class TypeNameHandlingConverter : JsonConverter
|
||||
{
|
||||
private const string TypeFieldName = "TypeName";
|
||||
private static readonly IDictionary<Type, IValueHandler> ValueHandlers = new Dictionary<Type, IValueHandler>();
|
||||
|
||||
public static void RegisterTypeHandler<T>() where T : IValueHandler
|
||||
{
|
||||
var handler = Activator.CreateInstance<T>();
|
||||
RegisterTypeHandler(handler);
|
||||
}
|
||||
|
||||
public static void RegisterTypeHandler(IValueHandler handler)
|
||||
{
|
||||
ValueHandlers[handler.GetType()] = handler;
|
||||
}
|
||||
|
||||
static TypeNameHandlingConverter()
|
||||
{
|
||||
RegisterTypeHandler<DateTimeHandler>();
|
||||
RegisterTypeHandler<InstantHandler>();
|
||||
RegisterTypeHandler<AnnualDateHandler>();
|
||||
RegisterTypeHandler<DurationHandler>();
|
||||
RegisterTypeHandler<LocalDateHandler>();
|
||||
RegisterTypeHandler<LocalDateTimeHandler>();
|
||||
RegisterTypeHandler<LocalTimeHandler>();
|
||||
RegisterTypeHandler<OffsetDateHandler>();
|
||||
RegisterTypeHandler<OffsetHandler>();
|
||||
RegisterTypeHandler<OffsetTimeHandler>();
|
||||
RegisterTypeHandler<YearMonthHandler>();
|
||||
RegisterTypeHandler<ZonedDateTimeHandler>();
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var valueType = value.GetType();
|
||||
var token = JToken.FromObject(value);
|
||||
var handler = GetHandler(x => x.CanSerialize(token, valueType));
|
||||
|
||||
switch (token.Type)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
token[TypeFieldName] = GetAssemblyQualifiedTypeName(value.GetType());
|
||||
token.WriteTo(writer, serializer.Converters.ToArray());
|
||||
break;
|
||||
case JTokenType.Date: // Taking over DateTime serialization because NodaTime disabled date handling.
|
||||
var dateToken = new JObject();
|
||||
dateToken[TypeFieldName] = "DateTime";
|
||||
dateToken["Value"] = token;
|
||||
dateToken.WriteTo(writer, serializer.Converters.ToArray());
|
||||
break;
|
||||
default:
|
||||
token.WriteTo(writer);
|
||||
break;
|
||||
}
|
||||
handler.Serialize(writer, serializer, token);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
var token = JToken.ReadFrom(reader);
|
||||
var handler = GetHandler(x => x.CanDeserialize(token, objectType));
|
||||
|
||||
switch (token.Type)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
var typeName = token[TypeFieldName].Value<string>();
|
||||
|
||||
if (typeName == "DateTime")
|
||||
{
|
||||
var dateTime = token["Value"].ToObject<DateTime>();
|
||||
return dateTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
var type = Type.GetType(typeName);
|
||||
return token.ToObject(type, serializer);
|
||||
}
|
||||
default:
|
||||
return token.ToObject(objectType);
|
||||
}
|
||||
return handler.Deserialize(reader, serializer, objectType, token);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType) => true;
|
||||
|
||||
private string GetAssemblyQualifiedTypeName(Type type)
|
||||
{
|
||||
var typeName = type.FullName;
|
||||
var assemblyName = type.Assembly.GetName().Name;
|
||||
|
||||
return $"{typeName}, {assemblyName}";
|
||||
}
|
||||
private IValueHandler GetHandler(Func<IValueHandler, bool> predicate) =>
|
||||
ValueHandlers.Values.OrderByDescending(x => x.Priority).FirstOrDefault(predicate) ?? new DefaultValueHandler();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class AnnualDateHandler : PrimitiveValueHandler<AnnualDate>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => AnnualDatePattern.Iso.Parse(value.ToString()).Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class DateTimeHandler : PrimitiveValueHandler<DateTime>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => value.Value<DateTime>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public class DefaultValueHandler : IValueHandler
|
||||
{
|
||||
public int Priority => -9000;
|
||||
public bool CanSerialize(JToken value, Type type) => true;
|
||||
public bool CanDeserialize(JToken value, Type type) => true;
|
||||
public object Deserialize(JsonReader reader, JsonSerializer serializer, Type type, JToken value) => serializer.Deserialize(value.CreateReader(), type);
|
||||
public void Serialize(JsonWriter writer, JsonSerializer serializer, JToken value) => serializer.Serialize(writer, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class DurationHandler : PrimitiveValueHandler<Duration>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => DurationPattern.Roundtrip.Parse(value.ToString()).Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public interface IValueHandler
|
||||
{
|
||||
int Priority { get; }
|
||||
bool CanSerialize(JToken value, Type type);
|
||||
bool CanDeserialize(JToken value, Type type);
|
||||
object Deserialize(JsonReader reader, JsonSerializer serializer, Type type, JToken value);
|
||||
void Serialize(JsonWriter writer, JsonSerializer serializer, JToken value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class InstantHandler : PrimitiveValueHandler<Instant>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => InstantPattern.ExtendedIso.Parse(value.ToString()).Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class LocalDateHandler : PrimitiveValueHandler<LocalDate>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => LocalDatePattern.Iso.Parse(value.ToString()).Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class LocalDateTimeHandler : PrimitiveValueHandler<LocalDateTime>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => LocalDateTimePattern.ExtendedIso.Parse(value.ToString()).Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class LocalTimeHandler : PrimitiveValueHandler<LocalTime>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => LocalTimePattern.LongExtendedIso.Parse(value.ToString()).Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public class ObjectHandler : IValueHandler
|
||||
{
|
||||
private const string TypeFieldName = "TypeName";
|
||||
public int Priority => -8999;
|
||||
public bool CanSerialize(JToken value, Type type) => value.Type == JTokenType.Object;
|
||||
public bool CanDeserialize(JToken value, Type type) => value.Type == JTokenType.Object;
|
||||
|
||||
public object Deserialize(JsonReader reader, JsonSerializer serializer, Type type, JToken value)
|
||||
{
|
||||
var typeName = value[TypeFieldName].Value<string>();
|
||||
var objectType = Type.GetType(typeName);
|
||||
return value.ToObject(objectType, serializer);
|
||||
}
|
||||
|
||||
public void Serialize(JsonWriter writer, JsonSerializer serializer, JToken value)
|
||||
{
|
||||
value[TypeFieldName] = GetAssemblyQualifiedTypeName(value.GetType());
|
||||
value.WriteTo(writer, serializer.Converters.ToArray());
|
||||
}
|
||||
|
||||
private string GetAssemblyQualifiedTypeName(Type type)
|
||||
{
|
||||
var typeName = type.FullName;
|
||||
var assemblyName = type.Assembly.GetName().Name;
|
||||
|
||||
return $"{typeName}, {assemblyName}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class OffsetDateHandler : PrimitiveValueHandler<OffsetDate>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => OffsetDatePattern.GeneralIso.Parse(value.ToString()).Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class OffsetHandler : PrimitiveValueHandler<Offset>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => OffsetPattern.GeneralInvariant.Parse(value.ToString()).Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class OffsetTimeHandler : PrimitiveValueHandler<OffsetTime>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => OffsetTimePattern.GeneralIso.Parse(value.ToString()).Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public abstract class PrimitiveValueHandler<T> : IValueHandler
|
||||
{
|
||||
public virtual int Priority => 0;
|
||||
public bool CanSerialize(JToken value, Type type) => type == typeof(T);
|
||||
public bool CanDeserialize(JToken value, Type type) => value.Type == JTokenType.Object && value["Type"]?.Value<string>() == TypeName;
|
||||
protected virtual string TypeName => typeof(T).Name;
|
||||
|
||||
public virtual object Deserialize(JsonReader reader, JsonSerializer serializer, Type type, JToken value)
|
||||
{
|
||||
var valueToken = value["Value"];
|
||||
return ParseValue(valueToken);
|
||||
}
|
||||
|
||||
public virtual void Serialize(JsonWriter writer, JsonSerializer serializer, JToken value)
|
||||
{
|
||||
var token = new JObject
|
||||
{
|
||||
["Type"] = TypeName,
|
||||
["Value"] = value
|
||||
};
|
||||
token.WriteTo(writer, serializer.Converters.ToArray());
|
||||
}
|
||||
|
||||
protected abstract object ParseValue(JToken value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class YearMonthHandler : PrimitiveValueHandler<YearMonth>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => YearMonthPattern.Iso.Parse(value.ToString()).Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace Elsa.Serialization.Handlers
|
||||
{
|
||||
public sealed class ZonedDateTimeHandler : PrimitiveValueHandler<ZonedDateTime>
|
||||
{
|
||||
protected override object ParseValue(JToken value) => ZonedDateTimePattern.ExtendedFormatOnlyIso.Parse(value.ToString()).Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
|
||||
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.Extensions
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.CustomSchema
|
||||
{
|
||||
public static class CustomSchemaDbContextBuilderExtensions
|
||||
{
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
using Elsa.Persistence.EntityFrameworkCore.DbContexts;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.DbContexts
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.CustomSchema
|
||||
{
|
||||
public class CustomSchemaModelCacheKeyFactory : IModelCacheKeyFactory
|
||||
{
|
||||
public CustomSchemaModelCacheKeyFactory() { }
|
||||
public CustomSchemaModelCacheKeyFactory()
|
||||
{
|
||||
}
|
||||
|
||||
public object Create(DbContext context)
|
||||
{
|
||||
string schema = null;
|
||||
|
|
@ -18,6 +21,7 @@ namespace Elsa.Persistence.EntityFrameworkCore.DbContexts
|
|||
schema = dbContextCustomSchema.Schema;
|
||||
}
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
Type = context.GetType(),
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.CustomSchema
|
||||
{
|
||||
public class CustomSchemaOptionsExtension : IDbContextOptionsExtension
|
||||
{
|
||||
public DbContextOptionsExtensionInfo Info => new CustomSchemaOptionsExtensionInfo(this);
|
||||
|
||||
public IDbContextCustomSchema ContextCustomSchema { get; protected set; }
|
||||
|
||||
public CustomSchemaOptionsExtension(IDbContextCustomSchema customSchema) : base()
|
||||
{
|
||||
ContextCustomSchema = customSchema;
|
||||
}
|
||||
|
||||
protected CustomSchemaOptionsExtension(CustomSchemaOptionsExtension copyFrom)
|
||||
{
|
||||
copyFrom.ContextCustomSchema = ContextCustomSchema;
|
||||
}
|
||||
|
||||
public void ApplyServices(IServiceCollection services)
|
||||
{
|
||||
}
|
||||
|
||||
public void Validate(IDbContextOptions options)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.CustomSchema
|
||||
{
|
||||
public sealed class CustomSchemaOptionsExtensionInfo : DbContextOptionsExtensionInfo
|
||||
{
|
||||
private string logFragment;
|
||||
|
||||
public CustomSchemaOptionsExtensionInfo(IDbContextOptionsExtension dbContextOptionsExtension) : base(dbContextOptionsExtension)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The extension for which this instance contains metadata.
|
||||
/// </summary>
|
||||
private new CustomSchemaOptionsExtension Extension => (CustomSchemaOptionsExtension)base.Extension;
|
||||
|
||||
public override bool IsDatabaseProvider => false;
|
||||
|
||||
public override string LogFragment
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(LogFragment))
|
||||
return logFragment;
|
||||
|
||||
if (Extension.ContextCustomSchema != null && Extension.ContextCustomSchema.UseCustomSchema)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.Append($"Use Custom Schema: {Extension.ContextCustomSchema.UseCustomSchema}");
|
||||
builder.Append($"Custom Schema: {Extension.ContextCustomSchema.Schema}");
|
||||
builder.Append($"Migrations History Table Name: {Extension.ContextCustomSchema.MigrationsHistoryTableName}");
|
||||
|
||||
logFragment = builder.ToString();
|
||||
}
|
||||
|
||||
return logFragment;
|
||||
}
|
||||
}
|
||||
|
||||
public override long GetServiceProviderHashCode() => 0;
|
||||
|
||||
public override void PopulateDebugInfo([NotNull] IDictionary<string, string> debugInfo)
|
||||
{
|
||||
debugInfo["CustomSchemaExtensionInfo"] = true.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.DbContexts
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.CustomSchema
|
||||
{
|
||||
public class DbContextCustomSchema : IDbContextCustomSchema
|
||||
{
|
||||
|
|
@ -1,8 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.DbContexts
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.CustomSchema
|
||||
{
|
||||
public interface IDbContextCustomSchema
|
||||
{
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using System;
|
||||
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.DbContexts
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.CustomSchema
|
||||
{
|
||||
public class SchemaEntityTypeConfiguration<TEntity> : IEntityTypeConfiguration<TEntity>
|
||||
where TEntity : class
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
|
||||
namespace Elsa.Persistence.EntityFrameworkCore.DbContexts
|
||||
{
|
||||
public class CustomSchemaOptionsExtension : IDbContextOptionsExtension
|
||||
{
|
||||
public DbContextOptionsExtensionInfo Info => new CustomSchemaExtensionInfo(this);
|
||||
|
||||
public IDbContextCustomSchema ContextCustomSchema { get; protected set; }
|
||||
public CustomSchemaOptionsExtension(IDbContextCustomSchema customSchema) : base()
|
||||
{
|
||||
ContextCustomSchema = customSchema;
|
||||
}
|
||||
protected CustomSchemaOptionsExtension(CustomSchemaOptionsExtension copyFrom)
|
||||
{
|
||||
copyFrom.ContextCustomSchema = ContextCustomSchema;
|
||||
}
|
||||
|
||||
public void ApplyServices(IServiceCollection services)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Validate(IDbContextOptions options)
|
||||
{
|
||||
}
|
||||
|
||||
protected CustomSchemaOptionsExtension Clone()
|
||||
{
|
||||
return new CustomSchemaOptionsExtension(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomSchemaExtensionInfo : DbContextOptionsExtensionInfo
|
||||
{
|
||||
string logFragment = null;
|
||||
public CustomSchemaExtensionInfo(IDbContextOptionsExtension dbContextOptionsExtension) : base(dbContextOptionsExtension)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The extension for which this instance contains metadata.
|
||||
/// </summary>
|
||||
public new virtual CustomSchemaOptionsExtension Extension
|
||||
=> (CustomSchemaOptionsExtension)base.Extension;
|
||||
|
||||
public override bool IsDatabaseProvider => false;
|
||||
|
||||
public override string LogFragment
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(LogFragment)) return logFragment;
|
||||
|
||||
if(Extension.ContextCustomSchema != null && Extension.ContextCustomSchema.UseCustomSchema)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.Append($"Use Custom Schema: {Extension.ContextCustomSchema.UseCustomSchema}");
|
||||
builder.Append($"Custom Schema: {Extension.ContextCustomSchema.Schema}");
|
||||
builder.Append($"Migrations History Table Name: {Extension.ContextCustomSchema.MigrationsHistoryTableName}");
|
||||
|
||||
logFragment = builder.ToString();
|
||||
}
|
||||
|
||||
return logFragment;
|
||||
}
|
||||
}
|
||||
|
||||
public override long GetServiceProviderHashCode()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override void PopulateDebugInfo([NotNullAttribute] IDictionary<string, string> debugInfo)
|
||||
{
|
||||
debugInfo["CustomSchemaExtensionInfo"] = true.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using Elsa.Models;
|
||||
using Elsa.Persistence.EntityFrameworkCore.CustomSchema;
|
||||
using Elsa.Persistence.EntityFrameworkCore.Entities;
|
||||
using Elsa.Persistence.EntityFrameworkCore.Extensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
|
|
@ -15,17 +14,18 @@ namespace Elsa.Persistence.EntityFrameworkCore.DbContexts
|
|||
public class ElsaContext : DbContext
|
||||
{
|
||||
private readonly JsonSerializerSettings serializerSettings;
|
||||
/// <summary>
|
||||
/// The CustomSchemaModelCacheKeyFactory will not resolve services from the DI container for constructor injection
|
||||
/// so this is necessary in order to set the custom schema for the Model Cache.
|
||||
/// </summary>
|
||||
internal IDbContextCustomSchema DbContextCustomSchema { get; set; }
|
||||
|
||||
public ElsaContext(DbContextOptions<ElsaContext> options) : base(options)
|
||||
{
|
||||
serializerSettings = new JsonSerializerSettings().ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
|
||||
DbContextCustomSchema = options.GetDbContextCustomSchema();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The CustomSchemaModelCacheKeyFactory will not resolve services from the DI container for constructor injection
|
||||
/// so this is necessary in order to set the custom schema for the Model Cache.
|
||||
/// </summary>
|
||||
internal IDbContextCustomSchema DbContextCustomSchema { get; }
|
||||
|
||||
public DbSet<WorkflowDefinitionVersionEntity> WorkflowDefinitionVersions { get; set; }
|
||||
public DbSet<WorkflowInstanceEntity> WorkflowInstances { get; set; }
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Elsa.AutoMapper.Extensions;
|
|||
using Elsa.AutoMapper.Extensions.NodaTime;
|
||||
using Elsa.Persistence.YesSql.Indexes;
|
||||
using Elsa.Persistence.YesSql.Mapping;
|
||||
using Elsa.Persistence.YesSql.Schema;
|
||||
using Elsa.Persistence.YesSql.Services;
|
||||
using Elsa.Persistence.YesSql.StartupTasks;
|
||||
using Elsa.Runtime;
|
||||
|
|
@ -22,6 +23,7 @@ namespace Elsa.Persistence.YesSql.Extensions
|
|||
.AddSingleton(sp => StoreFactory.CreateStore(sp, configure))
|
||||
.AddSingleton<IIndexProvider, WorkflowDefinitionIndexProvider>()
|
||||
.AddSingleton<IIndexProvider, WorkflowInstanceIndexProvider>()
|
||||
.AddTransient<ISchemaVersionStore, SchemaVersionStore>()
|
||||
.AddScoped(CreateSession)
|
||||
.AddAutoMapperProfile<NodaTimeProfile>(ServiceLifetime.Singleton)
|
||||
.AddAutoMapperProfile<DocumentProfile>(ServiceLifetime.Singleton)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Persistence.YesSql.Schema
|
||||
{
|
||||
public interface ISchemaVersionStore
|
||||
{
|
||||
Task<int> GetVersionAsync();
|
||||
Task SaveVersionAsync(int version);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace Elsa.Persistence.YesSql.Schema
|
||||
{
|
||||
public class SchemaVersionDocument
|
||||
{
|
||||
public int Version { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
using System.Threading.Tasks;
|
||||
using YesSql;
|
||||
|
||||
namespace Elsa.Persistence.YesSql.Schema
|
||||
{
|
||||
public class SchemaVersionStore : ISchemaVersionStore
|
||||
{
|
||||
private readonly ISession session;
|
||||
|
||||
public SchemaVersionStore(ISession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public async Task<int> GetVersionAsync()
|
||||
{
|
||||
var schemaVersion = await session.Query<SchemaVersionDocument>().FirstOrDefaultAsync();
|
||||
|
||||
if (schemaVersion == null)
|
||||
{
|
||||
schemaVersion = new SchemaVersionDocument {Version = 0};
|
||||
session.Save(schemaVersion);
|
||||
await session.CommitAsync();
|
||||
}
|
||||
|
||||
return schemaVersion.Version;
|
||||
}
|
||||
|
||||
public async Task SaveVersionAsync(int version)
|
||||
{
|
||||
var schemaVersion = await session.Query<SchemaVersionDocument>().FirstOrDefaultAsync();
|
||||
schemaVersion.Version = version;
|
||||
session.Save(schemaVersion);
|
||||
await session.CommitAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Persistence.YesSql.Indexes;
|
||||
using Elsa.Persistence.YesSql.Schema;
|
||||
using Elsa.Runtime;
|
||||
using YesSql;
|
||||
using YesSql.Sql;
|
||||
|
|
@ -11,19 +14,68 @@ namespace Elsa.Persistence.YesSql.StartupTasks
|
|||
public class InitializeStoreTask : IStartupTask
|
||||
{
|
||||
private readonly IStore store;
|
||||
private readonly ISchemaVersionStore schemaVersionStore;
|
||||
private readonly SchemaUpdate[] schemaVersionUpdates;
|
||||
|
||||
public InitializeStoreTask(IStore store)
|
||||
public InitializeStoreTask(IStore store,
|
||||
ISchemaVersionStore schemaVersionStore)
|
||||
{
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
CreateTables();
|
||||
return Task.CompletedTask;
|
||||
this.schemaVersionStore = schemaVersionStore;
|
||||
|
||||
schemaVersionUpdates = new[]
|
||||
{
|
||||
new SchemaUpdate {Version = 1, Update = UpdateToVersion1}
|
||||
};
|
||||
}
|
||||
|
||||
private void CreateTables()
|
||||
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
int currentVersion = await schemaVersionStore.GetVersionAsync();
|
||||
|
||||
foreach (var schemaUpdate in schemaVersionUpdates.Where(x => x.Version > currentVersion))
|
||||
{
|
||||
schemaUpdate.Update();
|
||||
await schemaVersionStore.SaveVersionAsync(schemaUpdate.Version);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateToVersion1()
|
||||
{
|
||||
PerformUpdates(builder =>
|
||||
{
|
||||
builder
|
||||
.CreateMapIndexTable(nameof(WorkflowDefinitionIndex), table => table
|
||||
.Column<string>("VersionId")
|
||||
.Column<string>("WorkflowDefinitionId")
|
||||
.Column<int>("Version")
|
||||
.Column<bool>("IsPublished")
|
||||
.Column<bool>("IsLatest")
|
||||
.Column<bool>("IsDisabled")
|
||||
)
|
||||
.CreateMapIndexTable(nameof(WorkflowDefinitionStartActivitiesIndex), table => table
|
||||
.Column<string>("StartActivityId")
|
||||
.Column<string>("StartActivityType")
|
||||
.Column<bool>("IsDisabled")
|
||||
)
|
||||
.CreateMapIndexTable(nameof(WorkflowInstanceIndex), table => table
|
||||
.Column<string>("WorkflowInstanceId")
|
||||
.Column<string>("WorkflowDefinitionId")
|
||||
.Column<string>("CorrelationId")
|
||||
.Column<string>("WorkflowStatus")
|
||||
.Column<DateTime>("CreatedAt")
|
||||
)
|
||||
.CreateMapIndexTable(nameof(WorkflowInstanceBlockingActivitiesIndex), table => table
|
||||
.Column<string>("ActivityId")
|
||||
.Column<string>("ActivityType")
|
||||
.Column<string>("CorrelationId")
|
||||
.Column<string>("WorkflowStatus")
|
||||
.Column<DateTime>("CreatedAt")
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void PerformUpdates(Action<ISchemaBuilder> builder)
|
||||
{
|
||||
using (var connection = store.Configuration.ConnectionFactory.CreateConnection())
|
||||
{
|
||||
|
|
@ -31,38 +83,19 @@ namespace Elsa.Persistence.YesSql.StartupTasks
|
|||
|
||||
using (var transaction = connection.BeginTransaction(store.Configuration.IsolationLevel))
|
||||
{
|
||||
new SchemaBuilder(store.Configuration, transaction, false)
|
||||
.CreateMapIndexTable(nameof(WorkflowDefinitionIndex), table => table
|
||||
.Column<string>("VersionId")
|
||||
.Column<string>("WorkflowDefinitionId")
|
||||
.Column<int>("Version")
|
||||
.Column<bool>("IsPublished")
|
||||
.Column<bool>("IsLatest")
|
||||
.Column<bool>("IsDisabled")
|
||||
)
|
||||
.CreateMapIndexTable(nameof(WorkflowDefinitionStartActivitiesIndex), table => table
|
||||
.Column<string>("StartActivityId")
|
||||
.Column<string>("StartActivityType")
|
||||
.Column<bool>("IsDisabled")
|
||||
)
|
||||
.CreateMapIndexTable(nameof(WorkflowInstanceIndex), table => table
|
||||
.Column<string>("WorkflowInstanceId")
|
||||
.Column<string>("WorkflowDefinitionId")
|
||||
.Column<string>("CorrelationId")
|
||||
.Column<string>("WorkflowStatus")
|
||||
.Column<DateTime>("CreatedAt")
|
||||
)
|
||||
.CreateMapIndexTable(nameof(WorkflowInstanceBlockingActivitiesIndex), table => table
|
||||
.Column<string>("ActivityId")
|
||||
.Column<string>("ActivityType")
|
||||
.Column<string>("CorrelationId")
|
||||
.Column<string>("WorkflowStatus")
|
||||
.Column<DateTime>("CreatedAt")
|
||||
);
|
||||
var schemaBuilder = new SchemaBuilder(store.Configuration, transaction, false);
|
||||
|
||||
builder(schemaBuilder);
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SchemaUpdate
|
||||
{
|
||||
internal int Version { get; set; }
|
||||
internal Action Update { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Elsa;
|
||||
using Elsa.Persistence.EntityFrameworkCore.CustomSchema;
|
||||
using Elsa.Persistence.EntityFrameworkCore.DbContexts;
|
||||
using Elsa.Persistence.EntityFrameworkCore.Extensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Elsa.Extensions;
|
|||
using Elsa.Models;
|
||||
using Elsa.Persistence;
|
||||
using Elsa.Persistence.EntityFrameworkCore;
|
||||
using Elsa.Persistence.EntityFrameworkCore.CustomSchema;
|
||||
using Elsa.Persistence.EntityFrameworkCore.DbContexts;
|
||||
using Elsa.Persistence.EntityFrameworkCore.Extensions;
|
||||
using Elsa.Runtime;
|
||||
|
|
|
|||
Loading…
Reference in a new issue