From 78d61e70ddb8fc4fe301dffea6cf2ed169b1cd56 Mon Sep 17 00:00:00 2001 From: Sebastian Ederer Date: Mon, 3 Nov 2025 17:55:01 +0100 Subject: [PATCH] fix: implement support for naming conventions --- ...tyFrameworkCore.TimescaleDB.Example.csproj | 3 +- .../Program.cs | 2 +- .../TimescaleMigrationsModelDiffer.cs | 119 +++++++++++++----- 3 files changed, 89 insertions(+), 35 deletions(-) diff --git a/CmdScale.EntityFrameworkCore.TimescaleDB.Example/CmdScale.EntityFrameworkCore.TimescaleDB.Example.csproj b/CmdScale.EntityFrameworkCore.TimescaleDB.Example/CmdScale.EntityFrameworkCore.TimescaleDB.Example.csproj index 762d3d8..3106dd5 100644 --- a/CmdScale.EntityFrameworkCore.TimescaleDB.Example/CmdScale.EntityFrameworkCore.TimescaleDB.Example.csproj +++ b/CmdScale.EntityFrameworkCore.TimescaleDB.Example/CmdScale.EntityFrameworkCore.TimescaleDB.Example.csproj @@ -8,7 +8,8 @@ - + + diff --git a/CmdScale.EntityFrameworkCore.TimescaleDB.Example/Program.cs b/CmdScale.EntityFrameworkCore.TimescaleDB.Example/Program.cs index efb6d19..f216b37 100644 --- a/CmdScale.EntityFrameworkCore.TimescaleDB.Example/Program.cs +++ b/CmdScale.EntityFrameworkCore.TimescaleDB.Example/Program.cs @@ -9,7 +9,7 @@ string? connectionString = builder.Configuration.GetConnectionString("Timescale"); builder.Services.AddDbContext(options => - options.UseNpgsql(connectionString).UseTimescaleDb()); + options.UseNpgsql(connectionString).UseTimescaleDb().UseSnakeCaseNamingConvention()); IHost host = builder.Build(); diff --git a/CmdScale.EntityFrameworkCore.TimescaleDB/TimescaleMigrationsModelDiffer.cs b/CmdScale.EntityFrameworkCore.TimescaleDB/TimescaleMigrationsModelDiffer.cs index 2e4dcbb..81e1283 100644 --- a/CmdScale.EntityFrameworkCore.TimescaleDB/TimescaleMigrationsModelDiffer.cs +++ b/CmdScale.EntityFrameworkCore.TimescaleDB/TimescaleMigrationsModelDiffer.cs @@ -152,36 +152,72 @@ private static IEnumerable GetHypertables(IRelational { // Retrieve the annotations set by the convention bool isHypertable = entityType.FindAnnotation(HypertableAnnotations.IsHypertable)?.Value as bool? ?? false; - string? timeColumnName = entityType.FindAnnotation(HypertableAnnotations.HypertableTimeColumn)?.Value as string; + if (!isHypertable) + { + continue; + } - string chunkTimeInterval = entityType.FindAnnotation(HypertableAnnotations.ChunkTimeInterval)?.Value as string ?? DefaultValues.ChunkTimeInterval; + // Get convention-aware store identifier for the table + StoreObjectIdentifier storeIdentifier = StoreObjectIdentifier.Table(entityType.GetTableName()!, entityType.GetSchema()); + + string? timeColumnModelName = entityType.FindAnnotation(HypertableAnnotations.HypertableTimeColumn)?.Value as string; + if (string.IsNullOrWhiteSpace(timeColumnModelName)) + { + continue; + } + + string? timeColumnName = entityType.FindProperty(timeColumnModelName)?.GetColumnName(storeIdentifier); + if (string.IsNullOrWhiteSpace(timeColumnName)) + { + continue; + } + string? chunkSkipColumnsString = entityType.FindAnnotation(HypertableAnnotations.ChunkSkipColumns)?.Value as string; - List? chunkSkipColumns = chunkSkipColumnsString?.Split(',', StringSplitOptions.TrimEntries).ToList(); + List? chunkSkipColumns = null; + if (!string.IsNullOrWhiteSpace(chunkSkipColumnsString)) + { + chunkSkipColumns = chunkSkipColumnsString.Split(',', StringSplitOptions.TrimEntries) + .Select(modelPropName => entityType.FindProperty(modelPropName)?.GetColumnName(storeIdentifier)) + .Where(name => name != null) + .ToList()!; + } - bool enableCompression = entityType.FindAnnotation(HypertableAnnotations.EnableCompression)?.Value as bool? ?? false; - IAnnotation? additionalDimensionsAnnotations = entityType.FindAnnotation(HypertableAnnotations.AdditionalDimensions); List? additionalDimensions = null; - + IAnnotation? additionalDimensionsAnnotations = entityType.FindAnnotation(HypertableAnnotations.AdditionalDimensions); if (additionalDimensionsAnnotations?.Value is string json && !string.IsNullOrWhiteSpace(json)) { - additionalDimensions = JsonSerializer.Deserialize>(json); + List? modelDimensions = JsonSerializer.Deserialize>(json); + if (modelDimensions != null) + { + additionalDimensions = []; + foreach (Dimension dim in modelDimensions) + { + string? conventionalColumnName = entityType.FindProperty(dim.ColumnName)?.GetColumnName(storeIdentifier); + if (conventionalColumnName != null) + { + Dimension newDimension = JsonSerializer.Deserialize(JsonSerializer.Serialize(dim))!; + newDimension.ColumnName = conventionalColumnName; + additionalDimensions.Add(newDimension); + } + } + } } - if (isHypertable && !string.IsNullOrWhiteSpace(timeColumnName)) + string chunkTimeInterval = entityType.FindAnnotation(HypertableAnnotations.ChunkTimeInterval)?.Value as string ?? DefaultValues.ChunkTimeInterval; + bool enableCompression = entityType.FindAnnotation(HypertableAnnotations.EnableCompression)?.Value as bool? ?? false; + + yield return new CreateHypertableOperation { - yield return new CreateHypertableOperation - { - TableName = entityType.GetTableName()!, - Schema = entityType.GetSchema() ?? DefaultValues.DefaultSchema, - TimeColumnName = timeColumnName, - ChunkTimeInterval = chunkTimeInterval ?? DefaultValues.ChunkTimeInterval, - EnableCompression = enableCompression, - ChunkSkipColumns = chunkSkipColumns, - AdditionalDimensions = additionalDimensions - }; - } + TableName = entityType.GetTableName()!, + Schema = entityType.GetSchema() ?? DefaultValues.DefaultSchema, + TimeColumnName = timeColumnName, + ChunkTimeInterval = chunkTimeInterval ?? DefaultValues.ChunkTimeInterval, + EnableCompression = enableCompression, + ChunkSkipColumns = chunkSkipColumns, + AdditionalDimensions = additionalDimensions + }; } } @@ -191,27 +227,44 @@ private static IEnumerable GetReorderPolicies(IRelati { yield break; } + foreach (IEntityType entityType in relationalModel.Model.GetEntityTypes()) { // Retrieve the annotations set by the convention bool hasReorderPolicy = entityType.FindAnnotation(ReorderPolicyAnnotations.HasReorderPolicy)?.Value as bool? ?? false; - string? indexName = entityType.FindAnnotation(ReorderPolicyAnnotations.IndexName)?.Value as string; - DateTime? initialStart = entityType.FindAnnotation(ReorderPolicyAnnotations.InitialStart)?.Value as DateTime?; + if (!hasReorderPolicy) + { + continue; + } + + // Get convention-aware store identifier for the table + StoreObjectIdentifier storeIdentifier = StoreObjectIdentifier.Table(entityType.GetTableName()!, entityType.GetSchema()); - if (hasReorderPolicy && !string.IsNullOrWhiteSpace(indexName) && !string.IsNullOrWhiteSpace(indexName)) + string? indexModelName = entityType.FindAnnotation(ReorderPolicyAnnotations.IndexName)?.Value as string; + if (string.IsNullOrWhiteSpace(indexModelName)) { - yield return new AddReorderPolicyOperation - { - TableName = entityType.GetTableName()!, - Schema = entityType.GetSchema() ?? DefaultValues.DefaultSchema, - IndexName = indexName!, - InitialStart = initialStart, - ScheduleInterval = entityType.FindAnnotation(ReorderPolicyAnnotations.ScheduleInterval)?.Value as string ?? DefaultValues.ReorderPolicyScheduleInterval, - MaxRuntime = entityType.FindAnnotation(ReorderPolicyAnnotations.MaxRuntime)?.Value as string ?? DefaultValues.ReorderPolicyMaxRuntime, - MaxRetries = entityType.FindAnnotation(ReorderPolicyAnnotations.MaxRetries)?.Value as int? ?? DefaultValues.ReorderPolicyMaxRetries, - RetryPeriod = entityType.FindAnnotation(ReorderPolicyAnnotations.RetryPeriod)?.Value as string ?? DefaultValues.ReorderPolicyRetryPeriod - }; + continue; + } + + string? indexName = entityType.FindIndex(indexModelName)?.GetDatabaseName(storeIdentifier); + if (string.IsNullOrWhiteSpace(indexName)) + { + continue; } + + DateTime? initialStart = entityType.FindAnnotation(ReorderPolicyAnnotations.InitialStart)?.Value as DateTime?; + + yield return new AddReorderPolicyOperation + { + TableName = entityType.GetTableName()!, + Schema = entityType.GetSchema() ?? DefaultValues.DefaultSchema, + IndexName = indexName!, + InitialStart = initialStart, + ScheduleInterval = entityType.FindAnnotation(ReorderPolicyAnnotations.ScheduleInterval)?.Value as string ?? DefaultValues.ReorderPolicyScheduleInterval, + MaxRuntime = entityType.FindAnnotation(ReorderPolicyAnnotations.MaxRuntime)?.Value as string ?? DefaultValues.ReorderPolicyMaxRuntime, + MaxRetries = entityType.FindAnnotation(ReorderPolicyAnnotations.MaxRetries)?.Value as int? ?? DefaultValues.ReorderPolicyMaxRetries, + RetryPeriod = entityType.FindAnnotation(ReorderPolicyAnnotations.RetryPeriod)?.Value as string ?? DefaultValues.ReorderPolicyRetryPeriod + }; } }