diff --git a/src/Umbraco.Core/Extensions/TypeLoaderExtensions.cs b/src/Umbraco.Core/Extensions/TypeLoaderExtensions.cs
index e6e67f6153..b2d9618f30 100644
--- a/src/Umbraco.Core/Extensions/TypeLoaderExtensions.cs
+++ b/src/Umbraco.Core/Extensions/TypeLoaderExtensions.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Composing;
+using Umbraco.Cms.Core.Packaging;
using Umbraco.Cms.Core.PropertyEditors;
namespace Umbraco.Extensions
@@ -14,17 +15,18 @@ namespace Umbraco.Extensions
///
/// Gets all classes implementing .
///
- public static IEnumerable GetDataEditors(this TypeLoader mgr)
- {
- return mgr.GetTypes();
- }
+ public static IEnumerable GetDataEditors(this TypeLoader mgr) => mgr.GetTypes();
///
/// Gets all classes implementing ICacheRefresher.
///
- public static IEnumerable GetCacheRefreshers(this TypeLoader mgr)
- {
- return mgr.GetTypes();
- }
+ public static IEnumerable GetCacheRefreshers(this TypeLoader mgr) => mgr.GetTypes();
+
+ ///
+ /// Gest all classes implementing
+ ///
+ ///
+ ///
+ public static IEnumerable GetPackageMigrationPlans(this TypeLoader mgr) => mgr.GetTypes();
}
}
diff --git a/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs b/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs
index 114aea8f24..3688f5467a 100644
--- a/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs
+++ b/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs
@@ -1,11 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
+using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Migrations;
namespace Umbraco.Cms.Core.Packaging
{
- public abstract class PackageMigrationPlan : MigrationPlan
+ ///
+ /// Base class for package migration plans
+ ///
+ public abstract class PackageMigrationPlan : MigrationPlan, IDiscoverable
{
protected PackageMigrationPlan(string name) : base(name)
{
diff --git a/src/Umbraco.Core/Persistence/Repositories/IKeyValueRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IKeyValueRepository.cs
index e9625892d4..9d5119898c 100644
--- a/src/Umbraco.Core/Persistence/Repositories/IKeyValueRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/IKeyValueRepository.cs
@@ -1,8 +1,15 @@
-using Umbraco.Cms.Core.Models;
+using System.Collections.Generic;
+using Umbraco.Cms.Core.Models;
namespace Umbraco.Cms.Core.Persistence.Repositories
{
public interface IKeyValueRepository : IReadRepository, IWriteRepository
{
+ ///
+ /// Returns key/value pairs for all keys with the specified prefix.
+ ///
+ ///
+ ///
+ IReadOnlyDictionary Find(string keyPrefix);
}
}
diff --git a/src/Umbraco.Core/Services/IKeyValueService.cs b/src/Umbraco.Core/Services/IKeyValueService.cs
index 4c24ca19de..456733094c 100644
--- a/src/Umbraco.Core/Services/IKeyValueService.cs
+++ b/src/Umbraco.Core/Services/IKeyValueService.cs
@@ -1,4 +1,7 @@
-namespace Umbraco.Cms.Core.Services
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Umbraco.Cms.Core.Services
{
///
/// Manages the simplified key/value store.
@@ -11,6 +14,13 @@
/// Returns null if no value was found for the key.
string GetValue(string key);
+ ///
+ /// Returns key/value pairs for all keys with the specified prefix.
+ ///
+ ///
+ ///
+ IReadOnlyDictionary Find(string keyPrefix);
+
///
/// Sets a value.
///
diff --git a/src/Umbraco.Infrastructure/Persistence/Mappers/AccessMapper.cs b/src/Umbraco.Infrastructure/Persistence/Mappers/AccessMapper.cs
index ddfcae09f1..8d88b2d7df 100644
--- a/src/Umbraco.Infrastructure/Persistence/Mappers/AccessMapper.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Mappers/AccessMapper.cs
@@ -1,9 +1,10 @@
-using System;
+using System;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
namespace Umbraco.Cms.Infrastructure.Persistence.Mappers
{
+
[MapperFor(typeof(PublicAccessEntry))]
public sealed class AccessMapper : BaseMapper
{
diff --git a/src/Umbraco.Infrastructure/Persistence/Mappers/KeyValueMapper.cs b/src/Umbraco.Infrastructure/Persistence/Mappers/KeyValueMapper.cs
new file mode 100644
index 0000000000..a133d4066c
--- /dev/null
+++ b/src/Umbraco.Infrastructure/Persistence/Mappers/KeyValueMapper.cs
@@ -0,0 +1,22 @@
+using System;
+using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Infrastructure.Persistence.Dtos;
+
+namespace Umbraco.Cms.Infrastructure.Persistence.Mappers
+{
+ [MapperFor(typeof(KeyValue))]
+ [MapperFor(typeof(IKeyValue))]
+ public sealed class KeyValueMapper : BaseMapper
+ {
+ public KeyValueMapper(Lazy sqlContext, MapperConfigurationStore maps)
+ : base(sqlContext, maps)
+ { }
+
+ protected override void DefineMaps()
+ {
+ DefineMap(nameof(KeyValue.Identifier), nameof(KeyValueDto.Key));
+ DefineMap(nameof(KeyValue.Value), nameof(KeyValueDto.Value));
+ DefineMap(nameof(KeyValue.UpdateDate), nameof(KeyValueDto.UpdateDate));
+ }
+ }
+}
diff --git a/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollectionBuilder.cs b/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollectionBuilder.cs
index e797319810..8b993365a0 100644
--- a/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollectionBuilder.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollectionBuilder.cs
@@ -32,6 +32,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Mappers
Add();
Add();
Add();
+ Add();
Add();
Add();
Add();
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs
index 74988d2026..a4ffe7ee9a 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs
@@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Persistence.Querying;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
+using Umbraco.Cms.Infrastructure.Persistence.Querying;
using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
@@ -19,6 +20,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
: base(scopeAccessor, AppCaches.NoCache, logger)
{ }
+ ///
+ public IReadOnlyDictionary Find(string keyPrefix)
+ => Get(Query().Where(entity => entity.Identifier.StartsWith(keyPrefix)))
+ .ToDictionary(x => x.Identifier, x => x.Value);
+
#region Overrides of IReadWriteQueryRepository
public override void Save(IKeyValue entity)
@@ -47,15 +53,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
return sql;
}
- protected override string GetBaseWhereClause()
- {
- return Cms.Core.Constants.DatabaseSchema.Tables.KeyValue + ".key = @id";
- }
+ protected override string GetBaseWhereClause() => Core.Constants.DatabaseSchema.Tables.KeyValue + ".key = @id";
- protected override IEnumerable GetDeleteClauses()
- {
- return Enumerable.Empty();
- }
+ protected override IEnumerable GetDeleteClauses() => Enumerable.Empty();
protected override IKeyValue PerformGet(string id)
{
@@ -73,7 +73,10 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
protected override IEnumerable PerformGetByQuery(IQuery query)
{
- throw new NotSupportedException();
+ var sqlClause = GetBaseQuery(false);
+ var translator = new SqlTranslator(sqlClause, query);
+ var sql = translator.Translate();
+ return Database.Fetch(sql).Select(Map);
}
protected override void PersistNewItem(IKeyValue entity)
diff --git a/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs b/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs
index bcd35ee7f4..73bdbdc7e8 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs
@@ -1,4 +1,5 @@
-using System;
+using System;
+using System.Collections.Generic;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Scoping;
@@ -25,6 +26,15 @@ namespace Umbraco.Cms.Core.Services.Implement
}
}
+ ///
+ public IReadOnlyDictionary Find(string keyPrefix)
+ {
+ using (var scope = _scopeProvider.CreateScope(autoComplete: true))
+ {
+ return _repository.Find(keyPrefix);
+ }
+ }
+
///
public void SetValue(string key, string value)
{
diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/KeyValueServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/KeyValueServiceTests.cs
index 3437e1b286..0aa1892390 100644
--- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/KeyValueServiceTests.cs
+++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/KeyValueServiceTests.cs
@@ -1,6 +1,7 @@
-// Copyright (c) Umbraco.
+// Copyright (c) Umbraco.
// See LICENSE for more details.
+using System.Collections.Generic;
using System.Threading;
using NUnit.Framework;
using Umbraco.Cms.Core.Services;
@@ -19,6 +20,27 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services
{
private IKeyValueService KeyValueService => GetRequiredService();
+ [Test]
+ public void Can_Query_For_Key_Prefix()
+ {
+ // Arrange
+ KeyValueService.SetValue("test1", "hello1");
+ KeyValueService.SetValue("test2", "hello2");
+ KeyValueService.SetValue("test3", "hello3");
+ KeyValueService.SetValue("test4", "hello4");
+
+ // Act
+ IReadOnlyDictionary value = KeyValueService.Find("test");
+
+ // Assert
+
+ Assert.AreEqual(4, value.Count);
+ Assert.AreEqual("hello1", value["test1"]);
+ Assert.AreEqual("hello2", value["test2"]);
+ Assert.AreEqual("hello3", value["test3"]);
+ Assert.AreEqual("hello4", value["test4"]);
+ }
+
[Test]
public void GetValue_ForMissingKey_ReturnsNull()
{