diff --git a/DataProtection.sln b/DataProtection.sln
index c08ab6a1ce..7b22058b82 100644
--- a/DataProtection.sln
+++ b/DataProtection.sln
@@ -77,6 +77,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureKeyVault", "samples\Az
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureKeyVault.Test", "test\Microsoft.AspNetCore.DataProtection.AzureKeyVault.Test\Microsoft.AspNetCore.DataProtection.AzureKeyVault.Test.csproj", "{C85ED942-8121-453F-8308-9DB730843B63}"
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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -265,6 +271,30 @@ Global
{C85ED942-8121-453F-8308-9DB730843B63}.Release|Any CPU.Build.0 = Release|Any CPU
{C85ED942-8121-453F-8308-9DB730843B63}.Release|x86.ActiveCfg = Release|Any CPU
{C85ED942-8121-453F-8308-9DB730843B63}.Release|x86.Build.0 = Release|Any CPU
+ {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|x86.Build.0 = Debug|Any CPU
+ {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {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
+ {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|x86.Build.0 = Debug|Any CPU
+ {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -293,6 +323,9 @@ Global
{4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
{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}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DD305D75-BD1B-43AE-BF04-869DA6A0858F}
diff --git a/build/dependencies.props b/build/dependencies.props
index 9798c889a4..06b0858ebe 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -8,6 +8,8 @@
2.2.0-preview1-34967
2.2.0-preview1-34967
2.3.2
+ 2.2.0-preview1-34967
+ 2.2.0-preview1-34967
2.2.0-preview1-34967
2.2.0-preview1-34967
2.2.0-preview1-34967
diff --git a/samples/EntityFrameworkCore/DataProtectionKeyContext.cs b/samples/EntityFrameworkCore/DataProtectionKeyContext.cs
new file mode 100644
index 0000000000..a84031ae50
--- /dev/null
+++ b/samples/EntityFrameworkCore/DataProtectionKeyContext.cs
@@ -0,0 +1,21 @@
+// 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/EntityFrameworkCore/EntityFrameworkCore.csproj
new file mode 100644
index 0000000000..212ec9d566
--- /dev/null
+++ b/samples/EntityFrameworkCore/EntityFrameworkCore.csproj
@@ -0,0 +1,18 @@
+
+
+
+ exe
+ net461;netcoreapp2.1
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/EntityFrameworkCore/Program.cs b/samples/EntityFrameworkCore/Program.cs
new file mode 100644
index 0000000000..9e8a0d5ee1
--- /dev/null
+++ b/samples/EntityFrameworkCore/Program.cs
@@ -0,0 +1,33 @@
+// 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;
+using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using System;
+
+namespace EntityFrameworkCore
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ // Configure
+ using (var services = new ServiceCollection()
+ .AddLogging(o => o.AddConsole().SetMinimumLevel(LogLevel.Debug))
+ .AddDbContext()
+ .AddDataProtection()
+ .PersistKeysToDbContext()
+ .SetDefaultKeyLifetime(TimeSpan.FromDays(7))
+ .Services
+ .BuildServiceProvider(validateScopes: true))
+ {
+ // Run a sample payload
+ var protector = services.GetDataProtector("sample-purpose");
+ var protectedData = protector.Protect("Hello world!");
+ Console.WriteLine(protectedData);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/ConfigureKeyManagementOptions.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/ConfigureKeyManagementOptions.cs
new file mode 100644
index 0000000000..246aa3c1e5
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/ConfigureKeyManagementOptions.cs
@@ -0,0 +1,22 @@
+// 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
new file mode 100644
index 0000000000..b13a9fbd60
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/DataProtectionKey.cs
@@ -0,0 +1,31 @@
+// 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
+{
+ ///
+ /// Code first model used by .
+ ///
+ public class DataProtectionKey
+ {
+ ///
+ /// The entity identifier of the .
+ ///
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; set; }
+
+ ///
+ /// The friendly name of the .
+ ///
+ public string FriendlyName { get; set; }
+
+ ///
+ /// The XML representation of the .
+ ///
+ public string Xml { get; set; }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreDataProtectionExtensions.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreDataProtectionExtensions.cs
new file mode 100644
index 0000000000..a3577f0e6d
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreDataProtectionExtensions.cs
@@ -0,0 +1,46 @@
+// 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.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+
+namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore
+{
+ ///
+ /// Extension method class for configuring instances of
+ ///
+ public static class EntityFrameworkCoreDataProtectionExtensions
+ {
+ ///
+ /// Configures the data protection system to persist keys to an EntityFrameworkCore datastore
+ ///
+ /// The instance to modify.
+ /// The value .
+ 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 =>
+ {
+ var scope = provider.CreateScope();
+ return new EntityFrameworkCoreXmlRepository(
+ contextFactory: scope.ServiceProvider.GetRequiredService>(),
+ loggerFactory: scope.ServiceProvider.GetService());
+ });
+
+ services.AddTransient, ConfigureKeyManagementOptions>();
+
+ return builder;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreXmlRepository.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreXmlRepository.cs
new file mode 100644
index 0000000000..6720c400b8
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreXmlRepository.cs
@@ -0,0 +1,69 @@
+// 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;
+
+namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore
+{
+ ///
+ /// An backed by an EntityFrameworkCore datastore.
+ ///
+ 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();
+
+ ///
+ /// 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)
+ {
+ _contextFactory = contextFactory ?? throw new ArgumentNullException(nameof(contextFactory));
+ _loggerFactory = loggerFactory;
+ }
+
+ ///
+ public virtual IReadOnlyCollection GetAllElements()
+ => _context?.Set()?.AsNoTracking().Select(key => TryParseKeyXml(key.Xml)).ToList().AsReadOnly();
+
+ ///
+ public void StoreElement(XElement element, string friendlyName)
+ {
+ var newKey = new DataProtectionKey()
+ {
+ FriendlyName = friendlyName,
+ Xml = element.ToString(SaveOptions.DisableFormatting)
+ };
+ var context = _context;
+ context?.Set()?.Add(newKey);
+ _logger?.LogSavingKeyToDbContext(friendlyName, typeof(TContext).Name);
+ context?.SaveChanges();
+ }
+
+ private XElement TryParseKeyXml(string xml)
+ {
+ try
+ {
+ return XElement.Parse(xml);
+ }
+ catch (Exception e)
+ {
+ _logger?.LogExceptionWhileParsingKeyXml(xml, e);
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/IDataProtectionKeyContext.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/IDataProtectionKeyContext.cs
new file mode 100644
index 0000000000..39998d2a79
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/IDataProtectionKeyContext.cs
@@ -0,0 +1,18 @@
+// 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.EntityFrameworkCore;
+
+namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore
+{
+ ///
+ /// Interface used to store instances of in a
+ ///
+ public interface IDataProtectionKeyContext
+ {
+ ///
+ /// A collection of
+ ///
+ DbSet DataProtectionKeys { get; }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/LoggingExtensions.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/LoggingExtensions.cs
new file mode 100644
index 0000000000..d0aeb09271
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/LoggingExtensions.cs
@@ -0,0 +1,31 @@
+// 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;
+
+namespace Microsoft.Extensions.Logging
+{
+ internal static class LoggingExtensions
+ {
+ private static readonly Action _anExceptionOccurredWhileParsingKeyXml;
+ private static readonly Action _savingKeyToDbContext;
+
+ static LoggingExtensions()
+ {
+ _anExceptionOccurredWhileParsingKeyXml = LoggerMessage.Define(
+ eventId: 1,
+ logLevel: LogLevel.Warning,
+ formatString: "An exception occurred while parsing the key xml '{Xml}'.");
+ _savingKeyToDbContext = LoggerMessage.Define(
+ eventId: 2,
+ logLevel: LogLevel.Debug,
+ formatString: "Saving key '{FriendlyName}' to '{DbContext}'.");
+ }
+
+ public static void LogExceptionWhileParsingKeyXml(this ILogger logger, string keyXml, Exception exception)
+ => _anExceptionOccurredWhileParsingKeyXml(logger, keyXml, exception);
+
+ public static void LogSavingKeyToDbContext(this ILogger logger, string friendlyName, string contextName)
+ => _savingKeyToDbContext(logger, friendlyName, contextName, null);
+ }
+}
diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj
new file mode 100644
index 0000000000..966b1bfc78
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj
@@ -0,0 +1,24 @@
+
+
+
+ EntityFramworkCore storage support as key store.
+ $(ExperimentalVersionPrefix)
+ $(ExperimentalVersionSuffix)
+ false
+ $(ExperimentalPackageVersion)
+ netstandard2.0
+ true
+ true
+ aspnetcore;dataprotection;entityframeworkcore
+ false
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionEntityFrameworkTests.cs b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionEntityFrameworkTests.cs
new file mode 100644
index 0000000000..31034c7f4c
--- /dev/null
+++ b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionEntityFrameworkTests.cs
@@ -0,0 +1,68 @@
+// 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 Xunit;
+
+namespace Microsoft.AspNetCore.DataProtection
+{
+ public class DataProtectionEntityFrameworkTests
+ {
+ [Fact]
+ public void CreateRepository_ThrowsIf_ContextIsNull()
+ {
+ Assert.Throws(() => new EntityFrameworkCoreXmlRepository(null));
+ }
+
+ [Fact]
+ public void StoreElement_PersistsData()
+ {
+ 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);
+ 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)))
+ {
+ Assert.Equal(1, context.DataProtectionKeys.Count());
+ Assert.Equal(key.FriendlyName, context.DataProtectionKeys.Single()?.FriendlyName);
+ Assert.Equal(key.Xml, context.DataProtectionKeys.Single()?.Xml);
+ }
+ }
+
+ [Fact]
+ public void GetAllElements_ReturnsAllElements()
+ {
+ 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");
+ }
+ // 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);
+ }
+ }
+
+ private DbContextOptions BuildDbContextOptions(string databaseName)
+ => new DbContextOptionsBuilder().UseInMemoryDatabase(databaseName: databaseName).Options;
+
+ private DataProtectionKeyContext BuildDataProtectionKeyContext(string databaseName)
+ => new DataProtectionKeyContext(BuildDbContextOptions(databaseName));
+ }
+}
diff --git a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionKeyContext.cs b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionKeyContext.cs
new file mode 100644
index 0000000000..96151de0bb
--- /dev/null
+++ b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionKeyContext.cs
@@ -0,0 +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.EntityFrameworkCore;
+
+namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test
+{
+ class DataProtectionKeyContext : DbContext, IDataProtectionKeyContext
+ {
+ public DataProtectionKeyContext(DbContextOptions options) : base(options) { }
+
+ public DbSet DataProtectionKeys { get; set; }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/EntityFrameworkCoreDataProtectionBuilderExtensionsTests.cs b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/EntityFrameworkCoreDataProtectionBuilderExtensionsTests.cs
new file mode 100644
index 0000000000..d04ccdde88
--- /dev/null
+++ b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/EntityFrameworkCoreDataProtectionBuilderExtensionsTests.cs
@@ -0,0 +1,26 @@
+// 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.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Xunit;
+
+namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test
+{
+ public class EntityFrameworkCoreDataProtectionBuilderExtensionsTests
+ {
+ [Fact]
+ public void PersistKeysToEntityFrameworkCore_UsesEntityFrameworkCoreXmlRepository()
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection
+ .AddDbContext()
+ .AddDataProtection()
+ .PersistKeysToDbContext();
+ var serviceProvider = serviceCollection.BuildServiceProvider();
+ var keyManagementOptions = serviceProvider.GetRequiredService>();
+ Assert.IsType>(keyManagementOptions.Value.XmlRepository);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj
new file mode 100644
index 0000000000..ed07b79f25
--- /dev/null
+++ b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj
@@ -0,0 +1,15 @@
+
+
+
+ $(StandardTestTfms)
+
+
+
+
+
+
+
+
+
+
+