Browse Source

Backport the Entity Framework 6.x/Entity Framework Core changes to OpenIddict 1.x

pull/670/head
Kévin Chalet 8 years ago
parent
commit
efc67b7186
  1. 72
      src/OpenIddict.EntityFramework/Configurations/OpenIddictApplicationConfiguration.cs
  2. 68
      src/OpenIddict.EntityFramework/Configurations/OpenIddictAuthorizationConfiguration.cs
  3. 57
      src/OpenIddict.EntityFramework/Configurations/OpenIddictScopeConfiguration.cs
  4. 66
      src/OpenIddict.EntityFramework/Configurations/OpenIddictTokenConfiguration.cs
  5. 152
      src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkExtensions.cs
  6. 67
      src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictApplicationConfiguration.cs
  7. 62
      src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictAuthorizationConfiguration.cs
  8. 50
      src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictScopeConfiguration.cs
  9. 57
      src/OpenIddict.EntityFrameworkCore/Configurations/OpenIddictTokenConfiguration.cs
  10. 2
      src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreCustomizer.cs
  11. 123
      src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreExtensions.cs
  12. 56
      test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictEntityFrameworkCoreExtensionsTests.cs

72
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
{
/// <summary>
/// Defines a relational mapping for the Application entity.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictApplicationConfiguration<TApplication, TAuthorization, TToken, TKey> : EntityTypeConfiguration<TApplication>
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TKey : IEquatable<TKey>
{
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");
}
}
}

68
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
{
/// <summary>
/// Defines a relational mapping for the Authorization entity.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictAuthorizationConfiguration<TAuthorization, TApplication, TToken, TKey> : EntityTypeConfiguration<TAuthorization>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TKey : IEquatable<TKey>
{
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");
}
}
}

57
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
{
/// <summary>
/// Defines a relational mapping for the Scope entity.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictScopeConfiguration<TScope, TKey> : EntityTypeConfiguration<TScope>
where TScope : OpenIddictScope<TKey>
where TKey : IEquatable<TKey>
{
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");
}
}
}

66
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
{
/// <summary>
/// Defines a relational mapping for the Token entity.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictTokenConfiguration<TToken, TApplication, TAuthorization, TKey> : EntityTypeConfiguration<TToken>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TKey : IEquatable<TKey>
{
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");
}
}
}

152
src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkExtensions.cs

