234 lines
7.8 KiB
C#
234 lines
7.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using Umbraco.Core.Models;
|
|
using Umbraco.Core.Persistence.UnitOfWork;
|
|
|
|
namespace Umbraco.Core.Services
|
|
{
|
|
public class IdkMap
|
|
{
|
|
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
|
|
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
|
|
private readonly Dictionary<Id2KeyCompositeKey, Guid> _id2Key = new Dictionary<Id2KeyCompositeKey, Guid>();
|
|
private readonly Dictionary<Key2IdCompositeKey, int> _key2Id = new Dictionary<Key2IdCompositeKey, int>();
|
|
|
|
public IdkMap(IDatabaseUnitOfWorkProvider uowProvider)
|
|
{
|
|
_uowProvider = uowProvider;
|
|
}
|
|
|
|
// note - no need for uow, scope would be enough, but a pain to wire
|
|
// note - for pure read-only we might want to *not* enforce a transaction?
|
|
|
|
public Attempt<int> GetIdForKey(Guid key, UmbracoObjectTypes umbracoObjectType)
|
|
{
|
|
var compositeKey = new Key2IdCompositeKey() { Key = key, UmbracoObjectType = umbracoObjectType };
|
|
int id;
|
|
try
|
|
{
|
|
_locker.EnterReadLock();
|
|
if (_key2Id.TryGetValue(compositeKey, out id)) return Attempt.Succeed(id);
|
|
}
|
|
finally
|
|
{
|
|
if (_locker.IsReadLockHeld)
|
|
_locker.ExitReadLock();
|
|
}
|
|
|
|
int? val;
|
|
using (var uow = _uowProvider.GetUnitOfWork())
|
|
{
|
|
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id AND nodeObjectType=@nodeObjectType",
|
|
new { id = key, nodeObjectType = GetNodeObjectTypeGuid(umbracoObjectType) });
|
|
uow.Commit();
|
|
}
|
|
|
|
if (val == null) return Attempt<int>.Fail();
|
|
id = val.Value;
|
|
|
|
try
|
|
{
|
|
var reversedCompositeKey = new Id2KeyCompositeKey { Id = id, UmbracoObjectType = umbracoObjectType };
|
|
_locker.EnterWriteLock();
|
|
_id2Key[reversedCompositeKey] = key;
|
|
_key2Id[compositeKey] = id;
|
|
}
|
|
finally
|
|
{
|
|
if (_locker.IsWriteLockHeld)
|
|
_locker.ExitWriteLock();
|
|
}
|
|
|
|
return Attempt.Succeed(id);
|
|
}
|
|
|
|
public Attempt<int> GetIdForUdi(Udi udi)
|
|
{
|
|
var guidUdi = udi as GuidUdi;
|
|
if (guidUdi == null)
|
|
return Attempt<int>.Fail();
|
|
|
|
var umbracoType = Constants.UdiEntityType.ToUmbracoObjectType(guidUdi.EntityType);
|
|
return GetIdForKey(guidUdi.Guid, umbracoType);
|
|
}
|
|
|
|
public Attempt<Guid> GetKeyForId(int id, UmbracoObjectTypes umbracoObjectType)
|
|
{
|
|
var compositeKey = new Id2KeyCompositeKey() {Id = id, UmbracoObjectType = umbracoObjectType};
|
|
Guid key;
|
|
try
|
|
{
|
|
_locker.EnterReadLock();
|
|
if (_id2Key.TryGetValue(compositeKey, out key)) return Attempt.Succeed(key);
|
|
}
|
|
finally
|
|
{
|
|
if (_locker.IsReadLockHeld)
|
|
_locker.ExitReadLock();
|
|
}
|
|
|
|
Guid? val;
|
|
using (var uow = _uowProvider.GetUnitOfWork())
|
|
{
|
|
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id AND nodeObjectType=@nodeObjectType",
|
|
new { id, nodeObjectType = GetNodeObjectTypeGuid(umbracoObjectType) });
|
|
uow.Commit();
|
|
}
|
|
|
|
if (val == null) return Attempt<Guid>.Fail();
|
|
key = val.Value;
|
|
|
|
try
|
|
{
|
|
var reversedCompositeKey = new Key2IdCompositeKey {Key = key, UmbracoObjectType = umbracoObjectType};
|
|
_locker.EnterWriteLock();
|
|
_id2Key[compositeKey] = key;
|
|
_key2Id[reversedCompositeKey] = id;
|
|
}
|
|
finally
|
|
{
|
|
if (_locker.IsWriteLockHeld)
|
|
_locker.ExitWriteLock();
|
|
}
|
|
|
|
return Attempt.Succeed(key);
|
|
}
|
|
|
|
private static Guid GetNodeObjectTypeGuid(UmbracoObjectTypes umbracoObjectType)
|
|
{
|
|
var guid = umbracoObjectType.GetGuid();
|
|
if (guid == Guid.Empty)
|
|
throw new NotSupportedException("Unsupported object type (" + umbracoObjectType + ").");
|
|
return guid;
|
|
}
|
|
|
|
public void ClearCache()
|
|
{
|
|
try
|
|
{
|
|
_locker.EnterWriteLock();
|
|
_id2Key.Clear();
|
|
_key2Id.Clear();
|
|
}
|
|
finally
|
|
{
|
|
if (_locker.IsWriteLockHeld)
|
|
_locker.ExitWriteLock();
|
|
}
|
|
}
|
|
|
|
public void ClearCache(int id)
|
|
{
|
|
try
|
|
{
|
|
_locker.EnterWriteLock();
|
|
var match = _id2Key.Keys.SingleOrDefault(x => x.Id == id);
|
|
if (match == null) return;
|
|
var key = _id2Key[match];
|
|
var reversedCompositeKey = new Key2IdCompositeKey { Key = key, UmbracoObjectType = match.UmbracoObjectType };
|
|
_id2Key.Remove(match);
|
|
_key2Id.Remove(reversedCompositeKey);
|
|
}
|
|
finally
|
|
{
|
|
if (_locker.IsWriteLockHeld)
|
|
_locker.ExitWriteLock();
|
|
}
|
|
}
|
|
|
|
public void ClearCache(Guid key)
|
|
{
|
|
try
|
|
{
|
|
_locker.EnterWriteLock();
|
|
var match = _key2Id.Keys.SingleOrDefault(x => x.Key == key);
|
|
if (match == null) return;
|
|
var id = _key2Id[match];
|
|
var reversedCompositeKey = new Id2KeyCompositeKey {Id = id, UmbracoObjectType = match.UmbracoObjectType};
|
|
_id2Key.Remove(reversedCompositeKey);
|
|
_key2Id.Remove(match);
|
|
}
|
|
finally
|
|
{
|
|
if (_locker.IsWriteLockHeld)
|
|
_locker.ExitWriteLock();
|
|
}
|
|
}
|
|
|
|
internal class Id2KeyCompositeKey
|
|
{
|
|
public int Id { get; set; }
|
|
public UmbracoObjectTypes UmbracoObjectType { get; set; }
|
|
|
|
protected bool Equals(Id2KeyCompositeKey other)
|
|
{
|
|
return Id == other.Id && UmbracoObjectType == other.UmbracoObjectType;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (ReferenceEquals(null, obj)) return false;
|
|
if (ReferenceEquals(this, obj)) return true;
|
|
if (obj.GetType() != this.GetType()) return false;
|
|
return Equals((Id2KeyCompositeKey) obj);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
unchecked
|
|
{
|
|
return (Id * 397) ^ (int) UmbracoObjectType;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class Key2IdCompositeKey
|
|
{
|
|
public Guid Key { get; set; }
|
|
public UmbracoObjectTypes UmbracoObjectType { get; set; }
|
|
|
|
protected bool Equals(Key2IdCompositeKey other)
|
|
{
|
|
return Key.Equals(other.Key) && UmbracoObjectType == other.UmbracoObjectType;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (ReferenceEquals(null, obj)) return false;
|
|
if (ReferenceEquals(this, obj)) return true;
|
|
if (obj.GetType() != this.GetType()) return false;
|
|
return Equals((Key2IdCompositeKey) obj);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
unchecked
|
|
{
|
|
return (Key.GetHashCode() * 397) ^ (int) UmbracoObjectType;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |