diff --git a/data/Piranha.Data.EF/Db.cs b/data/Piranha.Data.EF/Db.cs index f11f087c3..f53e81d6b 100644 --- a/data/Piranha.Data.EF/Db.cs +++ b/data/Piranha.Data.EF/Db.cs @@ -9,6 +9,8 @@ */ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage; using System; using System.Linq; @@ -222,12 +224,26 @@ public abstract class Db : DbContext, IDb where T : Db /// public DbSet Taxonomies { get; set; } + /// + /// Gets the default database schema. + /// + /// + /// If null, the database schema will fall back to the default value used by the database connection. + /// + protected string DatabaseSchema { get; } + /// /// Default constructor. /// /// Configuration options public Db(DbContextOptions options) : base(options) { + var schemaExtension = options.FindExtension(); + if (schemaExtension != null) + { + DatabaseSchema = schemaExtension.Schema; + } + if (!IsInitialized) { lock (Mutex) @@ -251,6 +267,11 @@ public Db(DbContextOptions options) : base(options) /// The current model builder protected override void OnModelCreating(ModelBuilder mb) { + if (DatabaseSchema is not null) + { + mb.HasDefaultSchema(DatabaseSchema); + } + mb.Entity().ToTable("Piranha_Aliases"); mb.Entity().Property(a => a.AliasUrl).IsRequired().HasMaxLength(256); mb.Entity().Property(a => a.RedirectUrl).IsRequired().HasMaxLength(256); @@ -434,7 +455,6 @@ protected override void OnModelCreating(ModelBuilder mb) mb.Entity().HasOne(s => s.Language).WithMany().OnDelete(DeleteBehavior.Restrict); mb.Entity().HasIndex(s => s.InternalId).IsUnique(); - mb.Entity().ToTable("Piranha_SiteFields"); mb.Entity().Property(f => f.RegionId).HasMaxLength(64).IsRequired(); mb.Entity().Property(f => f.FieldId).HasMaxLength(64).IsRequired(); diff --git a/data/Piranha.Data.EF/PiranhaEFDatabaseSchemaExtension.cs b/data/Piranha.Data.EF/PiranhaEFDatabaseSchemaExtension.cs new file mode 100644 index 000000000..a4bf6897b --- /dev/null +++ b/data/Piranha.Data.EF/PiranhaEFDatabaseSchemaExtension.cs @@ -0,0 +1,79 @@ +/* + * Copyright (c) .NET Foundation and Contributors + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * https://github.com/piranhacms/piranha.core + * + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +/// +/// A database context extension used to store a custom database schema. +/// The schema set will be used as the default schema in the database context that this extension is configured. +/// Used by the extension method. +/// +/// +internal class PiranhaEFDatabaseSchemaExtension : IDbContextOptionsExtension +{ + public PiranhaEFDatabaseSchemaExtension(string schema) => SetSchema(schema); + + /// + /// Sets the schema. + /// + /// The schema to use. + /// When schema is null or an empty string. + public void SetSchema(string schema) + { + if (string.IsNullOrWhiteSpace(schema)) + { + throw new ArgumentException("Schema must be a non-empty string", nameof(schema)); + } + Schema = schema; + } + + private PiranhaEFDarabaseSchemaExtensionInfo _info; + + public DbContextOptionsExtensionInfo Info => _info ??= new PiranhaEFDarabaseSchemaExtensionInfo(this); + + public string Schema { get; private set; } + + public void ApplyServices(IServiceCollection services) + { + // This extension does not apply any additional services + } + + public void Validate(IDbContextOptions options) + { + // This extension does not participate in options validation + } +} + +/// +/// The database schema extension info. +/// Used by extension. +/// +/// +internal class PiranhaEFDarabaseSchemaExtensionInfo : DbContextOptionsExtensionInfo +{ + public PiranhaEFDarabaseSchemaExtensionInfo(IDbContextOptionsExtension extension) : base(extension) + { + } + + public override string LogFragment { get; } = string.Empty; + public override bool IsDatabaseProvider { get; } + + public override int GetServiceProviderHashCode() => 0; + + public override void PopulateDebugInfo(IDictionary debugInfo) + { + // This extension does not provide additional debug info + } + + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true; +} \ No newline at end of file diff --git a/data/Piranha.Data.EF/PiranhaEFExtensions.cs b/data/Piranha.Data.EF/PiranhaEFExtensions.cs index 5202da66b..7922888e8 100644 --- a/data/Piranha.Data.EF/PiranhaEFExtensions.cs +++ b/data/Piranha.Data.EF/PiranhaEFExtensions.cs @@ -9,11 +9,18 @@ */ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations.Internal; using Microsoft.Extensions.DependencyInjection; using Piranha; using Piranha.Repositories; using Piranha.Services; using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; public static class PiranhaEFExtensions { @@ -92,4 +99,28 @@ private static IServiceCollection RegisterServices(this IServiceCollection se return services; } + + /// + /// Adds a custom database schema that will be used by the database context being configured. + /// + /// The database context options builder. + /// The database schema. + /// A database context options builder. + /// Throws when is null or empty. + public static DbContextOptionsBuilder UseCustomDatabaseSchema(this DbContextOptionsBuilder builder, string schema) + { + var extension = builder.Options.FindExtension(); + if (extension == null) + { + extension = new PiranhaEFDatabaseSchemaExtension(schema); + } + else + { + extension.SetSchema(schema); + } + + ((IDbContextOptionsBuilderInfrastructure)builder).AddOrUpdateExtension(extension); + + return builder; + } } \ No newline at end of file