From 7520ffa0efd04c18630c2612855cb590a94cc69a Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Wed, 29 Aug 2018 14:33:57 -0700 Subject: [PATCH] Fix up service scoping in the EF Core xml repository and update package version to 2.2 --- DataProtection.sln | 22 +- .../DataProtectionKeyContext.cs | 21 -- .../EntityFrameworkCoreSample.csproj} | 0 .../Program.cs | 25 ++- samples/Redis/Program.cs | 2 +- .../ConfigureKeyManagementOptions.cs | 22 -- .../DataProtectionKey.cs | 5 - ...tyFrameworkCoreDataProtectionExtensions.cs | 25 +-- .../EntityFrameworkCoreXmlRepository.cs | 56 +++-- ....DataProtection.EntityFrameworkCore.csproj | 11 +- .../baseline.netcore.json | 203 ++++++++++++++++++ .../DataProtectionEntityFrameworkTests.cs | 51 ++--- ...oreDataProtectionBuilderExtensionsTests.cs | 2 +- 13 files changed, 310 insertions(+), 135 deletions(-) delete mode 100644 samples/EntityFrameworkCore/DataProtectionKeyContext.cs rename samples/{EntityFrameworkCore/EntityFrameworkCore.csproj => EntityFrameworkCoreSample/EntityFrameworkCoreSample.csproj} (100%) rename samples/{EntityFrameworkCore => EntityFrameworkCoreSample}/Program.cs (56%) delete mode 100644 src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/ConfigureKeyManagementOptions.cs create mode 100644 src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/baseline.netcore.json diff --git a/DataProtection.sln b/DataProtection.sln index 7b22058b82..7fb7eb0592 100644 --- a/DataProtection.sln +++ b/DataProtection.sln @@ -79,10 +79,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataPr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test", "test\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj", "{06728BF2-C5EB-44C7-9F30-14FAA5649E14}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkCore", "samples\EntityFrameworkCore\EntityFrameworkCore.csproj", "{E837A2E3-FC93-494C-8689-5AF9C6802AD7}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore", "src\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj", "{3E4CA7FE-741B-4C78-A775-220E0E3C1B03}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkCoreSample", "samples\EntityFrameworkCoreSample\EntityFrameworkCoreSample.csproj", "{22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -279,14 +279,6 @@ Global {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|Any CPU.Build.0 = Release|Any CPU {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|x86.ActiveCfg = Release|Any CPU {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|x86.Build.0 = Release|Any CPU - {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Debug|x86.ActiveCfg = Debug|Any CPU - {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Debug|x86.Build.0 = Debug|Any CPU - {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Release|Any CPU.Build.0 = Release|Any CPU - {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Release|x86.ActiveCfg = Release|Any CPU - {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Release|x86.Build.0 = Release|Any CPU {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|Any CPU.Build.0 = Debug|Any CPU {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -295,6 +287,14 @@ Global {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|Any CPU.Build.0 = Release|Any CPU {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|x86.ActiveCfg = Release|Any CPU {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|x86.Build.0 = Release|Any CPU + {22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Debug|x86.ActiveCfg = Debug|Any CPU + {22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Debug|x86.Build.0 = Debug|Any CPU + {22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Release|Any CPU.Build.0 = Release|Any CPU + {22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Release|x86.ActiveCfg = Release|Any CPU + {22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -324,8 +324,8 @@ Global {295E8539-5450-4764-B3F5-51F968628022} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2} {C85ED942-8121-453F-8308-9DB730843B63} = {60336AB3-948D-4D15-A5FB-F32A2B91E814} {06728BF2-C5EB-44C7-9F30-14FAA5649E14} = {60336AB3-948D-4D15-A5FB-F32A2B91E814} - {E837A2E3-FC93-494C-8689-5AF9C6802AD7} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2} {3E4CA7FE-741B-4C78-A775-220E0E3C1B03} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA} + {22BA4EAB-641E-42B2-BB37-9C3BCFD99F76} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DD305D75-BD1B-43AE-BF04-869DA6A0858F} diff --git a/samples/EntityFrameworkCore/DataProtectionKeyContext.cs b/samples/EntityFrameworkCore/DataProtectionKeyContext.cs deleted file mode 100644 index a84031ae50..0000000000 --- a/samples/EntityFrameworkCore/DataProtectionKeyContext.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; - -namespace EntityFrameworkCore -{ - class DataProtectionKeyContext : DbContext, IDataProtectionKeyContext - { - public DataProtectionKeyContext(DbContextOptions options) : base(options) { } - public DbSet DataProtectionKeys { get; set; } - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - base.OnConfiguring(optionsBuilder); - optionsBuilder.UseInMemoryDatabase("DataProtection_EntityFrameworkCore"); - optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); - optionsBuilder.EnableSensitiveDataLogging(); - } - } -} diff --git a/samples/EntityFrameworkCore/EntityFrameworkCore.csproj b/samples/EntityFrameworkCoreSample/EntityFrameworkCoreSample.csproj similarity index 100% rename from samples/EntityFrameworkCore/EntityFrameworkCore.csproj rename to samples/EntityFrameworkCoreSample/EntityFrameworkCoreSample.csproj diff --git a/samples/EntityFrameworkCore/Program.cs b/samples/EntityFrameworkCoreSample/Program.cs similarity index 56% rename from samples/EntityFrameworkCore/Program.cs rename to samples/EntityFrameworkCoreSample/Program.cs index 9e8a0d5ee1..d4e978a7b8 100644 --- a/samples/EntityFrameworkCore/Program.cs +++ b/samples/EntityFrameworkCoreSample/Program.cs @@ -1,27 +1,35 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using System; -namespace EntityFrameworkCore +namespace EntityFrameworkCoreSample { class Program { static void Main(string[] args) { // Configure - using (var services = new ServiceCollection() + var services = new ServiceCollection() .AddLogging(o => o.AddConsole().SetMinimumLevel(LogLevel.Debug)) - .AddDbContext() + .AddDbContext(o => + { + o.UseInMemoryDatabase("DataProtection_EntityFrameworkCore"); + o.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + o.EnableSensitiveDataLogging(); + }) .AddDataProtection() .PersistKeysToDbContext() .SetDefaultKeyLifetime(TimeSpan.FromDays(7)) .Services - .BuildServiceProvider(validateScopes: true)) + .BuildServiceProvider(validateScopes: true); + + using(services) { // Run a sample payload var protector = services.GetDataProtector("sample-purpose"); @@ -30,4 +38,11 @@ namespace EntityFrameworkCore } } } + + class DataProtectionKeyContext : DbContext, IDataProtectionKeyContext + { + public DataProtectionKeyContext(DbContextOptions options) : base(options) { } + + public DbSet DataProtectionKeys { get; set; } + } } diff --git a/samples/Redis/Program.cs b/samples/Redis/Program.cs index f8f213cfad..aa1cdf5164 100644 --- a/samples/Redis/Program.cs +++ b/samples/Redis/Program.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using StackExchange.Redis; -namespace Redis +namespace RedisSample { public class Program { diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/ConfigureKeyManagementOptions.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/ConfigureKeyManagementOptions.cs deleted file mode 100644 index 246aa3c1e5..0000000000 --- a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/ConfigureKeyManagementOptions.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.DataProtection.KeyManagement; -using Microsoft.AspNetCore.DataProtection.Repositories; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using System; - -namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore -{ - internal class ConfigureKeyManagementOptions : IConfigureOptions - { - private readonly IServiceProvider _serviceProvider; - - public ConfigureKeyManagementOptions(IServiceProvider serviceProvider) - => _serviceProvider = serviceProvider; - - public void Configure(KeyManagementOptions options) - => options.XmlRepository = _serviceProvider.CreateScope().ServiceProvider.GetRequiredService(); - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/DataProtectionKey.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/DataProtectionKey.cs index b13a9fbd60..c236d5cb89 100644 --- a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/DataProtectionKey.cs +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/DataProtectionKey.cs @@ -1,9 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore { /// @@ -14,8 +11,6 @@ namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore /// /// The entity identifier of the . /// - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } /// diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreDataProtectionExtensions.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreDataProtectionExtensions.cs index a3577f0e6d..ff24b58eb9 100644 --- a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreDataProtectionExtensions.cs +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreDataProtectionExtensions.cs @@ -1,15 +1,15 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Microsoft.AspNetCore.DataProtection.KeyManagement; -using Microsoft.AspNetCore.DataProtection.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; -using System; -namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore +namespace Microsoft.AspNetCore.DataProtection { /// /// Extension method class for configuring instances of @@ -24,22 +24,15 @@ namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore public static IDataProtectionBuilder PersistKeysToDbContext(this IDataProtectionBuilder builder) where TContext : DbContext, IDataProtectionKeyContext { - var services = builder.Services; - - services.AddScoped>( - provider => new Func( - () => provider.CreateScope().ServiceProvider.GetService())); - - services.AddScoped(provider => + builder.Services.AddSingleton>(services => { - var scope = provider.CreateScope(); - return new EntityFrameworkCoreXmlRepository( - contextFactory: scope.ServiceProvider.GetRequiredService>(), - loggerFactory: scope.ServiceProvider.GetService()); + var loggerFactory = services.GetService() ?? NullLoggerFactory.Instance; + return new ConfigureOptions(options => + { + options.XmlRepository = new EntityFrameworkCoreXmlRepository(services, loggerFactory); + }); }); - services.AddTransient, ConfigureKeyManagementOptions>(); - return builder; } } diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreXmlRepository.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreXmlRepository.cs index 6720c400b8..62250cf3ef 100644 --- a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreXmlRepository.cs +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreXmlRepository.cs @@ -1,13 +1,14 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.AspNetCore.DataProtection.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; +using Microsoft.AspNetCore.DataProtection.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore { @@ -17,40 +18,51 @@ namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore public class EntityFrameworkCoreXmlRepository : IXmlRepository where TContext : DbContext, IDataProtectionKeyContext { - private readonly ILoggerFactory _loggerFactory; - private readonly Func _contextFactory; - - private ILogger> _logger => _loggerFactory?.CreateLogger>(); - - private TContext _context => _contextFactory?.Invoke(); + private readonly IServiceProvider _services; + private readonly ILogger _logger; /// /// Creates a new instance of the . /// - /// The factory method that creates a context to store instances of + /// /// The . - public EntityFrameworkCoreXmlRepository(Func contextFactory, ILoggerFactory loggerFactory = null) + public EntityFrameworkCoreXmlRepository(IServiceProvider services, ILoggerFactory loggerFactory) { - _contextFactory = contextFactory ?? throw new ArgumentNullException(nameof(contextFactory)); - _loggerFactory = loggerFactory; + if (loggerFactory == null) + { + throw new ArgumentNullException(nameof(loggerFactory)); + } + + _logger = loggerFactory.CreateLogger>(); + _services = services ?? throw new ArgumentNullException(nameof(services)); } /// public virtual IReadOnlyCollection GetAllElements() - => _context?.Set()?.AsNoTracking().Select(key => TryParseKeyXml(key.Xml)).ToList().AsReadOnly(); + { + using (var scope = _services.CreateScope()) + { + var context = scope.ServiceProvider.GetRequiredService(); + return context.DataProtectionKeys.AsNoTracking().Select(key => TryParseKeyXml(key.Xml)).ToList().AsReadOnly(); + } + } /// public void StoreElement(XElement element, string friendlyName) { - var newKey = new DataProtectionKey() + using (var scope = _services.CreateScope()) { - FriendlyName = friendlyName, - Xml = element.ToString(SaveOptions.DisableFormatting) - }; - var context = _context; - context?.Set()?.Add(newKey); - _logger?.LogSavingKeyToDbContext(friendlyName, typeof(TContext).Name); - context?.SaveChanges(); + var context = scope.ServiceProvider.GetRequiredService(); + var newKey = new DataProtectionKey() + { + FriendlyName = friendlyName, + Xml = element.ToString(SaveOptions.DisableFormatting) + }; + + context.DataProtectionKeys.Add(newKey); + _logger.LogSavingKeyToDbContext(friendlyName, typeof(TContext).Name); + context.SaveChanges(); + } } private XElement TryParseKeyXml(string xml) diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj index 966b1bfc78..e1715d94f2 100644 --- a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj @@ -1,16 +1,11 @@  - EntityFramworkCore storage support as key store. - $(ExperimentalVersionPrefix) - $(ExperimentalVersionSuffix) - false - $(ExperimentalPackageVersion) + Support for storing keys using Entity Framework Core. netstandard2.0 true true aspnetcore;dataprotection;entityframeworkcore - false @@ -21,4 +16,8 @@ + + + + diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/baseline.netcore.json b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/baseline.netcore.json new file mode 100644 index 0000000000..9a9a7ebc1c --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/baseline.netcore.json @@ -0,0 +1,203 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.AspNetCore.DataProtection.EntityFrameworkCoreDataProtectionExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "PersistKeysToDbContext", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TContext", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext", + "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.IDataProtectionKeyContext" + ] + } + ] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Id", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Id", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_FriendlyName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_FriendlyName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Xml", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Xml", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.EntityFrameworkCoreXmlRepository", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetAllElements", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IReadOnlyCollection", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "StoreElement", + "Parameters": [ + { + "Name": "element", + "Type": "System.Xml.Linq.XElement" + }, + { + "Name": "friendlyName", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "services", + "Type": "System.IServiceProvider" + }, + { + "Name": "loggerFactory", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TContext", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext", + "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.IDataProtectionKeyContext" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.IDataProtectionKeyContext", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_DataProtectionKeys", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionEntityFrameworkTests.cs b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionEntityFrameworkTests.cs index 31034c7f4c..c298d8e64f 100644 --- a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionEntityFrameworkTests.cs +++ b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionEntityFrameworkTests.cs @@ -1,12 +1,14 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; -using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test; -using Microsoft.EntityFrameworkCore; using System; using System.Linq; using System.Xml.Linq; +using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; +using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace Microsoft.AspNetCore.DataProtection @@ -16,7 +18,7 @@ namespace Microsoft.AspNetCore.DataProtection [Fact] public void CreateRepository_ThrowsIf_ContextIsNull() { - Assert.Throws(() => new EntityFrameworkCoreXmlRepository(null)); + Assert.Throws(() => new EntityFrameworkCoreXmlRepository(null, null)); } [Fact] @@ -25,13 +27,13 @@ namespace Microsoft.AspNetCore.DataProtection var element = XElement.Parse(""); var friendlyName = "Element1"; var key = new DataProtectionKey() { FriendlyName = friendlyName, Xml = element.ToString() }; - using (var context = BuildDataProtectionKeyContext(nameof(StoreElement_PersistsData))) - { - var service = new EntityFrameworkCoreXmlRepository(() => context); + + var services = GetServices(nameof(StoreElement_PersistsData)); + var service = new EntityFrameworkCoreXmlRepository(services, NullLoggerFactory.Instance); service.StoreElement(element, friendlyName); - } + // Use a separate instance of the context to verify correct data was saved to database - using (var context = BuildDataProtectionKeyContext(nameof(StoreElement_PersistsData))) + using (var context = services.CreateScope().ServiceProvider.GetRequiredService< DataProtectionKeyContext>()) { Assert.Equal(1, context.DataProtectionKeys.Count()); Assert.Equal(key.FriendlyName, context.DataProtectionKeys.Single()?.FriendlyName); @@ -44,25 +46,24 @@ namespace Microsoft.AspNetCore.DataProtection { var element1 = XElement.Parse(""); var element2 = XElement.Parse(""); - using (var context = BuildDataProtectionKeyContext(nameof(GetAllElements_ReturnsAllElements))) - { - var service = new EntityFrameworkCoreXmlRepository(() => context); - service.StoreElement(element1, "element1"); - service.StoreElement(element2, "element2"); - } + + var services = GetServices(nameof(GetAllElements_ReturnsAllElements)); + var service1 = CreateRepo(services); + service1.StoreElement(element1, "element1"); + service1.StoreElement(element2, "element2"); + // Use a separate instance of the context to verify correct data was saved to database - using (var context = BuildDataProtectionKeyContext(nameof(GetAllElements_ReturnsAllElements))) - { - var service = new EntityFrameworkCoreXmlRepository(() => context); - var elements = service.GetAllElements(); - Assert.Equal(2, elements.Count); - } + var service2 = CreateRepo(services); + var elements = service2.GetAllElements(); + Assert.Equal(2, elements.Count); } - private DbContextOptions BuildDbContextOptions(string databaseName) - => new DbContextOptionsBuilder().UseInMemoryDatabase(databaseName: databaseName).Options; + private EntityFrameworkCoreXmlRepository CreateRepo(IServiceProvider services) + => new EntityFrameworkCoreXmlRepository(services, NullLoggerFactory.Instance); - private DataProtectionKeyContext BuildDataProtectionKeyContext(string databaseName) - => new DataProtectionKeyContext(BuildDbContextOptions(databaseName)); + private IServiceProvider GetServices(string dbName) + => new ServiceCollection() + .AddDbContext(o => o.UseInMemoryDatabase(dbName)) + .BuildServiceProvider(validateScopes: true); } } diff --git a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/EntityFrameworkCoreDataProtectionBuilderExtensionsTests.cs b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/EntityFrameworkCoreDataProtectionBuilderExtensionsTests.cs index d04ccdde88..55b67d98e3 100644 --- a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/EntityFrameworkCoreDataProtectionBuilderExtensionsTests.cs +++ b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/EntityFrameworkCoreDataProtectionBuilderExtensionsTests.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test .AddDbContext() .AddDataProtection() .PersistKeysToDbContext(); - var serviceProvider = serviceCollection.BuildServiceProvider(); + var serviceProvider = serviceCollection.BuildServiceProvider(validateScopes: true); var keyManagementOptions = serviceProvider.GetRequiredService>(); Assert.IsType>(keyManagementOptions.Value.XmlRepository); }