Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Oracle EF Core Migrations #6359

Merged
merged 6 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,5 @@ unlist.sh

# build artifacts
/artifacts

/docker/data/
8 changes: 7 additions & 1 deletion Elsa.sln
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docker", "docker", "{986E54
docker\ElsaServer.Dockerfile = docker\ElsaServer.Dockerfile
docker\ElsaServerAndStudio.Dockerfile = docker\ElsaServerAndStudio.Dockerfile
docker\ElsaStudio.Dockerfile = docker\ElsaStudio.Dockerfile
docker\init-db.sh = docker\init-db.sh
docker\otel-collector-config.yaml = docker\otel-collector-config.yaml
docker\docker-compose-kafka.yml = docker\docker-compose-kafka.yml
docker\init-db-postgres.sh = docker\init-db-postgres.sh
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Elasticsearch", "src\modules\Elsa.Elasticsearch\Elsa.Elasticsearch.csproj", "{3246883E-2FA7-4B4A-BDC5-99039A2869BC}"
Expand Down Expand Up @@ -387,6 +387,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Agents.Persistence.Ent
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Kafka", "src\modules\Elsa.Kafka\Elsa.Kafka.csproj", "{BF934627-F531-44FB-BEC2-ECA801FF31E7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "oracle-setup", "oracle-setup", "{66E2E2CF-967F-4564-89E8-F46FA973C99B}"
ProjectSection(SolutionItems) = preProject
docker\oracle-setup\setup.sql = docker\oracle-setup\setup.sql
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -968,6 +973,7 @@ Global
{2B939AC9-03A4-479E-AA0D-CB58F4A7F480} = {50470834-4CD8-479A-8B58-0A1869BA5D37}
{2CDF3E1C-267D-4198-B1C7-7E1F548FC120} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
{BF934627-F531-44FB-BEC2-ECA801FF31E7} = {DD089B8B-DA73-492A-9010-F772D1C178DA}
{66E2E2CF-967F-4564-89E8-F46FA973C99B} = {986E5482-0482-448C-B9E4-EC67A9474B85}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D4B5CEAA-7D70-4FCB-A68E-B03FBE5E0E5E}
Expand Down
14 changes: 13 additions & 1 deletion docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@
- "3306:3306"
volumes:
- mysql_data2:/var/lib/mysql

oracle:
image: container-registry.oracle.com/database/free:latest
container_name: oracle-db
environment:
ORACLE_PWD: elsa
ports:
- "1521:1521"
- "5500:5500"
volumes:
- ./data/oracle-data:/opt/oracle/oradata
- ./setup/oracle-setup:/opt/oracle/scripts/setup

mongodb:
image: mongo:latest
Expand Down Expand Up @@ -84,7 +96,6 @@
- ASPNETCORE_URLS=http://+:80
- Logging__LogLevel__Default=Information


elsa-server:
build:
context: ../.
Expand Down Expand Up @@ -116,6 +127,7 @@

volumes:
postgres-data:
oracle-data-free1:
mysql_data2:
cockroachdb-data:
mongodb_data:
File renamed without changes.
3 changes: 3 additions & 0 deletions docker/setup/oracle-setup/setup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
alter session set "_ORACLE_SCRIPT"=true;
CREATE USER ELSA IDENTIFIED BY elsa;
GRANT ALL PRIVILEGES TO ELSA;
1 change: 1 addition & 0 deletions src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<ProjectReference Include="..\..\modules\Elsa.Caching.Distributed.MassTransit\Elsa.Caching.Distributed.MassTransit.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Caching.Distributed.ProtoActor\Elsa.Caching.Distributed.ProtoActor.csproj" />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.MySql\Elsa.EntityFrameworkCore.MySql.csproj" Condition=" '$(TargetFramework)' != 'net9.0' " />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.Oracle\Elsa.EntityFrameworkCore.Oracle.csproj" />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.PostgreSql\Elsa.EntityFrameworkCore.PostgreSql.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Kafka\Elsa.Kafka.csproj" />
<ProjectReference Include="..\..\modules\Elsa.MassTransit.AzureServiceBus\Elsa.MassTransit.AzureServiceBus.csproj"/>
Expand Down
1 change: 1 addition & 0 deletions src/apps/Elsa.Server.Web/Enums/SqlDatabaseProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ public enum SqlDatabaseProvider
Sqlite,
MySql,
PostgreSql,
Oracle,
CockroachDb
}
15 changes: 13 additions & 2 deletions src/apps/Elsa.Server.Web/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Services;
using Elsa.DropIns.Extensions;
using Elsa.EntityFrameworkCore;
using Elsa.EntityFrameworkCore.Extensions;
using Elsa.EntityFrameworkCore.Modules.Alterations;
using Elsa.EntityFrameworkCore.Modules.Identity;
Expand Down Expand Up @@ -99,6 +100,7 @@
var sqliteConnectionString = configuration.GetConnectionString("Sqlite")!;
var sqlServerConnectionString = configuration.GetConnectionString("SqlServer")!;
var postgresConnectionString = configuration.GetConnectionString("PostgreSql")!;
var oracleConnectionString = configuration.GetConnectionString("Oracle")!;
var mySqlConnectionString = configuration.GetConnectionString("MySql")!;
var cockroachDbConnectionString = configuration.GetConnectionString("CockroachDb")!;
var mongoDbConnectionString = configuration.GetConnectionString("MongoDb")!;
Expand Down Expand Up @@ -195,6 +197,8 @@
#endif
else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
else if (sqlDatabaseProvider == SqlDatabaseProvider.Oracle)
ef.UseOracle(oracleConnectionString, new ElsaDbContextOptions{ SchemaName = "ELSA"});
else
ef.UseSqlite(sp => sp.GetSqliteConnectionString());

