From aebbc46acdef29903717bcd25d6a35107e385d5f Mon Sep 17 00:00:00 2001 From: Alex Henderson Date: Wed, 29 Jul 2026 21:51:56 +1200 Subject: [PATCH] feat(quartz): map EXECUTION_GROUP, PREFERRED_NODE and PREFERRED_NODE_AUTO columns Adds the remaining optional columns introduced across Quartz.NET 3.17-3.19 for all four providers (MySql, PostgreSQL, SQLite, SqlServer): - QRTZ_TRIGGERS.EXECUTION_GROUP - QRTZ_FIRED_TRIGGERS.EXECUTION_GROUP - QRTZ_TRIGGERS.PREFERRED_NODE - QRTZ_TRIGGERS.PREFERRED_NODE_AUTO Column names, nullability and the PREFERRED_NODE_AUTO default follow the official Quartz.NET table scripts. Store types follow each provider's existing convention in this repo. Fixes #73 Fixes #74 Co-Authored-By: Claude Opus 5 (1M context) --- ...artzFiredTriggerEntityTypeConfiguration.cs | 4 + .../QuartzTriggerEntityTypeConfiguration.cs | 14 ++++ ...artzFiredTriggerEntityTypeConfiguration.cs | 4 + .../QuartzTriggerEntityTypeConfiguration.cs | 14 ++++ ...artzFiredTriggerEntityTypeConfiguration.cs | 4 + .../QuartzTriggerEntityTypeConfiguration.cs | 14 ++++ ...artzFiredTriggerEntityTypeConfiguration.cs | 5 ++ .../QuartzTriggerEntityTypeConfiguration.cs | 15 ++++ .../Quartz/QuartzFiredTrigger.cs | 1 + .../Quartz/QuartzTrigger.cs | 3 + .../QuartzTriggerModelMappingTests.cs | 75 +++++++++++++++++++ .../QuartzTriggerModelMappingTests.cs | 65 ++++++++++++++++ .../QuartzTriggerModelMappingTests.cs | 71 ++++++++++++++++++ .../QuartzTriggerModelMappingTests.cs | 68 +++++++++++++++++ 14 files changed, 357 insertions(+) diff --git a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs index caefa20..77e5680 100644 --- a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs +++ b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs @@ -80,6 +80,10 @@ public void Configure(EntityTypeBuilder builder) .HasColumnName("REQUESTS_RECOVERY") .HasColumnType("tinyint(1)"); + builder.Property(x => x.ExecutionGroup) + .HasColumnName("EXECUTION_GROUP") + .HasColumnType("varchar(200)"); + builder.HasIndex(x => x.TriggerName) .HasDatabaseName($"IDX_{prefix}FT_TRIG_NAME"); diff --git a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs index a01aa57..1ab612b 100644 --- a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs +++ b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs @@ -82,6 +82,20 @@ public void Configure(EntityTypeBuilder builder) .HasColumnName("MISFIRE_ORIG_FIRE_TIME") .HasColumnType("bigint(19)"); + builder.Property(x => x.ExecutionGroup) + .HasColumnName("EXECUTION_GROUP") + .HasColumnType("varchar(200)"); + + builder.Property(x => x.PreferredNode) + .HasColumnName("PREFERRED_NODE") + .HasColumnType("varchar(200)"); + + builder.Property(x => x.PreferredNodeAuto) + .HasColumnName("PREFERRED_NODE_AUTO") + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .IsRequired(); + builder.Property(x => x.CalendarName) .HasColumnName("CALENDAR_NAME") .HasColumnType("varchar(200)"); diff --git a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs index d388b8a..009cba6 100644 --- a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs +++ b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs @@ -82,6 +82,10 @@ public void Configure(EntityTypeBuilder builder) .HasColumnName("requests_recovery") .HasColumnType("bool"); + builder.Property(x => x.ExecutionGroup) + .HasColumnName("execution_group") + .HasColumnType("text"); + builder.HasIndex(x => x.TriggerName) .HasDatabaseName($"idx_{prefix}ft_trig_name"); diff --git a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs index 8217e2a..6033c2c 100644 --- a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs +++ b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs @@ -84,6 +84,20 @@ public void Configure(EntityTypeBuilder builder) .HasColumnName("misfire_orig_fire_time") .HasColumnType("bigint"); + builder.Property(x => x.ExecutionGroup) + .HasColumnName("execution_group") + .HasColumnType("text"); + + builder.Property(x => x.PreferredNode) + .HasColumnName("preferred_node") + .HasColumnType("text"); + + builder.Property(x => x.PreferredNodeAuto) + .HasColumnName("preferred_node_auto") + .HasColumnType("bool") + .HasDefaultValue(false) + .IsRequired(); + builder.Property(x => x.CalendarName) .HasColumnName("calendar_name") .HasColumnType("text"); diff --git a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs index 1801feb..3305021 100644 --- a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs +++ b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs @@ -80,6 +80,10 @@ public void Configure(EntityTypeBuilder builder) .HasColumnName("REQUESTS_RECOVERY") .HasColumnType("bool"); + builder.Property(x => x.ExecutionGroup) + .HasColumnName("EXECUTION_GROUP") + .HasColumnType("text"); + builder.HasIndex(x => x.TriggerName) .HasDatabaseName($"IDX_{_prefix}FT_TRIG_NAME"); diff --git a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs index db5ea50..058f5bd 100644 --- a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs +++ b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs @@ -82,6 +82,20 @@ public void Configure(EntityTypeBuilder builder) .HasColumnName("MISFIRE_ORIG_FIRE_TIME") .HasColumnType("bigint"); + builder.Property(x => x.ExecutionGroup) + .HasColumnName("EXECUTION_GROUP") + .HasColumnType("text"); + + builder.Property(x => x.PreferredNode) + .HasColumnName("PREFERRED_NODE") + .HasColumnType("text"); + + builder.Property(x => x.PreferredNodeAuto) + .HasColumnName("PREFERRED_NODE_AUTO") + .HasColumnType("bool") + .HasDefaultValue(false) + .IsRequired(); + builder.Property(x => x.CalendarName) .HasColumnName("CALENDAR_NAME") .HasColumnType("text"); diff --git a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs index 384f985..f958ee3 100644 --- a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs +++ b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer/EntityTypeConfigurations/QuartzFiredTriggerEntityTypeConfiguration.cs @@ -84,6 +84,11 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.RequestsRecovery) .HasColumnName("REQUESTS_RECOVERY"); + builder.Property(x => x.ExecutionGroup) + .HasColumnName("EXECUTION_GROUP") + .HasMaxLength(200) + .IsUnicode(); + builder.HasIndex(x => x.TriggerName) .HasDatabaseName($"IDX_{prefix}FT_TRIG_NAME"); diff --git a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs index 8ea49a5..e4f9228 100644 --- a/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs +++ b/src/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer/EntityTypeConfigurations/QuartzTriggerEntityTypeConfiguration.cs @@ -88,6 +88,21 @@ public void Configure(EntityTypeBuilder builder) .HasColumnName("MISFIRE_ORIG_FIRE_TIME") .HasColumnType("bigint"); + builder.Property(x => x.ExecutionGroup) + .HasColumnName("EXECUTION_GROUP") + .HasMaxLength(200) + .IsUnicode(); + + builder.Property(x => x.PreferredNode) + .HasColumnName("PREFERRED_NODE") + .HasMaxLength(200) + .IsUnicode(); + + builder.Property(x => x.PreferredNodeAuto) + .HasColumnName("PREFERRED_NODE_AUTO") + .HasDefaultValue(false) + .IsRequired(); + builder.Property(x => x.CalendarName) .HasColumnName("CALENDAR_NAME") .HasMaxLength(200) diff --git a/src/AppAny.Quartz.EntityFrameworkCore.Migrations/Quartz/QuartzFiredTrigger.cs b/src/AppAny.Quartz.EntityFrameworkCore.Migrations/Quartz/QuartzFiredTrigger.cs index 4d89989..8fc876e 100644 --- a/src/AppAny.Quartz.EntityFrameworkCore.Migrations/Quartz/QuartzFiredTrigger.cs +++ b/src/AppAny.Quartz.EntityFrameworkCore.Migrations/Quartz/QuartzFiredTrigger.cs @@ -15,5 +15,6 @@ public class QuartzFiredTrigger public string? JobGroup { get; set; } public bool IsNonConcurrent { get; set; } public bool? RequestsRecovery { get; set; } + public string? ExecutionGroup { get; set; } } } diff --git a/src/AppAny.Quartz.EntityFrameworkCore.Migrations/Quartz/QuartzTrigger.cs b/src/AppAny.Quartz.EntityFrameworkCore.Migrations/Quartz/QuartzTrigger.cs index 62f2b7e..bc34241 100644 --- a/src/AppAny.Quartz.EntityFrameworkCore.Migrations/Quartz/QuartzTrigger.cs +++ b/src/AppAny.Quartz.EntityFrameworkCore.Migrations/Quartz/QuartzTrigger.cs @@ -18,6 +18,9 @@ public class QuartzTrigger public long StartTime { get; set; } public long? EndTime { get; set; } public long? MisfireOriginalFireTime { get; set; } + public string? ExecutionGroup { get; set; } + public string? PreferredNode { get; set; } + public bool PreferredNodeAuto { get; set; } public string? CalendarName { get; set; } = null!; public short? MisfireInstruction { get; set; } public byte[]? JobData { get; set; } diff --git a/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql.Tests/QuartzTriggerModelMappingTests.cs b/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql.Tests/QuartzTriggerModelMappingTests.cs index 336d152..2e70c23 100644 --- a/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql.Tests/QuartzTriggerModelMappingTests.cs +++ b/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.MySql.Tests/QuartzTriggerModelMappingTests.cs @@ -35,4 +35,79 @@ public void ShouldMapMisfireOriginalFireTimeColumn() Assert.Equal("MISFIRE_ORIG_FIRE_TIME", property!.GetColumnName(table)); Assert.Equal("bigint(19)", property.GetColumnType()); } + + [Fact] + public void ShouldMapExecutionGroupColumnOnTriggers() + { + using var dbContext = new MySqlIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzTrigger)); + + Assert.NotNull(entityType); + + var property = entityType!.FindProperty(nameof(QuartzTrigger.ExecutionGroup)); + Assert.NotNull(property); + + var table = StoreObjectIdentifier.Table(entityType.GetTableName()!, entityType.GetSchema()); + Assert.Equal("EXECUTION_GROUP", property!.GetColumnName(table)); + Assert.Equal("varchar(200)", property.GetColumnType()); + Assert.True(property.IsNullable); + } + + [Fact] + public void ShouldMapExecutionGroupColumnOnFiredTriggers() + { + using var dbContext = new MySqlIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzFiredTrigger)); + + Assert.NotNull(entityType); + + var property = entityType!.FindProperty(nameof(QuartzFiredTrigger.ExecutionGroup)); + Assert.NotNull(property); + + var table = StoreObjectIdentifier.Table(entityType.GetTableName()!, entityType.GetSchema()); + Assert.Equal("EXECUTION_GROUP", property!.GetColumnName(table)); + Assert.Equal("varchar(200)", property.GetColumnType()); + Assert.True(property.IsNullable); + } + + [Fact] + public void ShouldMapPreferredNodeColumnsOnTriggers() + { + using var dbContext = new MySqlIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzTrigger)); + + Assert.NotNull(entityType); + + var table = StoreObjectIdentifier.Table(entityType!.GetTableName()!, entityType.GetSchema()); + + var preferredNode = entityType.FindProperty(nameof(QuartzTrigger.PreferredNode)); + Assert.NotNull(preferredNode); + Assert.Equal("PREFERRED_NODE", preferredNode!.GetColumnName(table)); + Assert.Equal("varchar(200)", preferredNode.GetColumnType()); + Assert.True(preferredNode.IsNullable); + + var preferredNodeAuto = entityType.FindProperty(nameof(QuartzTrigger.PreferredNodeAuto)); + Assert.NotNull(preferredNodeAuto); + Assert.Equal("PREFERRED_NODE_AUTO", preferredNodeAuto!.GetColumnName(table)); + Assert.Equal("tinyint(1)", preferredNodeAuto.GetColumnType()); + Assert.False(preferredNodeAuto.IsNullable); + Assert.Equal(false, preferredNodeAuto.GetDefaultValue()); + } + + private static DbContextOptions CreateOptions() + { +#if NET10_0_OR_GREATER + // Oracle MySQL provider uses UseMySQL (capital SQL) and doesn't require ServerVersion parameter + return new DbContextOptionsBuilder() + .UseMySQL("Server=localhost;Port=3306;Database=quartz_mapping_tests;User=root;Password=password") + .Options; +#else + // Pomelo provider uses UseMySql and requires ServerVersion parameter (NET8_0) + return new DbContextOptionsBuilder() + .UseMySql( + "Server=localhost;Port=3306;Database=quartz_mapping_tests;User=root;Password=password", + ServerVersion.Parse("8.0.36-mysql")) + .Options; +#endif + } } diff --git a/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL.Tests/QuartzTriggerModelMappingTests.cs b/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL.Tests/QuartzTriggerModelMappingTests.cs index 9cc2778..9685190 100644 --- a/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL.Tests/QuartzTriggerModelMappingTests.cs +++ b/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL.Tests/QuartzTriggerModelMappingTests.cs @@ -25,4 +25,69 @@ public void ShouldMapMisfireOriginalFireTimeColumn() Assert.Equal("misfire_orig_fire_time", property!.GetColumnName(table)); Assert.Equal("bigint", property.GetColumnType()); } + + [Fact] + public void ShouldMapExecutionGroupColumnOnTriggers() + { + using var dbContext = new PostgreSqlIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzTrigger)); + + Assert.NotNull(entityType); + + var property = entityType!.FindProperty(nameof(QuartzTrigger.ExecutionGroup)); + Assert.NotNull(property); + + var table = StoreObjectIdentifier.Table(entityType.GetTableName()!, entityType.GetSchema()); + Assert.Equal("execution_group", property!.GetColumnName(table)); + Assert.Equal("text", property.GetColumnType()); + Assert.True(property.IsNullable); + } + + [Fact] + public void ShouldMapExecutionGroupColumnOnFiredTriggers() + { + using var dbContext = new PostgreSqlIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzFiredTrigger)); + + Assert.NotNull(entityType); + + var property = entityType!.FindProperty(nameof(QuartzFiredTrigger.ExecutionGroup)); + Assert.NotNull(property); + + var table = StoreObjectIdentifier.Table(entityType.GetTableName()!, entityType.GetSchema()); + Assert.Equal("execution_group", property!.GetColumnName(table)); + Assert.Equal("text", property.GetColumnType()); + Assert.True(property.IsNullable); + } + + [Fact] + public void ShouldMapPreferredNodeColumnsOnTriggers() + { + using var dbContext = new PostgreSqlIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzTrigger)); + + Assert.NotNull(entityType); + + var table = StoreObjectIdentifier.Table(entityType!.GetTableName()!, entityType.GetSchema()); + + var preferredNode = entityType.FindProperty(nameof(QuartzTrigger.PreferredNode)); + Assert.NotNull(preferredNode); + Assert.Equal("preferred_node", preferredNode!.GetColumnName(table)); + Assert.Equal("text", preferredNode.GetColumnType()); + Assert.True(preferredNode.IsNullable); + + var preferredNodeAuto = entityType.FindProperty(nameof(QuartzTrigger.PreferredNodeAuto)); + Assert.NotNull(preferredNodeAuto); + Assert.Equal("preferred_node_auto", preferredNodeAuto!.GetColumnName(table)); + Assert.Equal("bool", preferredNodeAuto.GetColumnType()); + Assert.False(preferredNodeAuto.IsNullable); + Assert.Equal(false, preferredNodeAuto.GetDefaultValue()); + } + + private static DbContextOptions CreateOptions() + { + return new DbContextOptionsBuilder() + .UseNpgsql("Host=localhost;Port=5432;Database=quartz_mapping_tests;Username=postgres;Password=postgres") + .Options; + } } diff --git a/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite.Tests/QuartzTriggerModelMappingTests.cs b/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite.Tests/QuartzTriggerModelMappingTests.cs index 18c421f..3b7470e 100644 --- a/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite.Tests/QuartzTriggerModelMappingTests.cs +++ b/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite.Tests/QuartzTriggerModelMappingTests.cs @@ -32,4 +32,75 @@ public void ShouldMapMisfireOriginalFireTimeColumn() Assert.Equal("MISFIRE_ORIG_FIRE_TIME", property!.GetColumnName(table)); Assert.Equal("bigint", property.GetColumnType()); } + + [Fact] + public void ShouldMapExecutionGroupColumnOnTriggers() + { + using var dbContext = new SQLiteIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzTrigger)); + + Assert.NotNull(entityType); + + var property = entityType!.FindProperty(nameof(QuartzTrigger.ExecutionGroup)); + Assert.NotNull(property); + + var table = StoreObjectIdentifier.Table(entityType.GetTableName()!, entityType.GetSchema()); + Assert.Equal("EXECUTION_GROUP", property!.GetColumnName(table)); + Assert.Equal("text", property.GetColumnType()); + Assert.True(property.IsNullable); + } + + [Fact] + public void ShouldMapExecutionGroupColumnOnFiredTriggers() + { + using var dbContext = new SQLiteIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzFiredTrigger)); + + Assert.NotNull(entityType); + + var property = entityType!.FindProperty(nameof(QuartzFiredTrigger.ExecutionGroup)); + Assert.NotNull(property); + + var table = StoreObjectIdentifier.Table(entityType.GetTableName()!, entityType.GetSchema()); + Assert.Equal("EXECUTION_GROUP", property!.GetColumnName(table)); + Assert.Equal("text", property.GetColumnType()); + Assert.True(property.IsNullable); + } + + [Fact] + public void ShouldMapPreferredNodeColumnsOnTriggers() + { + using var dbContext = new SQLiteIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzTrigger)); + + Assert.NotNull(entityType); + + var table = StoreObjectIdentifier.Table(entityType!.GetTableName()!, entityType.GetSchema()); + + var preferredNode = entityType.FindProperty(nameof(QuartzTrigger.PreferredNode)); + Assert.NotNull(preferredNode); + Assert.Equal("PREFERRED_NODE", preferredNode!.GetColumnName(table)); + Assert.Equal("text", preferredNode.GetColumnType()); + Assert.True(preferredNode.IsNullable); + + var preferredNodeAuto = entityType.FindProperty(nameof(QuartzTrigger.PreferredNodeAuto)); + Assert.NotNull(preferredNodeAuto); + Assert.Equal("PREFERRED_NODE_AUTO", preferredNodeAuto!.GetColumnName(table)); + Assert.Equal("bool", preferredNodeAuto.GetColumnType()); + Assert.False(preferredNodeAuto.IsNullable); + Assert.Equal(false, preferredNodeAuto.GetDefaultValue()); + } + + private static DbContextOptions CreateOptions() + { + var connectionString = new SqliteConnectionStringBuilder + { + Mode = SqliteOpenMode.Memory, + DataSource = ":memory:" + }.ToString(); + + return new DbContextOptionsBuilder() + .UseSqlite(connectionString) + .Options; + } } diff --git a/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer.Tests/QuartzTriggerModelMappingTests.cs b/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer.Tests/QuartzTriggerModelMappingTests.cs index 8035913..fea42f0 100644 --- a/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer.Tests/QuartzTriggerModelMappingTests.cs +++ b/tests/AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer.Tests/QuartzTriggerModelMappingTests.cs @@ -25,4 +25,72 @@ public void ShouldMapMisfireOriginalFireTimeColumn() Assert.Equal("MISFIRE_ORIG_FIRE_TIME", property!.GetColumnName(table)); Assert.Equal("bigint", property.GetColumnType()); } + + [Fact] + public void ShouldMapExecutionGroupColumnOnTriggers() + { + using var dbContext = new SqlServerIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzTrigger)); + + Assert.NotNull(entityType); + + var property = entityType!.FindProperty(nameof(QuartzTrigger.ExecutionGroup)); + Assert.NotNull(property); + + var table = StoreObjectIdentifier.Table(entityType.GetTableName()!, entityType.GetSchema()); + Assert.Equal("EXECUTION_GROUP", property!.GetColumnName(table)); + Assert.Equal(200, property.GetMaxLength()); + Assert.True(property.IsUnicode()); + Assert.True(property.IsNullable); + } + + [Fact] + public void ShouldMapExecutionGroupColumnOnFiredTriggers() + { + using var dbContext = new SqlServerIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzFiredTrigger)); + + Assert.NotNull(entityType); + + var property = entityType!.FindProperty(nameof(QuartzFiredTrigger.ExecutionGroup)); + Assert.NotNull(property); + + var table = StoreObjectIdentifier.Table(entityType.GetTableName()!, entityType.GetSchema()); + Assert.Equal("EXECUTION_GROUP", property!.GetColumnName(table)); + Assert.Equal(200, property.GetMaxLength()); + Assert.True(property.IsUnicode()); + Assert.True(property.IsNullable); + } + + [Fact] + public void ShouldMapPreferredNodeColumnsOnTriggers() + { + using var dbContext = new SqlServerIntegrationDbContext(CreateOptions()); + var entityType = dbContext.Model.FindEntityType(typeof(QuartzTrigger)); + + Assert.NotNull(entityType); + + var table = StoreObjectIdentifier.Table(entityType!.GetTableName()!, entityType.GetSchema()); + + var preferredNode = entityType.FindProperty(nameof(QuartzTrigger.PreferredNode)); + Assert.NotNull(preferredNode); + Assert.Equal("PREFERRED_NODE", preferredNode!.GetColumnName(table)); + Assert.Equal(200, preferredNode.GetMaxLength()); + Assert.True(preferredNode.IsUnicode()); + Assert.True(preferredNode.IsNullable); + + var preferredNodeAuto = entityType.FindProperty(nameof(QuartzTrigger.PreferredNodeAuto)); + Assert.NotNull(preferredNodeAuto); + Assert.Equal("PREFERRED_NODE_AUTO", preferredNodeAuto!.GetColumnName(table)); + Assert.Equal("bit", preferredNodeAuto.GetColumnType()); + Assert.False(preferredNodeAuto.IsNullable); + Assert.Equal(false, preferredNodeAuto.GetDefaultValue()); + } + + private static DbContextOptions CreateOptions() + { + return new DbContextOptionsBuilder() + .UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=QuartzMappingTests;Trusted_Connection=True;") + .Options; + } }