2017-12-22 12:29:56 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Linq;
|
2018-10-17 15:09:59 +02:00
|
|
|
|
using Umbraco.Core.Configuration;
|
2017-12-22 12:29:56 +01:00
|
|
|
|
using Umbraco.Core.Logging;
|
|
|
|
|
|
using Umbraco.Core.Migrations;
|
|
|
|
|
|
using Umbraco.Core.Scoping;
|
|
|
|
|
|
using Umbraco.Core.Persistence;
|
2017-12-28 09:06:33 +01:00
|
|
|
|
using Umbraco.Core.Persistence.Dtos;
|
2017-12-22 12:29:56 +01:00
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Core.Services.Implement
|
|
|
|
|
|
{
|
|
|
|
|
|
internal class KeyValueService : IKeyValueService
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly object _initialock = new object();
|
|
|
|
|
|
private readonly IScopeProvider _scopeProvider;
|
|
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
|
private bool _initialized;
|
|
|
|
|
|
|
|
|
|
|
|
public KeyValueService(IScopeProvider scopeProvider, ILogger logger)
|
|
|
|
|
|
{
|
|
|
|
|
|
_scopeProvider = scopeProvider;
|
|
|
|
|
|
_logger = logger;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void EnsureInitialized()
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (_initialock)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_initialized) return;
|
|
|
|
|
|
Initialize();
|
|
|
|
|
|
_initialized = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Initialize()
|
|
|
|
|
|
{
|
2018-10-17 15:09:59 +02:00
|
|
|
|
// 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)
|
|
|
|
|
|
|
2018-11-15 08:47:47 +01:00
|
|
|
|
if (UmbracoVersion.LocalVersion.Major >= 8)
|
2018-10-17 15:09:59 +02:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// else we are upgrading from 7, we can assume that the locks table
|
|
|
|
|
|
// exists, but we need to create everything for key/value
|
2017-12-26 11:35:21 +01:00
|
|
|
|
|
2017-12-22 12:29:56 +01:00
|
|
|
|
using (var scope = _scopeProvider.CreateScope())
|
|
|
|
|
|
{
|
2017-12-26 11:35:21 +01:00
|
|
|
|
var context = new MigrationContext(scope.Database, _logger);
|
2018-03-28 12:50:15 +11:00
|
|
|
|
var initMigration = new InitializeMigration(context);
|
|
|
|
|
|
initMigration.Migrate();
|
2017-12-22 12:29:56 +01:00
|
|
|
|
scope.Complete();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-28 12:50:15 +11:00
|
|
|
|
/// <summary>
|
2018-03-28 12:40:52 +02:00
|
|
|
|
/// A custom migration that executes standalone during the Initialize phase of this service.
|
2018-03-28 12:50:15 +11:00
|
|
|
|
/// </summary>
|
2018-10-17 15:09:59 +02:00
|
|
|
|
internal class InitializeMigration : MigrationBase
|
2018-03-28 12:50:15 +11:00
|
|
|
|
{
|
2018-03-28 12:40:52 +02:00
|
|
|
|
public InitializeMigration(IMigrationContext context)
|
|
|
|
|
|
: base(context)
|
|
|
|
|
|
{ }
|
2018-03-28 12:50:15 +11:00
|
|
|
|
|
|
|
|
|
|
public override void Migrate()
|
|
|
|
|
|
{
|
2018-10-17 15:09:59 +02:00
|
|
|
|
// 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<KeyValueService>("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<KeyValueDto>().Do();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void DropLockTableIdentity()
|
|
|
|
|
|
{
|
|
|
|
|
|
// one cannot simply drop an identity, that requires a bit of work
|
|
|
|
|
|
|
2018-03-28 12:50:15 +11:00
|
|
|
|
// 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();
|
2018-10-17 15:09:59 +02:00
|
|
|
|
|
2018-03-28 12:50:15 +11:00
|
|
|
|
// 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();
|
2018-10-17 15:09:59 +02:00
|
|
|
|
|
2018-03-28 12:50:15 +11:00
|
|
|
|
// 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();
|
2018-10-17 15:09:59 +02:00
|
|
|
|
|
2018-03-28 12:50:15 +11:00
|
|
|
|
// drop the temp. id column
|
|
|
|
|
|
Delete.Column("nid").FromTable(Constants.DatabaseSchema.Tables.Lock).Do();
|
2018-10-17 15:09:59 +02:00
|
|
|
|
|
2018-03-28 12:50:15 +11:00
|
|
|
|
// complete the primary key
|
|
|
|
|
|
Alter.Table(Constants.DatabaseSchema.Tables.Lock).AlterColumn("id").AsInt32().NotNullable().PrimaryKey("PK_umbracoLock").Do();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-21 11:32:07 +01:00
|
|
|
|
/// <inheritdoc />
|
2017-12-22 12:29:56 +01:00
|
|
|
|
public string GetValue(string key)
|
|
|
|
|
|
{
|
|
|
|
|
|
EnsureInitialized();
|
|
|
|
|
|
|
|
|
|
|
|
using (var scope = _scopeProvider.CreateScope())
|
|
|
|
|
|
{
|
|
|
|
|
|
var sql = scope.SqlContext.Sql().Select<KeyValueDto>().From<KeyValueDto>().Where<KeyValueDto>(x => x.Key == key);
|
|
|
|
|
|
var dto = scope.Database.Fetch<KeyValueDto>(sql).FirstOrDefault();
|
|
|
|
|
|
scope.Complete();
|
|
|
|
|
|
return dto?.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-21 11:32:07 +01:00
|
|
|
|
/// <inheritdoc />
|
2017-12-22 12:29:56 +01:00
|
|
|
|
public void SetValue(string key, string value)
|
|
|
|
|
|
{
|
|
|
|
|
|
EnsureInitialized();
|
|
|
|
|
|
|
|
|
|
|
|
using (var scope = _scopeProvider.CreateScope())
|
|
|
|
|
|
{
|
|
|
|
|
|
scope.WriteLock(Constants.Locks.KeyValues);
|
|
|
|
|
|
|
|
|
|
|
|
var sql = scope.SqlContext.Sql().Select<KeyValueDto>().From<KeyValueDto>().Where<KeyValueDto>(x => x.Key == key);
|
|
|
|
|
|
var dto = scope.Database.Fetch<KeyValueDto>(sql).FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
|
|
if (dto == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
dto = new KeyValueDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Key = key,
|
|
|
|
|
|
Value = value,
|
|
|
|
|
|
Updated = DateTime.Now
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
scope.Database.Insert(dto);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
dto.Value = value;
|
|
|
|
|
|
dto.Updated = DateTime.Now;
|
|
|
|
|
|
scope.Database.Update(dto);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
scope.Complete();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-21 11:32:07 +01:00
|
|
|
|
/// <inheritdoc />
|
2017-12-22 12:29:56 +01:00
|
|
|
|
public void SetValue(string key, string originValue, string newValue)
|
2018-03-21 11:32:07 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (!TrySetValue(key, originValue, newValue))
|
|
|
|
|
|
throw new InvalidOperationException("Could not set the value.");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public bool TrySetValue(string key, string originValue, string newValue)
|
2017-12-22 12:29:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
EnsureInitialized();
|
|
|
|
|
|
|
|
|
|
|
|
using (var scope = _scopeProvider.CreateScope())
|
|
|
|
|
|
{
|
|
|
|
|
|
scope.WriteLock(Constants.Locks.KeyValues);
|
|
|
|
|
|
|
|
|
|
|
|
var sql = scope.SqlContext.Sql().Select<KeyValueDto>().From<KeyValueDto>().Where<KeyValueDto>(x => x.Key == key);
|
|
|
|
|
|
var dto = scope.Database.Fetch<KeyValueDto>(sql).FirstOrDefault();
|
|
|
|
|
|
|
2018-03-21 11:32:07 +01:00
|
|
|
|
if (dto == null || dto.Value != originValue)
|
|
|
|
|
|
return false;
|
2017-12-22 12:29:56 +01:00
|
|
|
|
|
|
|
|
|
|
dto.Value = newValue;
|
|
|
|
|
|
dto.Updated = DateTime.Now;
|
|
|
|
|
|
scope.Database.Update(dto);
|
|
|
|
|
|
|
|
|
|
|
|
scope.Complete();
|
|
|
|
|
|
}
|
2018-03-21 11:32:07 +01:00
|
|
|
|
|
|
|
|
|
|
return true;
|
2017-12-22 12:29:56 +01:00
|
|
|
|
}
|
2018-10-17 15:09:59 +02:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets a value directly from the database, no scope, nothing.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>Used by <see cref="Runtime.CoreRuntime"/> to determine the runtime state.</remarks>
|
|
|
|
|
|
internal static string GetValue(IUmbracoDatabase database, string key)
|
|
|
|
|
|
{
|
|
|
|
|
|
// not 8 yet = no key/value table, no value
|
2018-11-15 08:47:47 +01:00
|
|
|
|
if (UmbracoVersion.LocalVersion.Major < 8)
|
2018-10-17 15:09:59 +02:00
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
|
|
var sql = database.SqlContext.Sql()
|
|
|
|
|
|
.Select<KeyValueDto>()
|
|
|
|
|
|
.From<KeyValueDto>()
|
|
|
|
|
|
.Where<KeyValueDto>(x => x.Key == key);
|
|
|
|
|
|
return database.FirstOrDefault<KeyValueDto>(sql)?.Value;
|
|
|
|
|
|
}
|
2017-12-22 12:29:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|