Expand Down Expand Up @@ -231,6 +235,8 @@
#endif
else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
else if (sqlDatabaseProvider == SqlDatabaseProvider.Oracle)
ef.UseOracle(oracleConnectionString, new ElsaDbContextOptions{ SchemaName = "ELSA"});
else
ef.UseSqlite(sp => sp.GetSqliteConnectionString());

Expand Down Expand Up @@ -276,7 +282,9 @@
ef.UseMySql(mySqlConnectionString);
#endif
else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
ef.UsePostgreSql(cockroachDbConnectionString);
else if (sqlDatabaseProvider == SqlDatabaseProvider.Oracle)
ef.UseOracle(oracleConnectionString, new ElsaDbContextOptions{ SchemaName = "ELSA"});
else
ef.UseSqlite(sp => sp.GetSqliteConnectionString());

Expand Down Expand Up @@ -411,7 +419,9 @@
ef.UseMySql(mySqlConnectionString);
#endif
else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
ef.UsePostgreSql(cockroachDbConnectionString);
else if (sqlDatabaseProvider == SqlDatabaseProvider.Oracle)
ef.UseOracle(oracleConnectionString, new ElsaDbContextOptions{ SchemaName = "ELSA"});
else
ef.UseSqlite(sp => sp.GetSqliteConnectionString());

Expand Down Expand Up @@ -614,6 +624,7 @@
if (sqlDatabaseProvider == SqlDatabaseProvider.Sqlite) ef.UseSqlite(sqliteConnectionString);
if (sqlDatabaseProvider == SqlDatabaseProvider.SqlServer) ef.UseSqlServer(sqlServerConnectionString);
if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql) ef.UsePostgreSql(postgresConnectionString);
if (sqlDatabaseProvider == SqlDatabaseProvider.Oracle) ef.UseOracle(oracleConnectionString, new ElsaDbContextOptions{ SchemaName = "ELSA"});
#if !NET9_0
if (sqlDatabaseProvider == SqlDatabaseProvider.MySql)
ef.UseMySql(mySqlConnectionString);
Expand Down
1 change: 1 addition & 0 deletions src/apps/Elsa.Server.Web/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"Sqlite": "Data Source=App_Data/elsa.sqlite.db;Cache=Shared;",
"MySql": "Server=localhost;Database=elsa;Uid=admin;Pwd=password;",
"PostgreSql": "Server=localhost;Username=elsa;Database=elsa;Port=5432;Password=elsa;SSLMode=Prefer;MaxPoolSize=2000;Timeout=60",
"Oracle": "Data Source=(DESCRIPTION = (ADDRESS_LIST = (FAILOVER =ON) (LOAD_BALANCE = OFF) (ADDRESS = (PROTOCOL =TCP)(HOST=localhost)(PORT=1521))) (CONNECT_DATA = (SID= FREE) ));User Id=ELSA;Password=elsa;",
"CockroachDb": "Host=localhost;Port=26257;Database=elsa;SslMode=Disable;Username=root;IncludeErrorDetail=true",
"MongoDb": "mongodb://localhost:27017/elsa-workflows",
"AzureServiceBus": "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,5 @@ public override void Apply()
AddEntityStore<ApiKeyDefinition, EFCoreApiKeyStore>();
AddEntityStore<ServiceDefinition, EFCoreServiceStore>();
AddEntityStore<AgentDefinition, EFCoreAgentStore>();
Services.AddScoped<IEntityModelCreatingHandler, SetupForOracle>();
}
}

This file was deleted.

37 changes: 37 additions & 0 deletions src/modules/Elsa.Common/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace Elsa.Extensions;

using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;

