From 3aea683b83aee1d5abd233217491a8575ca45da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Thu, 24 Jul 2025 10:41:32 +0200 Subject: [PATCH] Reduce the maximum lengh of string primary keys in the EF 6 and EF Core stores --- ...EntityFrameworkApplicationConfiguration.cs | 35 ++++++++++++------- ...tityFrameworkAuthorizationConfiguration.cs | 29 ++++++++++----- ...IddictEntityFrameworkScopeConfiguration.cs | 17 +++++++-- ...IddictEntityFrameworkTokenConfiguration.cs | 23 ++++++++---- ...tyFrameworkCoreApplicationConfiguration.cs | 28 +++++++++------ ...FrameworkCoreAuthorizationConfiguration.cs | 22 +++++++----- ...ctEntityFrameworkCoreScopeConfiguration.cs | 14 +++++--- ...ctEntityFrameworkCoreTokenConfiguration.cs | 20 +++++++---- 8 files changed, 128 insertions(+), 60 deletions(-) diff --git a/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkApplicationConfiguration.cs b/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkApplicationConfiguration.cs index 81434fcd4..b95e358ae 100644 --- a/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkApplicationConfiguration.cs +++ b/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkApplicationConfiguration.cs @@ -9,6 +9,7 @@ using System.Data.Entity.Infrastructure.Annotations; using System.Data.Entity.ModelConfiguration; using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; using OpenIddict.EntityFramework.Models; namespace OpenIddict.EntityFramework; @@ -37,39 +38,49 @@ public OpenIddictEntityFrameworkApplicationConfiguration() // Entity Framework would throw an exception due to the TKey generic parameter // being non-nullable when using value types like short, int, long or Guid. - HasKey(application => application.Id); + HasKey(static application => application.Id); - Property(application => application.ApplicationType) + Property(static application => application.ApplicationType) .HasMaxLength(50); - Property(application => application.ClientId) + Property(static application => application.ClientId) .HasMaxLength(100) .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute { IsUnique = true })); - Property(application => application.ClientType) + Property(static application => application.ClientType) .HasMaxLength(50); - Property(application => application.ConcurrencyToken) + Property(static application => application.ConcurrencyToken) .HasMaxLength(50) .IsConcurrencyToken(); - Property(application => application.ConsentType) + Property(static application => application.ConsentType) .HasMaxLength(50); - HasMany(application => application.Authorizations) - .WithOptional(authorization => authorization.Application!) - .Map(association => + if (typeof(TKey) == typeof(string)) + { + var parameter = Expression.Parameter(typeof(TApplication), "application"); + var property = Expression.Property(parameter, + typeof(TApplication).GetProperty(nameof(OpenIddictEntityFrameworkApplication.Id))!); + var lambda = Expression.Lambda>(property, parameter); + + Property(lambda).HasMaxLength(100); + } + + HasMany(static application => application.Authorizations) + .WithOptional(static authorization => authorization.Application!) + .Map(static association => { association.MapKey(nameof(OpenIddictEntityFrameworkAuthorization.Application) + nameof(OpenIddictEntityFrameworkApplication.Id)); }); - HasMany(application => application.Tokens) - .WithOptional(token => token.Application!) - .Map(association => + HasMany(static application => application.Tokens) + .WithOptional(static token => token.Application!) + .Map(static association => { association.MapKey(nameof(OpenIddictEntityFrameworkToken.Application) + nameof(OpenIddictEntityFrameworkApplication.Id)); diff --git a/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkAuthorizationConfiguration.cs b/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkAuthorizationConfiguration.cs index 45faa2017..eb7f3dd63 100644 --- a/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkAuthorizationConfiguration.cs +++ b/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkAuthorizationConfiguration.cs @@ -7,6 +7,7 @@ using System.ComponentModel; using System.Data.Entity.ModelConfiguration; using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; using OpenIddict.EntityFramework.Models; namespace OpenIddict.EntityFramework; @@ -35,25 +36,35 @@ public OpenIddictEntityFrameworkAuthorizationConfiguration() // Entity Framework would throw an exception due to the TKey generic parameter // being non-nullable when using value types like short, int, long or Guid. - HasKey(authorization => authorization.Id); + HasKey(static authorization => authorization.Id); - Property(authorization => authorization.ConcurrencyToken) + Property(static authorization => authorization.ConcurrencyToken) .HasMaxLength(50) .IsConcurrencyToken(); - Property(authorization => authorization.Status) + if (typeof(TKey) == typeof(string)) + { + var parameter = Expression.Parameter(typeof(TAuthorization), "authorization"); + var property = Expression.Property(parameter, + typeof(TAuthorization).GetProperty(nameof(OpenIddictEntityFrameworkAuthorization.Id))!); + var lambda = Expression.Lambda>(property, parameter); + + Property(lambda).HasMaxLength(100); + } + + Property(static authorization => authorization.Status) .HasMaxLength(50); - Property(authorization => authorization.Subject) + Property(static authorization => authorization.Subject) .HasMaxLength(400); - Property(authorization => authorization.Type) + Property(static authorization => authorization.Type) .HasMaxLength(50); - HasMany(authorization => authorization.Tokens) - .WithOptional(token => token.Authorization!) - .Map(association => association.MapKey(nameof(OpenIddictEntityFrameworkToken.Authorization) + - nameof(OpenIddictEntityFrameworkAuthorization.Id))) + HasMany(static authorization => authorization.Tokens) + .WithOptional(static token => token.Authorization!) + .Map(static association => association.MapKey(nameof(OpenIddictEntityFrameworkToken.Authorization) + + nameof(OpenIddictEntityFrameworkAuthorization.Id))) .WillCascadeOnDelete(); ToTable("OpenIddictAuthorizations"); diff --git a/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkScopeConfiguration.cs b/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkScopeConfiguration.cs index c919fe85f..88ae41a86 100644 --- a/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkScopeConfiguration.cs +++ b/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkScopeConfiguration.cs @@ -9,6 +9,7 @@ using System.Data.Entity.Infrastructure.Annotations; using System.Data.Entity.ModelConfiguration; using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; using OpenIddict.EntityFramework.Models; namespace OpenIddict.EntityFramework; @@ -31,13 +32,23 @@ public OpenIddictEntityFrameworkScopeConfiguration() // Entity Framework would throw an exception due to the TKey generic parameter // being non-nullable when using value types like short, int, long or Guid. - HasKey(scope => scope.Id); + HasKey(static scope => scope.Id); - Property(scope => scope.ConcurrencyToken) + Property(static scope => scope.ConcurrencyToken) .HasMaxLength(50) .IsConcurrencyToken(); - Property(scope => scope.Name) + if (typeof(TKey) == typeof(string)) + { + var parameter = Expression.Parameter(typeof(TScope), "scope"); + var property = Expression.Property(parameter, + typeof(TScope).GetProperty(nameof(OpenIddictEntityFrameworkScope.Id))!); + var lambda = Expression.Lambda>(property, parameter); + + Property(lambda).HasMaxLength(100); + } + + Property(static scope => scope.Name) .HasMaxLength(200) .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute { diff --git a/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkTokenConfiguration.cs b/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkTokenConfiguration.cs index 8f27438cd..ecc575777 100644 --- a/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkTokenConfiguration.cs +++ b/src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkTokenConfiguration.cs @@ -9,6 +9,7 @@ using System.Data.Entity.Infrastructure.Annotations; using System.Data.Entity.ModelConfiguration; using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; using OpenIddict.EntityFramework.Models; namespace OpenIddict.EntityFramework; @@ -37,26 +38,36 @@ public OpenIddictEntityFrameworkTokenConfiguration() // Entity Framework would throw an exception due to the TKey generic parameter // being non-nullable when using value types like short, int, long or Guid. - HasKey(token => token.Id); + HasKey(static token => token.Id); - Property(token => token.ConcurrencyToken) + Property(static token => token.ConcurrencyToken) .HasMaxLength(50) .IsConcurrencyToken(); + if (typeof(TKey) == typeof(string)) + { + var parameter = Expression.Parameter(typeof(TToken), "token"); + var property = Expression.Property(parameter, + typeof(TToken).GetProperty(nameof(OpenIddictEntityFrameworkToken.Id))!); + var lambda = Expression.Lambda>(property, parameter); + + Property(lambda).HasMaxLength(100); + } + // Warning: the index on the ReferenceId property MUST NOT be declared as // a unique index, as Entity Framework 6.x doesn't support creating indexes // with null-friendly WHERE conditions, unlike Entity Framework Core. - Property(token => token.ReferenceId) + Property(static token => token.ReferenceId) .HasMaxLength(100) .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); - Property(token => token.Status) + Property(static token => token.Status) .HasMaxLength(50); - Property(token => token.Subject) + Property(static token => token.Subject) .HasMaxLength(400); - Property(token => token.Type) + Property(static token => token.Type) .HasMaxLength(150); ToTable("OpenIddictTokens"); diff --git a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreApplicationConfiguration.cs b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreApplicationConfiguration.cs index b15f683da..ce0ba1c3d 100644 --- a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreApplicationConfiguration.cs +++ b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreApplicationConfiguration.cs @@ -40,9 +40,9 @@ public void Configure(EntityTypeBuilder builder) // Entity Framework would throw an exception due to the TKey generic parameter // being non-nullable when using value types like short, int, long or Guid. - builder.HasKey(application => application.Id); + builder.HasKey(static application => application.Id); - builder.Property(application => application.ApplicationType) + builder.Property(static application => application.ApplicationType) .HasMaxLength(50); // Warning: the non-generic overlord is deliberately used to work around @@ -51,30 +51,36 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(nameof(OpenIddictEntityFrameworkCoreApplication.ClientId)) .IsUnique(); - builder.Property(application => application.ClientId) + builder.Property(static application => application.ClientId) .HasMaxLength(100); - builder.Property(application => application.ClientType) + builder.Property(static application => application.ClientType) .HasMaxLength(50); - builder.Property(application => application.ConcurrencyToken) + builder.Property(static application => application.ConcurrencyToken) .HasMaxLength(50) .IsConcurrencyToken(); - builder.Property(application => application.ConsentType) + builder.Property(static application => application.ConsentType) .HasMaxLength(50); - builder.Property(application => application.Id) + builder.Property(static application => application.Id) .ValueGeneratedOnAdd(); - builder.HasMany(application => application.Authorizations) - .WithOne(authorization => authorization.Application!) + if (typeof(TKey) == typeof(string)) + { + builder.Property(static application => application.Id) + .HasMaxLength(100); + } + + builder.HasMany(static application => application.Authorizations) + .WithOne(static authorization => authorization.Application!) .HasForeignKey(nameof(OpenIddictEntityFrameworkCoreAuthorization.Application) + nameof(OpenIddictEntityFrameworkCoreApplication.Id)) .IsRequired(required: false); - builder.HasMany(application => application.Tokens) - .WithOne(token => token.Application!) + builder.HasMany(static application => application.Tokens) + .WithOne(static token => token.Application!) .HasForeignKey(nameof(OpenIddictEntityFrameworkCoreToken.Application) + nameof(OpenIddictEntityFrameworkCoreApplication.Id)) .IsRequired(required: false); diff --git a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreAuthorizationConfiguration.cs b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreAuthorizationConfiguration.cs index e908075b8..a9ca3c3a0 100644 --- a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreAuthorizationConfiguration.cs +++ b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreAuthorizationConfiguration.cs @@ -40,7 +40,7 @@ public void Configure(EntityTypeBuilder builder) // Entity Framework would throw an exception due to the TKey generic parameter // being non-nullable when using value types like short, int, long or Guid. - builder.HasKey(authorization => authorization.Id); + builder.HasKey(static authorization => authorization.Id); builder.HasIndex( nameof(OpenIddictEntityFrameworkCoreAuthorization.Application) + nameof(OpenIddictEntityFrameworkCoreApplication.Id), @@ -48,24 +48,30 @@ public void Configure(EntityTypeBuilder builder) nameof(OpenIddictEntityFrameworkCoreAuthorization.Subject), nameof(OpenIddictEntityFrameworkCoreAuthorization.Type)); - builder.Property(authorization => authorization.ConcurrencyToken) + builder.Property(static authorization => authorization.ConcurrencyToken) .HasMaxLength(50) .IsConcurrencyToken(); - builder.Property(authorization => authorization.Id) + builder.Property(static authorization => authorization.Id) .ValueGeneratedOnAdd(); - builder.Property(authorization => authorization.Status) + if (typeof(TKey) == typeof(string)) + { + builder.Property(static authorization => authorization.Id) + .HasMaxLength(100); + } + + builder.Property(static authorization => authorization.Status) .HasMaxLength(50); - builder.Property(authorization => authorization.Subject) + builder.Property(static authorization => authorization.Subject) .HasMaxLength(400); - builder.Property(authorization => authorization.Type) + builder.Property(static authorization => authorization.Type) .HasMaxLength(50); - builder.HasMany(authorization => authorization.Tokens) - .WithOne(token => token.Authorization!) + builder.HasMany(static authorization => authorization.Tokens) + .WithOne(static token => token.Authorization!) .HasForeignKey(nameof(OpenIddictEntityFrameworkCoreToken.Authorization) + nameof(OpenIddictEntityFrameworkCoreAuthorization.Id)) .IsRequired(required: false); diff --git a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreScopeConfiguration.cs b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreScopeConfiguration.cs index be2787534..462703c51 100644 --- a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreScopeConfiguration.cs +++ b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreScopeConfiguration.cs @@ -34,7 +34,7 @@ public void Configure(EntityTypeBuilder builder) // Entity Framework would throw an exception due to the TKey generic parameter // being non-nullable when using value types like short, int, long or Guid. - builder.HasKey(scope => scope.Id); + builder.HasKey(static scope => scope.Id); // Warning: the non-generic overlord is deliberately used to work around // a breaking change introduced in Entity Framework Core 3.x (where a @@ -42,14 +42,20 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(nameof(OpenIddictEntityFrameworkCoreScope.Name)) .IsUnique(); - builder.Property(scope => scope.ConcurrencyToken) + builder.Property(static scope => scope.ConcurrencyToken) .HasMaxLength(50) .IsConcurrencyToken(); - builder.Property(scope => scope.Id) + builder.Property(static scope => scope.Id) .ValueGeneratedOnAdd(); - builder.Property(scope => scope.Name) + if (typeof(TKey) == typeof(string)) + { + builder.Property(static scope => scope.Id) + .HasMaxLength(100); + } + + builder.Property(static scope => scope.Name) .HasMaxLength(200); builder.ToTable("OpenIddictScopes"); diff --git a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreTokenConfiguration.cs b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreTokenConfiguration.cs index c9483b35e..34c10e207 100644 --- a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreTokenConfiguration.cs +++ b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictEntityFrameworkCoreTokenConfiguration.cs @@ -40,7 +40,7 @@ public void Configure(EntityTypeBuilder builder) // Entity Framework would throw an exception due to the TKey generic parameter // being non-nullable when using value types like short, int, long or Guid. - builder.HasKey(token => token.Id); + builder.HasKey(static token => token.Id); // Warning: the non-generic overlord is deliberately used to work around // a breaking change introduced in Entity Framework Core 3.x (where a @@ -54,23 +54,29 @@ public void Configure(EntityTypeBuilder builder) nameof(OpenIddictEntityFrameworkCoreToken.Subject), nameof(OpenIddictEntityFrameworkCoreToken.Type)); - builder.Property(token => token.ConcurrencyToken) + builder.Property(static token => token.ConcurrencyToken) .HasMaxLength(50) .IsConcurrencyToken(); - builder.Property(token => token.Id) + builder.Property(static token => token.Id) .ValueGeneratedOnAdd(); - builder.Property(token => token.ReferenceId) + if (typeof(TKey) == typeof(string)) + { + builder.Property(static token => token.Id) + .HasMaxLength(100); + } + + builder.Property(static token => token.ReferenceId) .HasMaxLength(100); - builder.Property(token => token.Status) + builder.Property(static token => token.Status) .HasMaxLength(50); - builder.Property(token => token.Subject) + builder.Property(static token => token.Subject) .HasMaxLength(400); - builder.Property(token => token.Type) + builder.Property(static token => token.Type) .HasMaxLength(150); builder.ToTable("OpenIddictTokens");