diff --git a/src/Umbraco.Core/Models/IKeyValue.cs b/src/Umbraco.Core/Models/IKeyValue.cs
new file mode 100644
index 0000000000..6025d4d37b
--- /dev/null
+++ b/src/Umbraco.Core/Models/IKeyValue.cs
@@ -0,0 +1,12 @@
+using System;
+using Umbraco.Core.Models.Entities;
+
+namespace Umbraco.Core.Models
+{
+ public interface IKeyValue : IEntity
+ {
+ string Identifier { get; set; }
+
+ string Value { get; set; }
+ }
+}
diff --git a/src/Umbraco.Core/Models/KeyValue.cs b/src/Umbraco.Core/Models/KeyValue.cs
new file mode 100644
index 0000000000..2d47fcbfb3
--- /dev/null
+++ b/src/Umbraco.Core/Models/KeyValue.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Runtime.Serialization;
+using Umbraco.Core.Models.Entities;
+
+namespace Umbraco.Core.Models
+{
+ ///
+ /// Implements .
+ ///
+ [Serializable]
+ [DataContract(IsReference = true)]
+ public class KeyValue : EntityBase, IKeyValue, IEntity
+ {
+ private string _identifier;
+ private string _value;
+
+ ///
+ public string Identifier
+ {
+ get => _identifier;
+ set => SetPropertyValueAndDetectChanges(value, ref _identifier, nameof(Identifier));
+ }
+
+ ///
+ public string Value
+ {
+ get => _value;
+ set => SetPropertyValueAndDetectChanges(value, ref _value, nameof(Value));
+ }
+
+ bool IEntity.HasIdentity => !string.IsNullOrEmpty(Identifier);
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/Repositories/IKeyValueRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IKeyValueRepository.cs
new file mode 100644
index 0000000000..0eb2b27eb0
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Repositories/IKeyValueRepository.cs
@@ -0,0 +1,8 @@
+using Umbraco.Core.Models;
+
+namespace Umbraco.Core.Persistence.Repositories
+{
+ public interface IKeyValueRepository : IReadRepository, IWriteRepository
+ {
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/Repositories/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IUserRepository.cs
index 29ae581459..0d1a8764ae 100644
--- a/src/Umbraco.Core/Persistence/Repositories/IUserRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/IUserRepository.cs
@@ -20,8 +20,24 @@ namespace Umbraco.Core.Persistence.Repositories
///
///
///
+ [Obsolete("This method will be removed in future versions. Please use ExistsByUserName instead.")]
bool Exists(string username);
+ ///
+ /// Checks if a user with the username exists
+ ///
+ ///
+ ///
+ bool ExistsByUserName(string username);
+
+
+ ///
+ /// Checks if a user with the login exists
+ ///
+ ///
+ ///
+ bool ExistsByLogin(string login);
+
///
/// Gets a list of objects associated with a given group
///
diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs
index f3c95b07f9..c9bdc6d924 100644
--- a/src/Umbraco.Core/Services/ServiceContext.cs
+++ b/src/Umbraco.Core/Services/ServiceContext.cs
@@ -32,12 +32,13 @@ namespace Umbraco.Core.Services
private readonly Lazy _externalLoginService;
private readonly Lazy _redirectUrlService;
private readonly Lazy _consentService;
+ private readonly Lazy _keyValueService;
private readonly Lazy _contentTypeBaseServiceProvider;
///
/// Initializes a new instance of the class with lazy services.
///
- public ServiceContext(Lazy publicAccessService, Lazy domainService, Lazy auditService, Lazy localizedTextService, Lazy tagService, Lazy contentService, Lazy userService, Lazy memberService, Lazy mediaService, Lazy contentTypeService, Lazy mediaTypeService, Lazy dataTypeService, Lazy fileService, Lazy localizationService, Lazy packagingService, Lazy serverRegistrationService, Lazy entityService, Lazy relationService, Lazy macroService, Lazy memberTypeService, Lazy memberGroupService, Lazy notificationService, Lazy externalLoginService, Lazy redirectUrlService, Lazy consentService, Lazy contentTypeBaseServiceProvider)
+ public ServiceContext(Lazy publicAccessService, Lazy domainService, Lazy auditService, Lazy localizedTextService, Lazy tagService, Lazy contentService, Lazy userService, Lazy memberService, Lazy mediaService, Lazy contentTypeService, Lazy mediaTypeService, Lazy dataTypeService, Lazy fileService, Lazy localizationService, Lazy packagingService, Lazy serverRegistrationService, Lazy entityService, Lazy relationService, Lazy macroService, Lazy memberTypeService, Lazy memberGroupService, Lazy notificationService, Lazy externalLoginService, Lazy redirectUrlService, Lazy consentService, Lazy keyValueService, Lazy contentTypeBaseServiceProvider)
{
_publicAccessService = publicAccessService;
_domainService = domainService;
@@ -64,6 +65,7 @@ namespace Umbraco.Core.Services
_externalLoginService = externalLoginService;
_redirectUrlService = redirectUrlService;
_consentService = consentService;
+ _keyValueService = keyValueService;
_contentTypeBaseServiceProvider = contentTypeBaseServiceProvider;
}
@@ -99,6 +101,7 @@ namespace Umbraco.Core.Services
IServerRegistrationService serverRegistrationService = null,
IRedirectUrlService redirectUrlService = null,
IConsentService consentService = null,
+ IKeyValueService keyValueService = null,
IContentTypeBaseServiceProvider contentTypeBaseServiceProvider = null)
{
Lazy Lazy(T service) => service == null ? null : new Lazy(() => service);
@@ -129,6 +132,7 @@ namespace Umbraco.Core.Services
Lazy(externalLoginService),
Lazy(redirectUrlService),
Lazy(consentService),
+ Lazy(keyValueService),
Lazy(contentTypeBaseServiceProvider)
);
}
@@ -258,6 +262,11 @@ namespace Umbraco.Core.Services
///
public IConsentService ConsentService => _consentService.Value;
+ ///
+ /// Gets the KeyValueService.
+ ///
+ public IKeyValueService KeyValueService => _keyValueService.Value;
+
///
/// Gets the ContentTypeServiceBaseFactory.
///
diff --git a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Repositories.cs b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Repositories.cs
index 61ff4ecb6d..9a23255cc4 100644
--- a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Repositories.cs
+++ b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Repositories.cs
@@ -47,6 +47,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
composition.RegisterUnique();
composition.RegisterUnique();
composition.RegisterUnique();
+ composition.RegisterUnique();
composition.RegisterUnique();
composition.RegisterUnique();
diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs
index da6574670b..b4328c973d 100644
--- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs
@@ -344,7 +344,7 @@ namespace Umbraco.Core.Migrations.Install
var stateValueKey = upgrader.StateValueKey;
var finalState = upgrader.Plan.FinalState;
- _database.Insert(Constants.DatabaseSchema.Tables.KeyValue, "key", false, new KeyValueDto { Key = stateValueKey, Value = finalState, Updated = DateTime.Now });
+ _database.Insert(Constants.DatabaseSchema.Tables.KeyValue, "key", false, new KeyValueDto { Key = stateValueKey, Value = finalState, UpdateDate = DateTime.Now });
}
}
}
diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/KeyValueDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/KeyValueDto.cs
index b74039c388..5ead6d0d26 100644
--- a/src/Umbraco.Infrastructure/Persistence/Dtos/KeyValueDto.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Dtos/KeyValueDto.cs
@@ -21,6 +21,6 @@ namespace Umbraco.Core.Persistence.Dtos
[Column("updated")]
[Constraint(Default = SystemMethods.CurrentDateTime)]
- public DateTime Updated { get; set; }
+ public DateTime UpdateDate { get; set; }
}
}
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs
new file mode 100644
index 0000000000..3c2da4d5b7
--- /dev/null
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NPoco;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models;
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.Dtos;
+using Umbraco.Core.Persistence.Querying;
+using Umbraco.Core.Scoping;
+
+namespace Umbraco.Core.Persistence.Repositories.Implement
+{
+ internal class KeyValueRepository : NPocoRepositoryBase, IKeyValueRepository
+ {
+ public KeyValueRepository(IScopeAccessor scopeAccessor, ILogger logger)
+ : base(scopeAccessor, AppCaches.NoCache, logger)
+ { }
+
+ #region Overrides of IReadWriteQueryRepository
+
+ public override void Save(IKeyValue entity)
+ {
+ if (Get(entity.Identifier) == null)
+ PersistNewItem(entity);
+ else
+ PersistUpdatedItem(entity);
+ }
+
+ #endregion
+
+ #region Overrides of NPocoRepositoryBase
+
+ protected override Guid NodeObjectTypeId => throw new NotSupportedException();
+
+ protected override Sql GetBaseQuery(bool isCount)
+ {
+ var sql = SqlContext.Sql();
+
+ sql = isCount
+ ? sql.SelectCount()
+ : sql.Select();
+
+ sql
+ .From();
+
+ return sql;
+ }
+
+ protected override string GetBaseWhereClause()
+ {
+ return Constants.DatabaseSchema.Tables.KeyValue + ".key = @id";
+ }
+
+ protected override IEnumerable GetDeleteClauses()
+ {
+ return Enumerable.Empty();
+ }
+
+ protected override IKeyValue PerformGet(string id)
+ {
+ var sql = GetBaseQuery(false).Where(x => x.Key == id);
+ var dto = Database.Fetch(sql).FirstOrDefault();
+ return dto == null ? null : Map(dto);
+ }
+
+ protected override IEnumerable PerformGetAll(params string[] ids)
+ {
+ var sql = GetBaseQuery(false).WhereIn(x => x.Key, ids);
+ var dtos = Database.Fetch(sql);
+ return dtos.WhereNotNull().Select(Map);
+ }
+
+ protected override IEnumerable PerformGetByQuery(IQuery query)
+ {
+ throw new NotSupportedException();
+ }
+
+ protected override void PersistNewItem(IKeyValue entity)
+ {
+ var dto = Map(entity);
+ Database.Insert(dto);
+ }
+
+ protected override void PersistUpdatedItem(IKeyValue entity)
+ {
+ var dto = Map(entity);
+ Database.Update(dto);
+ }
+
+ private static KeyValueDto Map(IKeyValue keyValue)
+ {
+ if (keyValue == null) return null;
+
+ return new KeyValueDto
+ {
+ Key = keyValue.Identifier,
+ Value = keyValue.Value,
+ UpdateDate = keyValue.UpdateDate,
+ };
+ }
+
+ private static IKeyValue Map(KeyValueDto dto)
+ {
+ if (dto == null) return null;
+
+ return new KeyValue
+ {
+ Identifier = dto.Key,
+ Value = dto.Value,
+ UpdateDate = dto.UpdateDate,
+ };
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
index e8397ba22a..6985bf78da 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
@@ -132,7 +132,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
///
/// This method is backed by an cache
///
- public void Save(TEntity entity)
+ public virtual void Save(TEntity entity)
{
if (entity.HasIdentity == false)
CachePolicy.Create(entity, PersistNewItem);
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs
index 2f1b6c5814..c8de108db8 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs
@@ -636,6 +636,11 @@ ORDER BY colName";
}
public bool Exists(string username)
+ {
+ return ExistsByUserName(username);
+ }
+
+ public bool ExistsByUserName(string username)
{
var sql = SqlContext.Sql()
.SelectCount()
@@ -645,6 +650,16 @@ ORDER BY colName";
return Database.ExecuteScalar(sql) > 0;
}
+ public bool ExistsByLogin(string login)
+ {
+ var sql = SqlContext.Sql()
+ .SelectCount()
+ .From()
+ .Where(x => x.Login == login);
+
+ return Database.ExecuteScalar(sql) > 0;
+ }
+
///
/// Gets a list of objects associated with a given group
///
diff --git a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseExtensions.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseExtensions.cs
index 249dd3dc73..4e5c3a113a 100644
--- a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseExtensions.cs
+++ b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Persistence
{
@@ -10,5 +11,20 @@ namespace Umbraco.Core.Persistence
if (asDatabase == null) throw new Exception("oops: database.");
return asDatabase;
}
+
+ ///
+ /// Gets a key/value directly from the database, no scope, nothing.
+ ///
+ /// Used by to determine the runtime state.
+ public static string GetFromKeyValueTable(this IUmbracoDatabase database, string key)
+ {
+ if (database is null) return null;
+
+ var sql = database.SqlContext.Sql()
+ .Select()
+ .From()
+ .Where(x => x.Key == key);
+ return database.FirstOrDefault(sql)?.Value;
+ }
}
}
diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs
index 1bf954e4c1..aa9ccb958f 100644
--- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs
+++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs
@@ -1,5 +1,4 @@
using System;
-using System.IO;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Composing.CompositionExtensions;
@@ -7,9 +6,8 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Grid;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Dashboards;
-using Umbraco.Core.Hosting;
using Umbraco.Core.Dictionary;
-using Umbraco.Core.IO;
+using Umbraco.Core.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
using Umbraco.Core.Migrations;
@@ -25,14 +23,14 @@ using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
-using Umbraco.Web.Models.PublishedContent;
-using Umbraco.Web.PublishedCache;
using Umbraco.Web;
-using Umbraco.Web.Migrations.PostMigrations;
using Umbraco.Web.Install;
-using Umbraco.Web.Trees;
+using Umbraco.Web.Migrations.PostMigrations;
+using Umbraco.Web.Models.PublishedContent;
using Umbraco.Web.PropertyEditors;
+using Umbraco.Web.PublishedCache;
using Umbraco.Web.Services;
+using Umbraco.Web.Trees;
using IntegerValidator = Umbraco.Core.PropertyEditors.Validators.IntegerValidator;
namespace Umbraco.Core.Runtime
diff --git a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs
index 99f38f0486..85d915b433 100644
--- a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs
+++ b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs
@@ -324,7 +324,7 @@ namespace Umbraco.Core.Runtime
{
Key = MainDomKey,
Value = id,
- Updated = DateTime.Now
+ UpdateDate = DateTime.Now
});
}
diff --git a/src/Umbraco.Infrastructure/RuntimeState.cs b/src/Umbraco.Infrastructure/RuntimeState.cs
index e0099c5e7e..d6f674f028 100644
--- a/src/Umbraco.Infrastructure/RuntimeState.cs
+++ b/src/Umbraco.Infrastructure/RuntimeState.cs
@@ -9,7 +9,7 @@ using Umbraco.Core.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Core.Migrations.Upgrade;
using Umbraco.Core.Persistence;
-using Umbraco.Core.Services.Implement;
+using Umbraco.Core.Persistence.Repositories.Implement;
using Umbraco.Core.Sync;
namespace Umbraco.Core
@@ -225,7 +225,7 @@ namespace Umbraco.Core
// no scope, no service - just directly accessing the database
using (var database = databaseFactory.CreateDatabase())
{
- CurrentMigrationState = KeyValueService.GetValue(database, stateValueKey);
+ CurrentMigrationState = database.GetFromKeyValueTable(stateValueKey);
FinalMigrationState = upgrader.Plan.FinalState;
}
diff --git a/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs b/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs
index d5d8e66525..12ca34b020 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs
@@ -1,168 +1,55 @@
using System;
-using System.Linq;
-using Umbraco.Core.Composing;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Migrations;
+using Umbraco.Core.Models;
+using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Scoping;
-using Umbraco.Core.Persistence;
-using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Services.Implement
{
internal class KeyValueService : IKeyValueService
{
- private readonly object _initialock = new object();
private readonly IScopeProvider _scopeProvider;
- private readonly ILogger _logger;
- private readonly IUmbracoVersion _umbracoVersion;
- private bool _initialized;
+ private readonly IKeyValueRepository _repository;
- public KeyValueService(IScopeProvider scopeProvider, ILogger logger, IUmbracoVersion umbracoVersion)
+ public KeyValueService(IScopeProvider scopeProvider, IKeyValueRepository repository)
{
_scopeProvider = scopeProvider;
- _logger = logger;
- _umbracoVersion = umbracoVersion;
- }
-
- private void EnsureInitialized()
- {
- lock (_initialock)
- {
- if (_initialized) return;
- Initialize();
- }
- }
-
- private void Initialize()
- {
- // the key/value service is entirely self-managed, because it is used by the
- // upgrader and anything we might change need to happen before everything else
-
- // if already running 8, either following an upgrade or an install,
- // then everything should be ok (the table should exist, etc)
-
- if (_umbracoVersion.LocalVersion != null && _umbracoVersion.LocalVersion.Major >= 8)
- {
- _initialized = true;
- return;
- }
-
- // else we are upgrading from 7, we can assume that the locks table
- // exists, but we need to create everything for key/value
-
- using (var scope = _scopeProvider.CreateScope())
- {
- var context = new MigrationContext(scope.Database, _logger);
- var initMigration = new InitializeMigration(context);
- initMigration.Migrate();
- scope.Complete();
- }
-
- // but don't assume we are initializing
- // we are upgrading from v7 and if anything goes wrong,
- // the table and everything will be rolled back
- }
-
- ///
- /// A custom migration that executes standalone during the Initialize phase of this service.
- ///
- internal class InitializeMigration : MigrationBase
- {
- public InitializeMigration(IMigrationContext context)
- : base(context)
- { }
-
- public override void Migrate()
- {
- // as long as we are still running 7 this migration will be invoked,
- // but due to multiple restarts during upgrades, maybe the table
- // exists already
- if (TableExists(Constants.DatabaseSchema.Tables.KeyValue))
- return;
-
- Logger.Info("Creating KeyValue structure.");
-
- // the locks table was initially created with an identity (auto-increment) primary key,
- // but we don't want this, especially as we are about to insert a new row into the table,
- // so here we drop that identity
- DropLockTableIdentity();
-
- // insert the lock object for key/value
- Insert.IntoTable(Constants.DatabaseSchema.Tables.Lock).Row(new {id = Constants.Locks.KeyValues, name = "KeyValues", value = 1}).Do();
-
- // create the key-value table
- Create.Table().Do();
- }
-
- private void DropLockTableIdentity()
- {
- // one cannot simply drop an identity, that requires a bit of work
-
- // create a temp. id column and copy values
- Alter.Table(Constants.DatabaseSchema.Tables.Lock).AddColumn("nid").AsInt32().Nullable().Do();
- Execute.Sql("update umbracoLock set nid = id").Do();
-
- // drop the id column entirely (cannot just drop identity)
- Delete.PrimaryKey("PK_umbracoLock").FromTable(Constants.DatabaseSchema.Tables.Lock).Do();
- Delete.Column("id").FromTable(Constants.DatabaseSchema.Tables.Lock).Do();
-
- // recreate the id column without identity and copy values
- Alter.Table(Constants.DatabaseSchema.Tables.Lock).AddColumn("id").AsInt32().Nullable().Do();
- Execute.Sql("update umbracoLock set id = nid").Do();
-
- // drop the temp. id column
- Delete.Column("nid").FromTable(Constants.DatabaseSchema.Tables.Lock).Do();
-
- // complete the primary key
- Alter.Table(Constants.DatabaseSchema.Tables.Lock).AlterColumn("id").AsInt32().NotNullable().PrimaryKey("PK_umbracoLock").Do();
- }
+ _repository = repository;
}
///
public string GetValue(string key)
{
- EnsureInitialized();
-
using (var scope = _scopeProvider.CreateScope())
{
- var sql = scope.SqlContext.Sql().Select().From().Where(x => x.Key == key);
- var dto = scope.Database.Fetch(sql).FirstOrDefault();
- scope.Complete();
- return dto?.Value;
+ return _repository.Get(key)?.Value;
}
}
///
public void SetValue(string key, string value)
{
- EnsureInitialized();
-
using (var scope = _scopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.KeyValues);
- var sql = scope.SqlContext.Sql().Select().From().Where(x => x.Key == key);
- var dto = scope.Database.Fetch(sql).FirstOrDefault();
-
- if (dto == null)
+ var keyValue = _repository.Get(key);
+ if (keyValue == null)
{
- dto = new KeyValueDto
+ keyValue = new KeyValue
{
- Key = key,
+ Identifier = key,
Value = value,
- Updated = DateTime.Now
+ UpdateDate = DateTime.Now,
};
-
- scope.Database.Insert(dto);
}
else
{
- dto.Value = value;
- dto.Updated = DateTime.Now;
- scope.Database.Update(dto);
+ keyValue.Value = value;
+ keyValue.UpdateDate = DateTime.Now;
}
+ _repository.Save(keyValue);
+
scope.Complete();
}
}
@@ -175,43 +62,26 @@ namespace Umbraco.Core.Services.Implement
}
///
- public bool TrySetValue(string key, string originValue, string newValue)
+ public bool TrySetValue(string key, string originalValue, string newValue)
{
- EnsureInitialized();
-
using (var scope = _scopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.KeyValues);
- var sql = scope.SqlContext.Sql().Select().From().Where(x => x.Key == key);
- var dto = scope.Database.Fetch(sql).FirstOrDefault();
-
- if (dto == null || dto.Value != originValue)
+ var keyValue = _repository.Get(key);
+ if (keyValue == null || keyValue.Value != originalValue)
+ {
return false;
+ }
- dto.Value = newValue;
- dto.Updated = DateTime.Now;
- scope.Database.Update(dto);
+ keyValue.Value = newValue;
+ keyValue.UpdateDate = DateTime.Now;
+ _repository.Save(keyValue);
scope.Complete();
}
return true;
}
-
- ///
- /// Gets a value directly from the database, no scope, nothing.
- ///
- /// Used by to determine the runtime state.
- internal static string GetValue(IUmbracoDatabase database, string key)
- {
- if (database is null) return null;
-
- var sql = database.SqlContext.Sql()
- .Select()
- .From()
- .Where(x => x.Key == key);
- return database.FirstOrDefault(sql)?.Value;
- }
}
}
diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs
index 4876772c86..fc0dd8a554 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs
@@ -756,7 +756,6 @@ namespace Umbraco.Core.Services.Implement
throw new ArgumentOutOfRangeException(nameof(matchType)); // causes rollback // causes rollback
}
- // TODO: Since this is by property value, we need a GetByPropertyQuery on the repo!
// TODO: Since this is by property value, we need a GetByPropertyQuery on the repo!
return _memberRepository.Get(query);
}
@@ -1196,7 +1195,6 @@ namespace Umbraco.Core.Services.Implement
{
scope.WriteLock(Constants.Locks.MemberTree);
- // TODO: What about content that has the contenttype as part of its composition?
// TODO: What about content that has the contenttype as part of its composition?
var query = Query().Where(x => x.ContentTypeId == memberTypeId);
diff --git a/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs b/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs
index ab9ea64292..f2af6cf424 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs
@@ -124,7 +124,6 @@ namespace Umbraco.Core.Services.Implement
}
else
{
- //If they are both the same already then there's nothing to update, exit
//If they are both the same already then there's nothing to update, exit
return OperationResult.Attempt.Succeed(evtMsgs, entry);
}
diff --git a/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs b/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs
index 97bf76e672..6a092b159f 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs
@@ -72,7 +72,6 @@ namespace Umbraco.Core.Services.Implement
_serverRegistrationRepository.Save(server);
_serverRegistrationRepository.DeactiveStaleServers(staleTimeout); // triggers a cache reload
- // reload - cheap, cached
// reload - cheap, cached
// default role is single server, but if registrations contain more
diff --git a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs
index f8982fc538..e23f7539c2 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs
@@ -46,7 +46,7 @@ namespace Umbraco.Core.Services.Implement
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
- return _userRepository.Exists(username);
+ return _userRepository.ExistsByUserName(username);
}
}
@@ -109,9 +109,9 @@ namespace Umbraco.Core.Services.Implement
User user;
using (var scope = ScopeProvider.CreateScope())
{
- var loginExists = scope.Database.ExecuteScalar("SELECT COUNT(id) FROM umbracoUser WHERE userLogin = @Login", new { Login = username }) != 0;
+ var loginExists = _userRepository.ExistsByLogin(username);
if (loginExists)
- throw new ArgumentException("Login already exists"); // causes rollback // causes rollback
+ throw new ArgumentException("Login already exists"); // causes rollback
user = new User(_globalSettings)
{
@@ -340,7 +340,6 @@ namespace Umbraco.Core.Services.Implement
_userRepository.Save(user);
- //Now we have to check for backwards compat hacks
//Now we have to check for backwards compat hacks
var explicitUser = user as User;
if (explicitUser != null && explicitUser.GroupsToSave.Count > 0)
@@ -358,7 +357,6 @@ namespace Umbraco.Core.Services.Implement
scope.Events.Dispatch(SavedUser, this, saveEventArgs);
}
- //commit the whole lot in one go
//commit the whole lot in one go
scope.Complete();
}
diff --git a/src/Umbraco.Tests/Persistence/Repositories/KeyValueRepositoryTests.cs b/src/Umbraco.Tests/Persistence/Repositories/KeyValueRepositoryTests.cs
new file mode 100644
index 0000000000..50ebc7ff5d
--- /dev/null
+++ b/src/Umbraco.Tests/Persistence/Repositories/KeyValueRepositoryTests.cs
@@ -0,0 +1,72 @@
+using System;
+using NUnit.Framework;
+using Umbraco.Core.Models;
+using Umbraco.Core.Persistence.Repositories;
+using Umbraco.Core.Persistence.Repositories.Implement;
+using Umbraco.Core.Scoping;
+using Umbraco.Tests.TestHelpers;
+using Umbraco.Tests.Testing;
+
+namespace Umbraco.Tests.Persistence.Repositories
+{
+ [TestFixture]
+ [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
+ public class KeyValueRepositoryTests : TestWithDatabaseBase
+ {
+ [Test]
+ public void CanSetAndGet()
+ {
+ var provider = TestObjects.GetScopeProvider(Logger);
+
+ // Insert new key/value
+ using (var scope = provider.CreateScope())
+ {
+ var keyValue = new KeyValue
+ {
+ Identifier = "foo",
+ Value = "bar",
+ UpdateDate = DateTime.Now,
+ };
+ var repo = CreateRepository(provider);
+ repo.Save(keyValue);
+ scope.Complete();
+ }
+
+ // Retrieve key/value
+ using (var scope = provider.CreateScope())
+ {
+ var repo = CreateRepository(provider);
+ var keyValue = repo.Get("foo");
+ scope.Complete();
+
+ Assert.AreEqual("bar", keyValue.Value);
+ }
+
+ // Update value
+ using (var scope = provider.CreateScope())
+ {
+ var repo = CreateRepository(provider);
+ var keyValue = repo.Get("foo");
+ keyValue.Value = "buzz";
+ keyValue.UpdateDate = DateTime.Now;
+ repo.Save(keyValue);
+ scope.Complete();
+ }
+
+ // Retrieve key/value again
+ using (var scope = provider.CreateScope())
+ {
+ var repo = CreateRepository(provider);
+ var keyValue = repo.Get("foo");
+ scope.Complete();
+
+ Assert.AreEqual("buzz", keyValue.Value);
+ }
+ }
+
+ private IKeyValueRepository CreateRepository(IScopeProvider provider)
+ {
+ return new KeyValueRepository((IScopeAccessor) provider, Logger);
+ }
+ }
+}
diff --git a/src/Umbraco.Tests/Services/KeyValueServiceTests.cs b/src/Umbraco.Tests/Services/KeyValueServiceTests.cs
new file mode 100644
index 0000000000..5ab29258f5
--- /dev/null
+++ b/src/Umbraco.Tests/Services/KeyValueServiceTests.cs
@@ -0,0 +1,93 @@
+using System.Threading;
+using NUnit.Framework;
+using NUnit.Framework.Internal;
+using Umbraco.Core.Services;
+using Umbraco.Tests.Testing;
+
+namespace Umbraco.Tests.Services
+{
+ ///
+ /// Tests covering methods in the KeyValueService class.
+ /// This is more of an integration test as it involves multiple layers
+ /// as well as configuration.
+ ///
+ [TestFixture]
+ [Apartment(ApartmentState.STA)]
+ [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
+ public class KeyValueServiceTests : TestWithSomeContentBase
+ {
+ [Test]
+ public void GetValue_ForMissingKey_ReturnsNull()
+ {
+ // Arrange
+ var keyValueService = ServiceContext.KeyValueService;
+
+ // Act
+ var value = keyValueService.GetValue("foo");
+
+ // Assert
+ Assert.IsNull(value);
+ }
+
+ [Test]
+ public void GetValue_ForExistingKey_ReturnsValue()
+ {
+ // Arrange
+ var keyValueService = ServiceContext.KeyValueService;
+ keyValueService.SetValue("foo", "bar");
+
+ // Act
+ var value = keyValueService.GetValue("foo");
+
+ // Assert
+ Assert.AreEqual("bar", value);
+ }
+
+ [Test]
+ public void SetValue_ForExistingKey_SavesValue()
+ {
+ // Arrange
+ var keyValueService = ServiceContext.KeyValueService;
+ keyValueService.SetValue("foo", "bar");
+
+ // Act
+ keyValueService.SetValue("foo", "buzz");
+ var value = keyValueService.GetValue("foo");
+
+ // Assert
+ Assert.AreEqual("buzz", value);
+ }
+
+ [Test]
+ public void TrySetValue_ForExistingKeyWithProvidedValue_ReturnsTrueAndSetsValue()
+ {
+ // Arrange
+ var keyValueService = ServiceContext.KeyValueService;
+ keyValueService.SetValue("foo", "bar");
+
+ // Act
+ var result = keyValueService.TrySetValue("foo", "bar", "buzz");
+ var value = keyValueService.GetValue("foo");
+
+ // Assert
+ Assert.IsTrue(result);
+ Assert.AreEqual("buzz", value);
+ }
+
+ [Test]
+ public void TrySetValue_ForExistingKeyWithoutProvidedValue_ReturnsFalseAndDoesNotSetValue()
+ {
+ // Arrange
+ var keyValueService = ServiceContext.KeyValueService;
+ keyValueService.SetValue("foo", "bar");
+
+ // Act
+ var result = keyValueService.TrySetValue("foo", "bang", "buzz");
+ var value = keyValueService.GetValue("foo");
+
+ // Assert
+ Assert.IsFalse(result);
+ Assert.AreEqual("bar", value);
+ }
+ }
+}
diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs
index 4609d8a59c..94fac6c158 100644
--- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs
+++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs
@@ -193,6 +193,7 @@ namespace Umbraco.Tests.TestHelpers
var tagService = GetLazyService(factory, c => new TagService(scopeProvider, logger, eventMessagesFactory, GetRepo(c)));
var redirectUrlService = GetLazyService(factory, c => new RedirectUrlService(scopeProvider, logger, eventMessagesFactory, GetRepo(c)));
var consentService = GetLazyService(factory, c => new ConsentService(scopeProvider, logger, eventMessagesFactory, GetRepo(c)));
+ var keyValueService = GetLazyService(factory, c => new KeyValueService(scopeProvider, GetRepo(c)));
var contentTypeServiceBaseFactory = GetLazyService(factory, c => new ContentTypeBaseServiceProvider(factory.GetInstance(),factory.GetInstance(),factory.GetInstance()));
return new ServiceContext(
@@ -221,6 +222,7 @@ namespace Umbraco.Tests.TestHelpers
externalLoginService,
redirectUrlService,
consentService,
+ keyValueService,
contentTypeServiceBaseFactory);
}
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index a1d086f94d..bb1a907843 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -148,6 +148,8 @@
+
+