@ -5,10 +5,7 @@
*/ */
using System; using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity; using System.Data.Entity;
using System.Data.Entity.Infrastructure.Annotations;
using System.Text;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenIddict.EntityFramework; using OpenIddict.EntityFramework;
@ -80,7 +77,7 @@ namespace Microsoft.Extensions.DependencyInjection
} }
/// <summary> /// <summary>
/// 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). /// using the default OpenIddict models and the default key type (string).
/// </summary> /// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param> /// <param name="builder">The builder used to configure the Entity Framework context.</param>
@ -92,8 +89,8 @@ namespace Microsoft.Extensions.DependencyInjection
OpenIddictToken, string>(); OpenIddictToken, string>();
/// <summary> /// <summary>
/// Registers the OpenIddict entity sets in the Entity Framework context /// Registers the OpenIddict entity sets in the Entity Framework 6.x
/// using the specified entities and the specified key type. /// context using the specified entities and the specified key type.
/// Note: using this method requires creating non-generic derived classes /// Note: using this method requires creating non-generic derived classes
/// for all the OpenIddict entities (application, authorization, scope, token). /// for all the OpenIddict entities (application, authorization, scope, token).
/// </summary> /// </summary>
@ -111,145 +108,10 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(builder)); throw new ArgumentNullException(nameof(builder));
} }
// Note: unlike Entity Framework 6.x 1.x/2.x, Entity Framework 6.x builder.Configurations.Add(new OpenIddictApplicationConfiguration<TApplication, TAuthorization, TToken, TKey>());
// always throws an exception when using generic types as entity types. builder.Configurations.Add(new OpenIddictAuthorizationConfiguration<TAuthorization, TApplication, TToken, TKey>());
// To ensure a better exception is thrown, a manual check is made here. builder.Configurations.Add(new OpenIddictScopeConfiguration<TScope, TKey>());
if (typeof(TApplication).IsGenericType) builder.Configurations.Add(new OpenIddictTokenConfiguration<TToken, TApplication, TAuthorization, TKey>());
{
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<TApplication>()
.HasKey(application => application.Id);
builder.Entity<TApplication>()
.Property(application => application.ClientId)
.IsRequired()
.HasMaxLength(450)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));
builder.Entity<TApplication>()
.Property(application => application.ConcurrencyToken)
.IsConcurrencyToken();
builder.Entity<TApplication>()
.Property(application => application.Type)
.IsRequired();
builder.Entity<TApplication>()
.HasMany(application => application.Authorizations)
.WithOptional(authorization => authorization.Application)
.Map(association => association.MapKey("ApplicationId"));
builder.Entity<TApplication>()
.HasMany(application => application.Tokens)
.WithOptional(token => token.Application)
.Map(association => association.MapKey("ApplicationId"));
builder.Entity<TApplication>()
.ToTable("OpenIddictApplications");
// Configure the TAuthorization entity.
builder.Entity<TAuthorization>()
.HasKey(authorization => authorization.Id);
builder.Entity<TAuthorization>()
.Property(authorization => authorization.ConcurrencyToken)
.IsConcurrencyToken();
builder.Entity<TAuthorization>()
.Property(authorization => authorization.Status)
.IsRequired();
builder.Entity<TAuthorization>()
.Property(authorization => authorization.Subject)
.IsRequired();
builder.Entity<TAuthorization>()
.Property(authorization => authorization.Type)
.IsRequired();
builder.Entity<TAuthorization>()
.HasMany(application => application.Tokens)
.WithOptional(token => token.Authorization)
.Map(association => association.MapKey("AuthorizationId"))
.WillCascadeOnDelete();
builder.Entity<TAuthorization>()
.ToTable("OpenIddictAuthorizations");
// Configure the TScope entity.
builder.Entity<TScope>()
.HasKey(scope => scope.Id);
builder.Entity<TScope>()
.Property(scope => scope.ConcurrencyToken)
.IsConcurrencyToken();
builder.Entity<TScope>()
.Property(scope => scope.Name)
.IsRequired()
.HasMaxLength(450)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));
builder.Entity<TScope>()
.ToTable("OpenIddictScopes");
// Configure the TToken entity.
builder.Entity<TToken>()
.HasKey(token => token.Id);
builder.Entity<TToken>()
.Property(token => token.ConcurrencyToken)
.IsConcurrencyToken();
builder.Entity<TToken>()
.Property(token => token.ReferenceId)
.HasMaxLength(450)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));
builder.Entity<TToken>()
.Property(token => token.Subject)
.IsRequired();
builder.Entity<TToken>()
.Property(token => token.Type)
.IsRequired();
builder.Entity<TToken>()
.ToTable("OpenIddictTokens");
return builder; return builder;
} }

67
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
{
/// <summary>
/// Defines a relational mapping for the Application entity.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictApplicationConfiguration<TApplication, TAuthorization, TToken, TKey>
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TKey : IEquatable<TKey>
{
public void Configure(EntityTypeBuilder<TApplication> 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");
}
}
}

62
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
{
/// <summary>
/// Defines a relational mapping for the Authorization entity.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictAuthorizationConfiguration<TAuthorization, TApplication, TToken, TKey>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TKey : IEquatable<TKey>
{
public void Configure(EntityTypeBuilder<TAuthorization> 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");
}
}
}

50
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
{
/// <summary>
/// Defines a relational mapping for the Scope entity.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictScopeConfiguration<TScope, TKey>
where TScope : OpenIddictScope<TKey>
where TKey : IEquatable<TKey>
{
public void Configure(EntityTypeBuilder<TScope> 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");
}
}
}

57
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
{
/// <summary>
/// Defines a relational mapping for the Token entity.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictTokenConfiguration<TToken, TApplication, TAuthorization, TKey>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TKey : IEquatable<TKey>
{
public void Configure(EntityTypeBuilder<TToken> 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");
}
}
}

2
src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreCustomizer.cs

