diff --git a/src/OpenIddict.EntityFramework/Configurations/OpenIddictApplicationConfiguration.cs b/src/OpenIddict.EntityFramework/Configurations/OpenIddictApplicationConfiguration.cs new file mode 100644 index 00000000..3cb81bf3 --- /dev/null +++ b/src/OpenIddict.EntityFramework/Configurations/OpenIddictApplicationConfiguration.cs @@ -0,0 +1,72 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data.Entity.Infrastructure.Annotations; +using System.Data.Entity.ModelConfiguration; +using System.Text; +using OpenIddict.EntityFramework.Models; + +namespace OpenIddict.EntityFramework +{ + /// + /// Defines a relational mapping for the Application entity. + /// + /// The type of the Application entity. + /// The type of the Authorization entity. + /// The type of the Token entity. + /// The type of the Key entity. + [EditorBrowsable(EditorBrowsableState.Never)] + public class OpenIddictApplicationConfiguration : EntityTypeConfiguration + where TApplication : OpenIddictApplication + where TAuthorization : OpenIddictAuthorization + where TToken : OpenIddictToken + where TKey : IEquatable + { + public OpenIddictApplicationConfiguration() + { + // Note: unlike Entity Framework Core 1.x/2.x, Entity Framework 6.x + // always throws an exception when using generic types as entity types. + // To ensure a better exception is thrown, a manual check is made here. + if (typeof(TApplication).IsGenericType) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The application entity cannot be a generic type.") + .Append("Consider creating a non-generic derived class.") + .ToString()); + } + + // Warning: optional foreign keys MUST NOT be added as CLR properties because + // 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); + + Property(application => application.ClientId) + .IsRequired() + .HasMaxLength(450) + .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); + + Property(application => application.ConcurrencyToken) + .IsConcurrencyToken(); + + Property(application => application.Type) + .IsRequired(); + + HasMany(application => application.Authorizations) + .WithOptional(authorization => authorization.Application) + .Map(association => association.MapKey("ApplicationId")); + + HasMany(application => application.Tokens) + .WithOptional(token => token.Application) + .Map(association => association.MapKey("ApplicationId")); + + ToTable("OpenIddictApplications"); + } + } +} diff --git a/src/OpenIddict.EntityFramework/Configurations/OpenIddictAuthorizationConfiguration.cs b/src/OpenIddict.EntityFramework/Configurations/OpenIddictAuthorizationConfiguration.cs new file mode 100644 index 00000000..6d30cc12 --- /dev/null +++ b/src/OpenIddict.EntityFramework/Configurations/OpenIddictAuthorizationConfiguration.cs @@ -0,0 +1,68 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.ComponentModel; +using System.Data.Entity.ModelConfiguration; +using System.Text; +using OpenIddict.EntityFramework.Models; + +namespace OpenIddict.EntityFramework +{ + /// + /// Defines a relational mapping for the Authorization entity. + /// + /// The type of the Authorization entity. + /// The type of the Application entity. + /// The type of the Token entity. + /// The type of the Key entity. + [EditorBrowsable(EditorBrowsableState.Never)] + public class OpenIddictAuthorizationConfiguration : EntityTypeConfiguration + where TAuthorization : OpenIddictAuthorization + where TApplication : OpenIddictApplication + where TToken : OpenIddictToken + where TKey : IEquatable + { + public OpenIddictAuthorizationConfiguration() + { + // Note: unlike Entity Framework Core 1.x/2.x, Entity Framework 6.x + // always throws an exception when using generic types as entity types. + // To ensure a better exception is thrown, a manual check is made here. + if (typeof(TAuthorization).IsGenericType) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The authorization entity cannot be a generic type.") + .Append("Consider creating a non-generic derived class.") + .ToString()); + } + + // Warning: optional foreign keys MUST NOT be added as CLR properties because + // 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); + + Property(authorization => authorization.ConcurrencyToken) + .IsConcurrencyToken(); + + Property(authorization => authorization.Status) + .IsRequired(); + + Property(authorization => authorization.Subject) + .IsRequired(); + + Property(authorization => authorization.Type) + .IsRequired(); + + HasMany(authorization => authorization.Tokens) + .WithOptional(token => token.Authorization) + .Map(association => association.MapKey("AuthorizationId")) + .WillCascadeOnDelete(); + + ToTable("OpenIddictAuthorizations"); + } + } +} diff --git a/src/OpenIddict.EntityFramework/Configurations/OpenIddictScopeConfiguration.cs b/src/OpenIddict.EntityFramework/Configurations/OpenIddictScopeConfiguration.cs new file mode 100644 index 00000000..6748215f --- /dev/null +++ b/src/OpenIddict.EntityFramework/Configurations/OpenIddictScopeConfiguration.cs @@ -0,0 +1,57 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data.Entity.Infrastructure.Annotations; +using System.Data.Entity.ModelConfiguration; +using System.Text; +using OpenIddict.EntityFramework.Models; + +namespace OpenIddict.EntityFramework +{ + /// + /// Defines a relational mapping for the Scope entity. + /// + /// The type of the Scope entity. + /// The type of the Key entity. + [EditorBrowsable(EditorBrowsableState.Never)] + public class OpenIddictScopeConfiguration : EntityTypeConfiguration + where TScope : OpenIddictScope + where TKey : IEquatable + { + public OpenIddictScopeConfiguration() + { + // Note: unlike Entity Framework Core 1.x/2.x, Entity Framework 6.x + // always throws an exception when using generic types as entity types. + // To ensure a better exception is thrown, a manual check is made here. + if (typeof(TScope).IsGenericType) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The scope entity cannot be a generic type.") + .Append("Consider creating a non-generic derived class.") + .ToString()); + } + + // Warning: optional foreign keys MUST NOT be added as CLR properties because + // 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); + + Property(scope => scope.ConcurrencyToken) + .IsConcurrencyToken(); + + Property(scope => scope.Name) + .IsRequired() + .HasMaxLength(450) + .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); + + ToTable("OpenIddictScopes"); + } + } +} diff --git a/src/OpenIddict.EntityFramework/Configurations/OpenIddictTokenConfiguration.cs b/src/OpenIddict.EntityFramework/Configurations/OpenIddictTokenConfiguration.cs new file mode 100644 index 00000000..ef442392 --- /dev/null +++ b/src/OpenIddict.EntityFramework/Configurations/OpenIddictTokenConfiguration.cs @@ -0,0 +1,66 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data.Entity.Infrastructure.Annotations; +using System.Data.Entity.ModelConfiguration; +using System.Text; +using OpenIddict.EntityFramework.Models; + +namespace OpenIddict.EntityFramework +{ + /// + /// Defines a relational mapping for the Token entity. + /// + /// The type of the Token entity. + /// The type of the Application entity. + /// The type of the Authorization entity. + /// The type of the Key entity. + [EditorBrowsable(EditorBrowsableState.Never)] + public class OpenIddictTokenConfiguration : EntityTypeConfiguration + where TToken : OpenIddictToken + where TApplication : OpenIddictApplication + where TAuthorization : OpenIddictAuthorization + where TKey : IEquatable + { + public OpenIddictTokenConfiguration() + { + // Note: unlike Entity Framework Core 1.x/2.x, Entity Framework 6.x + // always throws an exception when using generic types as entity types. + // To ensure a better exception is thrown, a manual check is made here. + if (typeof(TToken).IsGenericType) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The token entity cannot be a generic type.") + .Append("Consider creating a non-generic derived class.") + .ToString()); + } + + // Warning: optional foreign keys MUST NOT be added as CLR properties because + // 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); + + Property(token => token.ConcurrencyToken) + .IsConcurrencyToken(); + + Property(token => token.ReferenceId) + .HasMaxLength(450) + .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); + + Property(token => token.Subject) + .IsRequired(); + + Property(token => token.Type) + .IsRequired(); + + ToTable("OpenIddictTokens"); + } + } +} diff --git a/src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkExtensions.cs b/src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkExtensions.cs index d6833a88..c5225680 100644 --- a/src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkExtensions.cs +++ b/src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkExtensions.cs @@ -5,10 +5,7 @@ */ using System; -using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity; -using System.Data.Entity.Infrastructure.Annotations; -using System.Text; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection.Extensions; using OpenIddict.EntityFramework; @@ -80,7 +77,7 @@ namespace Microsoft.Extensions.DependencyInjection } /// - /// Registers the OpenIddict entity sets in the Entity Framework context + /// Registers the OpenIddict entity sets in the Entity Framework 6.x context /// using the default OpenIddict models and the default key type (string). /// /// The builder used to configure the Entity Framework context. @@ -92,8 +89,8 @@ namespace Microsoft.Extensions.DependencyInjection OpenIddictToken, string>(); /// - /// Registers the OpenIddict entity sets in the Entity Framework context - /// using the specified entities and the specified key type. + /// Registers the OpenIddict entity sets in the Entity Framework 6.x + /// context using the specified entities and the specified key type. /// Note: using this method requires creating non-generic derived classes /// for all the OpenIddict entities (application, authorization, scope, token). /// @@ -111,145 +108,10 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(builder)); } - // Note: unlike Entity Framework 6.x 1.x/2.x, Entity Framework 6.x - // always throws an exception when using generic types as entity types. - // To ensure a better exception is thrown, a manual check is made here. - if (typeof(TApplication).IsGenericType) - { - throw new InvalidOperationException(new StringBuilder() - .AppendLine("The application entity cannot be a generic type.") - .Append("Consider creating a non-generic derived class.") - .ToString()); - } - - if (typeof(TAuthorization).IsGenericType) - { - throw new InvalidOperationException(new StringBuilder() - .AppendLine("The authorization entity cannot be a generic type.") - .Append("Consider creating a non-generic derived class.") - .ToString()); - } - - if (typeof(TScope).IsGenericType) - { - throw new InvalidOperationException(new StringBuilder() - .AppendLine("The scope entity cannot be a generic type.") - .Append("Consider creating a non-generic derived class.") - .ToString()); - } - - if (typeof(TToken).IsGenericType) - { - throw new InvalidOperationException(new StringBuilder() - .AppendLine("The token entity cannot be a generic type.") - .Append("Consider creating a non-generic derived class.") - .ToString()); - } - - // Warning: optional foreign keys MUST NOT be added as CLR properties because - // 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. - - // Configure the TApplication entity. - builder.Entity() - .HasKey(application => application.Id); - - builder.Entity() - .Property(application => application.ClientId) - .IsRequired() - .HasMaxLength(450) - .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); - - builder.Entity() - .Property(application => application.ConcurrencyToken) - .IsConcurrencyToken(); - - builder.Entity() - .Property(application => application.Type) - .IsRequired(); - - builder.Entity() - .HasMany(application => application.Authorizations) - .WithOptional(authorization => authorization.Application) - .Map(association => association.MapKey("ApplicationId")); - - builder.Entity() - .HasMany(application => application.Tokens) - .WithOptional(token => token.Application) - .Map(association => association.MapKey("ApplicationId")); - - builder.Entity() - .ToTable("OpenIddictApplications"); - - // Configure the TAuthorization entity. - builder.Entity() - .HasKey(authorization => authorization.Id); - - builder.Entity() - .Property(authorization => authorization.ConcurrencyToken) - .IsConcurrencyToken(); - - builder.Entity() - .Property(authorization => authorization.Status) - .IsRequired(); - - builder.Entity() - .Property(authorization => authorization.Subject) - .IsRequired(); - - builder.Entity() - .Property(authorization => authorization.Type) - .IsRequired(); - - builder.Entity() - .HasMany(application => application.Tokens) - .WithOptional(token => token.Authorization) - .Map(association => association.MapKey("AuthorizationId")) - .WillCascadeOnDelete(); - - builder.Entity() - .ToTable("OpenIddictAuthorizations"); - - // Configure the TScope entity. - builder.Entity() - .HasKey(scope => scope.Id); - - builder.Entity() - .Property(scope => scope.ConcurrencyToken) - .IsConcurrencyToken(); - - builder.Entity() - .Property(scope => scope.Name) - .IsRequired() - .HasMaxLength(450) - .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); - - builder.Entity() - .ToTable("OpenIddictScopes"); - - // Configure the TToken entity. - builder.Entity() - .HasKey(token => token.Id); - - builder.Entity() - .Property(token => token.ConcurrencyToken) - .IsConcurrencyToken(); - - builder.Entity() - .Property(token => token.ReferenceId) - .HasMaxLength(450) - .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); - - builder.Entity() - .Property(token => token.Subject) - .IsRequired(); - - builder.Entity() - .Property(token => token.Type) - .IsRequired(); - - builder.Entity() - .ToTable("OpenIddictTokens"); + builder.Configurations.Add(new OpenIddictApplicationConfiguration()); + builder.Configurations.Add(new OpenIddictAuthorizationConfiguration()); + builder.Configurations.Add(new OpenIddictScopeConfiguration()); + builder.Configurations.Add(new OpenIddictTokenConfiguration()); return builder; } diff --git a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictApplicationConfiguration.cs b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictApplicationConfiguration.cs new file mode 100644 index 00000000..485a7661 --- /dev/null +++ b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictApplicationConfiguration.cs @@ -0,0 +1,67 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.ComponentModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using OpenIddict.EntityFrameworkCore.Models; + +namespace OpenIddict.EntityFrameworkCore +{ + /// + /// Defines a relational mapping for the Application entity. + /// + /// The type of the Application entity. + /// The type of the Authorization entity. + /// The type of the Token entity. + /// The type of the Key entity. + [EditorBrowsable(EditorBrowsableState.Never)] + public class OpenIddictApplicationConfiguration + where TApplication : OpenIddictApplication + where TAuthorization : OpenIddictAuthorization + where TToken : OpenIddictToken + where TKey : IEquatable + { + public void Configure(EntityTypeBuilder builder) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + // Warning: optional foreign keys MUST NOT be added as CLR properties because + // 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.HasIndex(application => application.ClientId) + .IsUnique(); + + builder.Property(application => application.ClientId) + .IsRequired(); + + builder.Property(application => application.ConcurrencyToken) + .IsConcurrencyToken(); + + builder.Property(application => application.Type) + .IsRequired(); + + builder.HasMany(application => application.Authorizations) + .WithOne(authorization => authorization.Application) + .HasForeignKey("ApplicationId") + .IsRequired(required: false); + + builder.HasMany(application => application.Tokens) + .WithOne(token => token.Application) + .HasForeignKey("ApplicationId") + .IsRequired(required: false); + + builder.ToTable("OpenIddictApplications"); + } + } +} diff --git a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictAuthorizationConfiguration.cs b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictAuthorizationConfiguration.cs new file mode 100644 index 00000000..8ba9eeb5 --- /dev/null +++ b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictAuthorizationConfiguration.cs @@ -0,0 +1,62 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.ComponentModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using OpenIddict.EntityFrameworkCore.Models; + +namespace OpenIddict.EntityFrameworkCore +{ + /// + /// Defines a relational mapping for the Authorization entity. + /// + /// The type of the Authorization entity. + /// The type of the Application entity. + /// The type of the Token entity. + /// The type of the Key entity. + [EditorBrowsable(EditorBrowsableState.Never)] + public class OpenIddictAuthorizationConfiguration + where TAuthorization : OpenIddictAuthorization + where TApplication : OpenIddictApplication + where TToken : OpenIddictToken + where TKey : IEquatable + { + public void Configure(EntityTypeBuilder builder) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + // Warning: optional foreign keys MUST NOT be added as CLR properties because + // 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.Property(authorization => authorization.ConcurrencyToken) + .IsConcurrencyToken(); + + builder.Property(authorization => authorization.Status) + .IsRequired(); + + builder.Property(authorization => authorization.Subject) + .IsRequired(); + + builder.Property(authorization => authorization.Type) + .IsRequired(); + + builder.HasMany(authorization => authorization.Tokens) + .WithOne(token => token.Authorization) + .HasForeignKey("AuthorizationId") + .IsRequired(required: false); + + builder.ToTable("OpenIddictAuthorizations"); + } + } +} diff --git a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictScopeConfiguration.cs b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictScopeConfiguration.cs new file mode 100644 index 00000000..039a51f9 --- /dev/null +++ b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictScopeConfiguration.cs @@ -0,0 +1,50 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.ComponentModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using OpenIddict.EntityFrameworkCore.Models; + +namespace OpenIddict.EntityFrameworkCore +{ + /// + /// Defines a relational mapping for the Scope entity. + /// + /// The type of the Scope entity. + /// The type of the Key entity. + [EditorBrowsable(EditorBrowsableState.Never)] + public class OpenIddictScopeConfiguration + where TScope : OpenIddictScope + where TKey : IEquatable + { + public void Configure(EntityTypeBuilder builder) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + // Warning: optional foreign keys MUST NOT be added as CLR properties because + // 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.HasIndex(scope => scope.Name) + .IsUnique(); + + builder.Property(scope => scope.ConcurrencyToken) + .IsConcurrencyToken(); + + builder.Property(scope => scope.Name) + .IsRequired(); + + builder.ToTable("OpenIddictScopes"); + } + } +} diff --git a/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictTokenConfiguration.cs b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictTokenConfiguration.cs new file mode 100644 index 00000000..43ec6bd3 --- /dev/null +++ b/src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictTokenConfiguration.cs @@ -0,0 +1,57 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.ComponentModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using OpenIddict.EntityFrameworkCore.Models; + +namespace OpenIddict.EntityFrameworkCore +{ + /// + /// Defines a relational mapping for the Token entity. + /// + /// The type of the Token entity. + /// The type of the Application entity. + /// The type of the Authorization entity. + /// The type of the Key entity. + [EditorBrowsable(EditorBrowsableState.Never)] + public class OpenIddictTokenConfiguration + where TToken : OpenIddictToken + where TApplication : OpenIddictApplication + where TAuthorization : OpenIddictAuthorization + where TKey : IEquatable + { + public void Configure(EntityTypeBuilder builder) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + // Warning: optional foreign keys MUST NOT be added as CLR properties because + // 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.HasIndex(token => token.ReferenceId) + .IsUnique(); + + builder.Property(token => token.ConcurrencyToken) + .IsConcurrencyToken(); + + builder.Property(token => token.Subject) + .IsRequired(); + + builder.Property(token => token.Type) + .IsRequired(); + + builder.ToTable("OpenIddictTokens"); + } + } +} diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreCustomizer.cs b/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreCustomizer.cs index fbf1d4a4..8d2e5d5b 100644 --- a/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreCustomizer.cs +++ b/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreCustomizer.cs @@ -15,7 +15,7 @@ namespace OpenIddict.EntityFrameworkCore { /// /// Represents a model customizer able to register the entity sets - /// required by the OpenIddict stack in an Entity Framework context. + /// required by the OpenIddict stack in an Entity Framework Core context. /// public class OpenIddictEntityFrameworkCoreCustomizer : ModelCustomizer where TApplication : OpenIddictApplication diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreExtensions.cs b/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreExtensions.cs index 30fd8981..e9936d05 100644 --- a/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreExtensions.cs +++ b/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreExtensions.cs @@ -79,7 +79,7 @@ namespace Microsoft.Extensions.DependencyInjection } /// - /// Registers the OpenIddict entity sets in the Entity Framework context + /// Registers the OpenIddict entity sets in the Entity Framework Core context /// using the default OpenIddict models and the default key type (string). /// /// The builder used to configure the Entity Framework context. @@ -91,8 +91,8 @@ namespace Microsoft.Extensions.DependencyInjection OpenIddictToken, string>(); /// - /// Registers the OpenIddict entity sets in the Entity Framework context - /// using the default OpenIddict models and the specified key type. + /// Registers the OpenIddict entity sets in the Entity Framework Core + /// context using the default OpenIddict models and the specified key type. /// /// The builder used to configure the Entity Framework context. /// The Entity Framework context builder. @@ -104,8 +104,8 @@ namespace Microsoft.Extensions.DependencyInjection OpenIddictToken, TKey>(); /// - /// Registers the OpenIddict entity sets in the Entity Framework context - /// using the specified entities and the specified key type. + /// Registers the OpenIddict entity sets in the Entity Framework Core + /// context using the specified entities and the specified key type. /// /// The builder used to configure the Entity Framework context. /// The Entity Framework context builder. @@ -128,7 +128,7 @@ namespace Microsoft.Extensions.DependencyInjection } /// - /// Registers the OpenIddict entity sets in the Entity Framework context + /// Registers the OpenIddict entity sets in the Entity Framework Core context /// using the default OpenIddict models and the default key type (string). /// /// The builder used to configure the Entity Framework context. @@ -140,8 +140,8 @@ namespace Microsoft.Extensions.DependencyInjection OpenIddictToken, string>(); /// - /// Registers the OpenIddict entity sets in the Entity Framework context - /// using the default OpenIddict models and the specified key type. + /// Registers the OpenIddict entity sets in the Entity Framework Core + /// context using the default OpenIddict models and the specified key type. /// /// The builder used to configure the Entity Framework context. /// The Entity Framework context builder. @@ -152,8 +152,8 @@ namespace Microsoft.Extensions.DependencyInjection OpenIddictToken, TKey>(); /// - /// Registers the OpenIddict entity sets in the Entity Framework context - /// using the specified entities and the specified key type. + /// Registers the OpenIddict entity sets in the Entity Framework Core + /// context using the specified entities and the specified key type. /// /// The builder used to configure the Entity Framework context. /// The Entity Framework context builder. @@ -169,101 +169,14 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(builder)); } - // Warning: optional foreign keys MUST NOT be added as CLR properties because - // 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. - - // Configure the TApplication entity. - builder.Entity(entity => - { - entity.HasKey(application => application.Id); - - entity.HasIndex(application => application.ClientId) - .IsUnique(); - - entity.Property(application => application.ClientId) - .IsRequired(); - - entity.Property(application => application.ConcurrencyToken) - .IsConcurrencyToken(); - - entity.Property(application => application.Type) - .IsRequired(); - - entity.HasMany(application => application.Authorizations) - .WithOne(authorization => authorization.Application) - .HasForeignKey("ApplicationId") - .IsRequired(required: false); - - entity.HasMany(application => application.Tokens) - .WithOne(token => token.Application) - .HasForeignKey("ApplicationId") - .IsRequired(required: false); - - entity.ToTable("OpenIddictApplications"); - }); - - // Configure the TAuthorization entity. - builder.Entity(entity => - { - entity.HasKey(authorization => authorization.Id); - - entity.Property(authorization => authorization.ConcurrencyToken) - .IsConcurrencyToken(); - - entity.Property(authorization => authorization.Status) - .IsRequired(); - - entity.Property(authorization => authorization.Subject) - .IsRequired(); - - entity.Property(authorization => authorization.Type) - .IsRequired(); - - entity.HasMany(authorization => authorization.Tokens) - .WithOne(token => token.Authorization) - .HasForeignKey("AuthorizationId") - .IsRequired(required: false); - - entity.ToTable("OpenIddictAuthorizations"); - }); - - // Configure the TScope entity. - builder.Entity(entity => - { - entity.HasKey(scope => scope.Id); - - entity.HasIndex(scope => scope.Name) - .IsUnique(); - - entity.Property(scope => scope.ConcurrencyToken) - .IsConcurrencyToken(); - - entity.Property(scope => scope.Name) - .IsRequired(); - - entity.ToTable("OpenIddictScopes"); - }); - - // Configure the TToken entity. - builder.Entity(entity => - { - entity.HasKey(token => token.Id); - - entity.HasIndex(token => token.ReferenceId) - .IsUnique(); - - entity.Property(token => token.ConcurrencyToken) - .IsConcurrencyToken(); - - entity.Property(token => token.Subject) - .IsRequired(); - - entity.Property(token => token.Type) - .IsRequired(); - - entity.ToTable("OpenIddictTokens"); - }); + new OpenIddictApplicationConfiguration() + .Configure(builder.Entity()); + new OpenIddictAuthorizationConfiguration() + .Configure(builder.Entity()); + new OpenIddictScopeConfiguration() + .Configure(builder.Entity()); + new OpenIddictTokenConfiguration() + .Configure(builder.Entity()); return builder; } diff --git a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictEntityFrameworkCoreExtensionsTests.cs b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictEntityFrameworkCoreExtensionsTests.cs index 659c3fa7..4fe800ae 100644 --- a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictEntityFrameworkCoreExtensionsTests.cs +++ b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictEntityFrameworkCoreExtensionsTests.cs @@ -5,9 +5,12 @@ */ using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Conventions; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Moq; using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.EntityFrameworkCore.Models; @@ -112,5 +115,58 @@ namespace OpenIddict.EntityFrameworkCore.Tests // Assert Assert.Contains(services, service => service.ServiceType == type && service.ImplementationType == type); } + + [Fact] + public void UseOpenIddict_RegistersDefaultEntityConfigurations() + { + // Arrange + var builder = new ModelBuilder(new ConventionSet()); + + // Act + builder.UseOpenIddict(); + + // Assert + Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictApplication))); + Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictAuthorization))); + Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictScope))); + Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictToken))); + } + + [Fact] + public void UseOpenIddict_RegistersDefaultEntityConfigurationsWithCustomKeyType() + { + // Arrange + var builder = new ModelBuilder(new ConventionSet()); + + // Act + builder.UseOpenIddict(); + + // Assert + Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictApplication))); + Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictAuthorization))); + Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictScope))); + Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictToken))); + } + + [Fact] + public void UseOpenIddict_RegistersCustomEntityConfigurations() + { + // Arrange + var builder = new ModelBuilder(new ConventionSet()); + + // Act + builder.UseOpenIddict(); + + // Assert + Assert.NotNull(builder.Model.FindEntityType(typeof(CustomApplication))); + Assert.NotNull(builder.Model.FindEntityType(typeof(CustomAuthorization))); + Assert.NotNull(builder.Model.FindEntityType(typeof(CustomScope))); + Assert.NotNull(builder.Model.FindEntityType(typeof(CustomToken))); + } + + public class CustomApplication : OpenIddictApplication { } + public class CustomAuthorization : OpenIddictAuthorization { } + public class CustomScope : OpenIddictScope { } + public class CustomToken : OpenIddictToken { } } }