diff --git a/src/OpenIddict.Core/OpenIddictExtensions.cs b/src/OpenIddict.Core/OpenIddictExtensions.cs index c6015012..7e127de5 100644 --- a/src/OpenIddict.Core/OpenIddictExtensions.cs +++ b/src/OpenIddict.Core/OpenIddictExtensions.cs @@ -39,8 +39,8 @@ namespace Microsoft.Extensions.DependencyInjection { throw new ArgumentNullException(nameof(services)); } - return services.AddOpenIddict>, - OpenIddictAuthorization>, + return services.AddOpenIddict, + OpenIddictAuthorization, OpenIddictScope, OpenIddictToken>(); } diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs b/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs index ccbdb5a7..d25d8e6e 100644 --- a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs +++ b/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs @@ -6,6 +6,7 @@ using System; using System.Diagnostics; +using System.Reflection; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -24,18 +25,6 @@ namespace Microsoft.Extensions.DependencyInjection { /// The . public static OpenIddictBuilder AddEntityFrameworkCoreStores([NotNull] this OpenIddictBuilder builder) where TContext : DbContext { - return builder.AddEntityFrameworkCoreStores(); - } - - /// - /// Registers the Entity Framework stores. Note: when using the built-in Entity Framework stores, - /// the entities MUST be derived from the models contained in the OpenIddict.Models package. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AddEntityFrameworkCoreStores([NotNull] this OpenIddictBuilder builder) - where TContext : DbContext - where TKey : IEquatable { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } @@ -45,6 +34,30 @@ namespace Microsoft.Extensions.DependencyInjection { builder.ScopeType != null && builder.TokenType != null, "The entity types exposed by OpenIddictBuilder shouldn't be null."); + var application = FindGenericBaseType(builder.ApplicationType, typeof(OpenIddictApplication<,>)); + if (application == null) { + throw new InvalidOperationException("The Entity Framework stores can only be used " + + "with the built-in OpenIddictApplication entity."); + } + + var authorization = FindGenericBaseType(builder.AuthorizationType, typeof(OpenIddictAuthorization<,>)); + if (authorization == null) { + throw new InvalidOperationException("The Entity Framework stores can only be used " + + "with the built-in OpenIddictAuthorization entity."); + } + + var scope = FindGenericBaseType(builder.ScopeType, typeof(OpenIddictScope<>)); + if (scope == null) { + throw new InvalidOperationException("The Entity Framework stores can only be used " + + "with the built-in OpenIddictScope entity."); + } + + var token = FindGenericBaseType(builder.TokenType, typeof(OpenIddictToken<>)); + if (token == null) { + throw new InvalidOperationException("The Entity Framework stores can only be used " + + "with the built-in OpenIddictToken entity."); + } + // Register the application store in the DI container. builder.Services.TryAddScoped( typeof(IOpenIddictApplicationStore<>).MakeGenericType(builder.ApplicationType), @@ -52,7 +65,7 @@ namespace Microsoft.Extensions.DependencyInjection { /* TApplication: */ builder.ApplicationType, /* TToken: */ builder.TokenType, /* TContext: */ typeof(TContext), - /* TKey: */ typeof(TKey))); + /* TKey: */ application.GenericTypeArguments[0])); // Register the authorization store in the DI container. builder.Services.TryAddScoped( @@ -61,7 +74,7 @@ namespace Microsoft.Extensions.DependencyInjection { /* TAuthorization: */ builder.AuthorizationType, /* TToken: */ builder.TokenType, /* TContext: */ typeof(TContext), - /* TKey: */ typeof(TKey))); + /* TKey: */ authorization.GenericTypeArguments[0])); // Register the scope store in the DI container. builder.Services.TryAddScoped( @@ -69,7 +82,7 @@ namespace Microsoft.Extensions.DependencyInjection { typeof(OpenIddictScopeStore<,,>).MakeGenericType( /* TScope: */ builder.ScopeType, /* TContext: */ typeof(TContext), - /* TKey: */ typeof(TKey))); + /* TKey: */ scope.GenericTypeArguments[0])); // Register the token store in the DI container. builder.Services.TryAddScoped( @@ -78,7 +91,7 @@ namespace Microsoft.Extensions.DependencyInjection { /* TToken: */ builder.TokenType, /* TAuthorization: */ builder.AuthorizationType, /* TContext: */ typeof(TContext), - /* TKey: */ typeof(TKey))); + /* TKey: */ token.GenericTypeArguments[0])); return builder; } @@ -100,8 +113,8 @@ namespace Microsoft.Extensions.DependencyInjection { /// The builder used to configure the Entity Framework context. /// The Entity Framework context builder. public static DbContextOptionsBuilder UseOpenIddict([NotNull] this DbContextOptionsBuilder builder) where TKey : IEquatable { - return builder.UseOpenIddict>, - OpenIddictAuthorization>, + return builder.UseOpenIddict, + OpenIddictAuthorization, OpenIddictScope, OpenIddictToken, TKey>(); } @@ -145,8 +158,8 @@ namespace Microsoft.Extensions.DependencyInjection { /// The builder used to configure the Entity Framework context. /// The Entity Framework context builder. public static ModelBuilder UseOpenIddict([NotNull] this ModelBuilder builder) where TKey : IEquatable { - return builder.UseOpenIddict>, - OpenIddictAuthorization>, + return builder.UseOpenIddict, + OpenIddictAuthorization, OpenIddictScope, OpenIddictToken, TKey>(); } @@ -214,5 +227,15 @@ namespace Microsoft.Extensions.DependencyInjection { return builder; } + + private static TypeInfo FindGenericBaseType(Type type, Type definition) { + for (var candidate = type.GetTypeInfo(); candidate != null; candidate = candidate.BaseType?.GetTypeInfo()) { + if (candidate.IsGenericType && candidate.GetGenericTypeDefinition() == definition) { + return candidate; + } + } + + return null; + } } } \ No newline at end of file diff --git a/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs index 120431e8..5f8eed6d 100644 --- a/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs @@ -22,8 +22,8 @@ namespace OpenIddict.Core.Tests { } [Theory] - [InlineData(typeof(OpenIddictApplicationManager>>))] - [InlineData(typeof(OpenIddictAuthorizationManager>>))] + [InlineData(typeof(OpenIddictApplicationManager>))] + [InlineData(typeof(OpenIddictAuthorizationManager>))] [InlineData(typeof(OpenIddictScopeManager>))] [InlineData(typeof(OpenIddictTokenManager>))] public void AddOpenIddict_KeyTypeCanBeOverriden(Type type) { diff --git a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs index 2ac867e1..4995f34f 100644 --- a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs @@ -6,38 +6,120 @@ using Xunit; namespace OpenIddict.EntityFrameworkCore.Tests { public class OpenIddictExtensionsTests { + [Fact] + public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidApplicationEntity() { + // Arrange + var services = new ServiceCollection(); + + // Act and assert + var exception = Assert.Throws(delegate { + services.AddOpenIddict() + .AddEntityFrameworkCoreStores(); + }); + + Assert.Equal("The Entity Framework stores can only be used " + + "with the built-in OpenIddictApplication entity.", exception.Message); + } + + [Fact] + public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidAuthorizationEntity() { + // Arrange + var services = new ServiceCollection(); + + // Act and assert + var exception = Assert.Throws(delegate { + services.AddOpenIddict() + .AddEntityFrameworkCoreStores(); + }); + + Assert.Equal("The Entity Framework stores can only be used " + + "with the built-in OpenIddictAuthorization entity.", exception.Message); + } + + [Fact] + public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidScopeEntity() { + // Arrange + var services = new ServiceCollection(); + + // Act and assert + var exception = Assert.Throws(delegate { + services.AddOpenIddict() + .AddEntityFrameworkCoreStores(); + }); + + Assert.Equal("The Entity Framework stores can only be used " + + "with the built-in OpenIddictScope entity.", exception.Message); + } + + [Fact] + public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidTokenEntity() { + // Arrange + var services = new ServiceCollection(); + + // Act and assert + var exception = Assert.Throws(delegate { + services.AddOpenIddict() + .AddEntityFrameworkCoreStores(); + }); + + Assert.Equal("The Entity Framework stores can only be used " + + "with the built-in OpenIddictToken entity.", exception.Message); + } + [Theory] - [InlineData(typeof(OpenIddictApplicationStore>, OpenIddictToken, DbContext, Guid>))] - [InlineData(typeof(OpenIddictAuthorizationStore>, OpenIddictToken, DbContext, Guid>))] - [InlineData(typeof(OpenIddictScopeStore, DbContext, Guid>))] - [InlineData(typeof(OpenIddictTokenStore, OpenIddictAuthorization>, DbContext, Guid>))] + [InlineData(typeof(OpenIddictApplicationStore))] + [InlineData(typeof(OpenIddictAuthorizationStore))] + [InlineData(typeof(OpenIddictScopeStore))] + [InlineData(typeof(OpenIddictTokenStore))] public void AddEntityFrameworkCoreStores_RegistersEntityFrameworkStores(Type type) { // Arrange var services = new ServiceCollection(); + // Act + services.AddOpenIddict() + .AddEntityFrameworkCoreStores(); + + // Assert + Assert.Contains(services, service => service.ImplementationType == type); + } + + [Theory] + [InlineData(typeof(OpenIddictApplicationStore, OpenIddictToken, DbContext, Guid>))] + [InlineData(typeof(OpenIddictAuthorizationStore, OpenIddictToken, DbContext, Guid>))] + [InlineData(typeof(OpenIddictScopeStore, DbContext, Guid>))] + [InlineData(typeof(OpenIddictTokenStore, OpenIddictAuthorization, DbContext, Guid>))] + public void AddEntityFrameworkCoreStores_KeyTypeIsInferredFromEntities(Type type) { + // Arrange + var services = new ServiceCollection(); + // Act services.AddOpenIddict() - .AddEntityFrameworkCoreStores(); + .AddEntityFrameworkCoreStores(); // Assert Assert.Contains(services, service => service.ImplementationType == type); } [Theory] - [InlineData(typeof(OpenIddictApplicationStore))] - [InlineData(typeof(OpenIddictAuthorizationStore))] - [InlineData(typeof(OpenIddictScopeStore))] - [InlineData(typeof(OpenIddictTokenStore))] - public void AddEntityFrameworkCoreStores_KeyTypeDefaultsToString(Type type) { + [InlineData(typeof(OpenIddictApplicationStore))] + [InlineData(typeof(OpenIddictAuthorizationStore))] + [InlineData(typeof(OpenIddictScopeStore))] + [InlineData(typeof(OpenIddictTokenStore))] + public void AddEntityFrameworkCoreStores_DefaultEntitiesCanBeReplaced(Type type) { // Arrange var services = new ServiceCollection(); // Act - services.AddOpenIddict() + services.AddOpenIddict() .AddEntityFrameworkCoreStores(); // Assert Assert.Contains(services, service => service.ImplementationType == type); } + + public class CustomApplication : OpenIddictApplication { } + public class CustomAuthorization : OpenIddictAuthorization { } + public class CustomScope : OpenIddictScope { } + public class CustomToken : OpenIddictToken { } } }