@ -15,7 +15,7 @@ namespace OpenIddict.EntityFrameworkCore
{ {
/// <summary> /// <summary>
/// Represents a model customizer able to register the entity sets /// 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.
/// </summary> /// </summary>
public class OpenIddictEntityFrameworkCoreCustomizer<TApplication, TAuthorization, TScope, TToken, TKey> : ModelCustomizer public class OpenIddictEntityFrameworkCoreCustomizer<TApplication, TAuthorization, TScope, TToken, TKey> : ModelCustomizer
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken> where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>

123
src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreExtensions.cs

@ -79,7 +79,7 @@ namespace Microsoft.Extensions.DependencyInjection
} }
/// <summary> /// <summary>
/// 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). /// using the default OpenIddict models and the default key type (string).
/// </summary> /// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param> /// <param name="builder">The builder used to configure the Entity Framework context.</param>
@ -91,8 +91,8 @@ namespace Microsoft.Extensions.DependencyInjection
OpenIddictToken, string>(); OpenIddictToken, string>();
/// <summary> /// <summary>
/// Registers the OpenIddict entity sets in the Entity Framework context /// Registers the OpenIddict entity sets in the Entity Framework Core
/// using the default OpenIddict models and the specified key type. /// context using the default OpenIddict models and the specified key type.
/// </summary> /// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param> /// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns> /// <returns>The Entity Framework context builder.</returns>
@ -104,8 +104,8 @@ namespace Microsoft.Extensions.DependencyInjection
OpenIddictToken<TKey>, TKey>(); OpenIddictToken<TKey>, TKey>();
/// <summary> /// <summary>
/// Registers the OpenIddict entity sets in the Entity Framework context /// Registers the OpenIddict entity sets in the Entity Framework Core
/// using the specified entities and the specified key type. /// context using the specified entities and the specified key type.
/// </summary> /// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param> /// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns> /// <returns>The Entity Framework context builder.</returns>
@ -128,7 +128,7 @@ namespace Microsoft.Extensions.DependencyInjection
} }
/// <summary> /// <summary>
/// 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). /// using the default OpenIddict models and the default key type (string).
/// </summary> /// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param> /// <param name="builder">The builder used to configure the Entity Framework context.</param>
@ -140,8 +140,8 @@ namespace Microsoft.Extensions.DependencyInjection
OpenIddictToken, string>(); OpenIddictToken, string>();
/// <summary> /// <summary>
/// Registers the OpenIddict entity sets in the Entity Framework context /// Registers the OpenIddict entity sets in the Entity Framework Core
/// using the default OpenIddict models and the specified key type. /// context using the default OpenIddict models and the specified key type.
/// </summary> /// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param> /// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns> /// <returns>The Entity Framework context builder.</returns>
@ -152,8 +152,8 @@ namespace Microsoft.Extensions.DependencyInjection
OpenIddictToken<TKey>, TKey>(); OpenIddictToken<TKey>, TKey>();
/// <summary> /// <summary>
/// Registers the OpenIddict entity sets in the Entity Framework context /// Registers the OpenIddict entity sets in the Entity Framework Core
/// using the specified entities and the specified key type. /// context using the specified entities and the specified key type.
/// </summary> /// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param> /// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns> /// <returns>The Entity Framework context builder.</returns>
@ -169,101 +169,14 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(builder)); throw new ArgumentNullException(nameof(builder));
} }
// Warning: optional foreign keys MUST NOT be added as CLR properties because new OpenIddictApplicationConfiguration<TApplication, TAuthorization, TToken, TKey>()
// Entity Framework would throw an exception due to the TKey generic parameter .Configure(builder.Entity<TApplication>());
// being non-nullable when using value types like short, int, long or Guid. new OpenIddictAuthorizationConfiguration<TAuthorization, TApplication, TToken, TKey>()
.Configure(builder.Entity<TAuthorization>());
// Configure the TApplication entity. new OpenIddictScopeConfiguration<TScope, TKey>()
builder.Entity<TApplication>(entity => .Configure(builder.Entity<TScope>());
{ new OpenIddictTokenConfiguration<TToken, TApplication, TAuthorization, TKey>()
entity.HasKey(application => application.Id); .Configure(builder.Entity<TToken>());
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<TAuthorization>(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<TScope>(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<TToken>(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");
});
return builder; return builder;
} }

56
test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictEntityFrameworkCoreExtensionsTests.cs

@ -5,9 +5,12 @@
*/ */
using System; using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq;
using OpenIddict.Abstractions; using OpenIddict.Abstractions;
using OpenIddict.Core; using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models; using OpenIddict.EntityFrameworkCore.Models;
@ -112,5 +115,58 @@ namespace OpenIddict.EntityFrameworkCore.Tests
// Assert // Assert
Assert.Contains(services, service => service.ServiceType == type && service.ImplementationType == type); 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<long>();
// Assert
Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictApplication<long>)));
Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictAuthorization<long>)));
Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictScope<long>)));
Assert.NotNull(builder.Model.FindEntityType(typeof(OpenIddictToken<long>)));
}
[Fact]
public void UseOpenIddict_RegistersCustomEntityConfigurations()
{
// Arrange
var builder = new ModelBuilder(new ConventionSet());
// Act
builder.UseOpenIddict<CustomApplication, CustomAuthorization, CustomScope, CustomToken, Guid>();
// 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<Guid, CustomAuthorization, CustomToken> { }
public class CustomAuthorization : OpenIddictAuthorization<Guid, CustomApplication, CustomToken> { }
public class CustomScope : OpenIddictScope<Guid> { }
public class CustomToken : OpenIddictToken<Guid, CustomApplication, CustomAuthorization> { }
} }
} }

Loading…
Cancel
Save