public static class ServiceCollectionExtensions
{
/// <summary>
/// Adds the service with a specific implementation type only if the combination
/// of service and implementation does not already exist in the service collection.
/// </summary>
public static IServiceCollection TryAddScopedImplementation<TService, TImplementation>(
this IServiceCollection services)
where TService : class
where TImplementation : class, TService
{
if (!services.Any(sd => sd.ServiceType == typeof(TService) && sd.ImplementationType == typeof(TImplementation)))
services.AddScoped<TService, TImplementation>();

return services;
}

/// <summary>
/// Adds the service with a specific implementation factory only if the combination
/// of service and implementation already doesn't exist.
/// </summary>
public static IServiceCollection TryAddScopedImplementation<TService>(
this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory)
where TService : class
{
if (services.All(sd => sd.ServiceType != typeof(TService)))
services.AddScoped(implementationFactory);

return services;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@ public override void Apply()
{
Services.AddScoped<IEntitySavingHandler, ApplyTenantId>();
Services.AddScoped<IEntityModelCreatingHandler, SetTenantIdFilter>();
Services.AddScoped<IEntityModelCreatingHandler, SetupForSqlite>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,14 @@ namespace Elsa.EntityFrameworkCore;
/// <summary>
/// Class That enable Schema change for Migration
/// </summary>
public class DbSchemaAwareMigrationAssembly : MigrationsAssembly
public class DbSchemaAwareMigrationAssembly(
ICurrentDbContext currentContext,
IDbContextOptions options,
IMigrationsIdGenerator idGenerator,
IDiagnosticsLogger<DbLoggerCategory.Migrations> logger)
: MigrationsAssembly(currentContext, options, idGenerator, logger)
{
private readonly DbContext _context;

public DbSchemaAwareMigrationAssembly(ICurrentDbContext currentContext,
IDbContextOptions options, IMigrationsIdGenerator idGenerator,
IDiagnosticsLogger<DbLoggerCategory.Migrations> logger)
: base(currentContext, options, idGenerator, logger)
{
_context = currentContext.Context;
}
private readonly DbContext _context = currentContext.Context;

public override Migration CreateMigration(TypeInfo migrationClass, string activeProvider)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
<PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" PrivateAssets="all" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="Open.Linq.AsyncExtensions" />
<PackageReference Include="System.CommandLine" />
Expand Down
47 changes: 28 additions & 19 deletions src/modules/Elsa.EntityFrameworkCore.Common/ElsaDbContextBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Elsa.Common.Multitenancy;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.DependencyInjection;

namespace Elsa.EntityFrameworkCore;
Expand All @@ -16,8 +17,9 @@ public abstract class ElsaDbContextBase : DbContext, IElsaDbContextSchema
EntityState.Added,
EntityState.Modified,
};

protected readonly IServiceProvider ServiceProvider;

protected IServiceProvider ServiceProvider { get; }
private readonly ElsaDbContextOptions? _elsaDbContextOptions;
public string? TenantId { get; set; }

/// <summary>
Expand All @@ -39,45 +41,52 @@ public abstract class ElsaDbContextBase : DbContext, IElsaDbContextSchema
protected ElsaDbContextBase(DbContextOptions options, IServiceProvider serviceProvider) : base(options)
{
ServiceProvider = serviceProvider;
var elsaDbContextOptions = options.FindExtension<ElsaDbContextOptionsExtension>()?.Options;

// ReSharper disable once VirtualMemberCallInConstructor
Schema = !string.IsNullOrWhiteSpace(elsaDbContextOptions?.SchemaName) ? elsaDbContextOptions.SchemaName : ElsaSchema;
_elsaDbContextOptions = options.FindExtension<ElsaDbContextOptionsExtension>()?.Options;

// ReSharper disable once VirtualMemberCallInConstructor
Schema = !string.IsNullOrWhiteSpace(_elsaDbContextOptions?.SchemaName) ? _elsaDbContextOptions.SchemaName : ElsaSchema;

var tenantAccessor = serviceProvider.GetService<ITenantAccessor>();
var tenantId = tenantAccessor?.Tenant?.Id;
if(!string.IsNullOrWhiteSpace(tenantId))

if (!string.IsNullOrWhiteSpace(tenantId))
TenantId = tenantId;
}

/// <inheritdoc/>
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
await OnBeforeSavingAsync(cancellationToken);
return await base.SaveChangesAsync(cancellationToken);
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.EnableSensitiveDataLogging();
#if NET9_0_OR_GREATER
optionsBuilder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
#endif
}

/// <inheritdoc />
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
if (!string.IsNullOrWhiteSpace(Schema))
{
if (!Database.IsSqlite())
modelBuilder.HasDefaultSchema(Schema);
}
if (!string.IsNullOrWhiteSpace(Schema))
modelBuilder.HasDefaultSchema(Schema);

var additionalConfigurations = _elsaDbContextOptions?.GetModelConfigurations(this);

additionalConfigurations?.Invoke(modelBuilder);

var entityTypeHandlers = ServiceProvider.GetServices<IEntityModelCreatingHandler>().ToList();

foreach (var entityType in modelBuilder.Model.GetEntityTypes())
foreach (var entityType in modelBuilder.Model.GetEntityTypes().ToList())
{
foreach (var handler in entityTypeHandlers)
{
foreach (var handler in entityTypeHandlers)
handler.Handle(this, modelBuilder, entityType);
}
}
}

private async Task OnBeforeSavingAsync(CancellationToken cancellationToken)
{
var handlers = ServiceProvider.GetServices<IEntitySavingHandler>().ToList();
Expand Down
Loading
Loading