Got pre-values caching properly and data types using the runtime cache provider correctly + unit tests.

This commit is contained in:
Shannon
2014-04-17 14:23:37 +10:00
parent d953966248
commit bf65bf5bab
14 changed files with 307 additions and 21 deletions

View File

@@ -53,5 +53,6 @@ namespace Umbraco.Core.Cache
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
public const string DataTypePreValuesCacheKey = "UmbracoPreVal";
}
}

View File

@@ -41,6 +41,12 @@ namespace Umbraco.Core.Cache
return result.Select(x => x.TryConvertTo<T>().Result);
}
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this ICacheProvider provider, string regexString)
{
var result = provider.GetCacheItemsByKeyExpression(regexString);
return result.Select(x => x.TryConvertTo<T>().Result);
}
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey)
{
var result = provider.GetCacheItem(cacheKey);

View File

@@ -171,6 +171,26 @@ namespace Umbraco.Core.Cache
select c.Value).ToList();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
var found = new List<object>();
foreach (var item in DictionaryCache)
{
var c = new DictionaryItemWrapper(item);
var s = c.Key as string;
if (s != null)
{
var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix));
if (Regex.IsMatch(withoutPrefix, regexString))
{
found.Add(c.Value);
}
}
}
return found;
}
/// <summary>
/// Returns a cache item by key, does not update the cache if it isn't there.
/// </summary>

View File

@@ -16,7 +16,15 @@ namespace Umbraco.Core.Cache
void ClearCacheByKeySearch(string keyStartsWith);
void ClearCacheByKeyExpression(string regexString);
IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith);
IEnumerable<object> GetCacheItemsByKeyExpression(string regexString);
/// <summary>
/// Returns an item with a given key
/// </summary>
/// <param name="cacheKey"></param>
/// <returns></returns>
object GetCacheItem(string cacheKey);
object GetCacheItem(string cacheKey, Func<object> getCacheItem);
}
}

View File

@@ -43,6 +43,11 @@ namespace Umbraco.Core.Cache
return Enumerable.Empty<object>();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return Enumerable.Empty<object>();
}
public virtual object GetCacheItem(string cacheKey)
{
return default(object);

View File

@@ -114,6 +114,13 @@ namespace Umbraco.Core.Cache
select c.Value).ToList();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return (from c in MemoryCache
where Regex.IsMatch(c.Key, regexString)
select c.Value).ToList();
}
public virtual object GetCacheItem(string cacheKey)
{
var result = MemoryCache.Get(cacheKey);

View File

@@ -59,6 +59,13 @@ namespace Umbraco.Core.Cache
select c.Value).ToList();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return (from KeyValuePair<string, object> c in StaticCache
where Regex.IsMatch(c.Key, regexString)
select c.Value).ToList();
}
public virtual object GetCacheItem(string cacheKey)
{
var result = StaticCache[cacheKey];

View File

@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Threading;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
@@ -11,6 +13,7 @@ using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Repositories
{
@@ -19,16 +22,22 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
internal class DataTypeDefinitionRepository : PetaPocoRepositoryBase<int, IDataTypeDefinition>, IDataTypeDefinitionRepository
{
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work)
private readonly CacheHelper _cacheHelper;
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper)
: base(work)
{
_cacheHelper = cacheHelper;
}
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, CacheHelper cacheHelper)
: base(work, cache)
{
_cacheHelper = cacheHelper;
}
private readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
#region Overrides of RepositoryBase<int,DataTypeDefinition>
protected override IDataTypeDefinition PerformGet(int id)
@@ -238,5 +247,85 @@ AND umbracoNode.id <> @id",
}
#endregion
public PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId)
{
using (var l = new UpgradeableReadLock(Locker))
{
var cached = _cacheHelper.RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(GetPrefixedCacheKey(dataTypeId));
if (cached != null && cached.Any())
{
//return from the cache
return cached.First();
}
l.UpgradeToWriteLock();
return GetAndCachePreValueCollection(dataTypeId);
}
}
public string GetPreValueAsString(int preValueId)
{
using (var l = new UpgradeableReadLock(Locker))
{
//We need to see if we can find the cached PreValueCollection based on the cache key above
var regex = CacheKeys.DataTypePreValuesCacheKey + @"[\d]+-[,\d]*" + preValueId + @"[,\d$]*";
var cached = _cacheHelper.RuntimeCache.GetCacheItemsByKeyExpression<PreValueCollection>(regex);
if (cached != null && cached.Any())
{
//return from the cache
var collection = cached.First();
var preVal = collection.PreValuesAsArray.Single(x => x.Id == preValueId);
return preVal.Value;
}
l.UpgradeToWriteLock();
//go and find the data type id for the pre val id passed in
var dto = Database.FirstOrDefault<DataTypePreValueDto>("WHERE id = @preValueId", new { preValueId = preValueId });
if (dto == null)
{
return string.Empty;
}
// go cache the collection
var preVals = GetAndCachePreValueCollection(dto.DataTypeNodeId);
//return the single value for this id
var pv = preVals.PreValuesAsArray.Single(x => x.Id == preValueId);
return pv.Value;
}
}
private string GetPrefixedCacheKey(int dataTypeId)
{
return CacheKeys.DataTypePreValuesCacheKey + dataTypeId + "-";
}
private PreValueCollection GetAndCachePreValueCollection(int dataTypeId)
{
//go get the data
var dtos = Database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = dataTypeId });
var list = dtos.Select(x => new Tuple<PreValue, string, int>(new PreValue(x.Id, x.Value), x.Alias, x.SortOrder)).ToList();
var collection = DataTypeService.PreValueConverter.ConvertToPreValuesCollection(list);
//now create the cache key, this needs to include all pre-value ids so that we can use this cached item in the GetPreValuesAsString method
//the key will be: "UmbracoPreValDATATYPEID-CSVOFPREVALIDS
var key = GetPrefixedCacheKey(dataTypeId)
+ string.Join(",", collection.FormatAsDictionary().Select(x => x.Value.Id).ToArray());
//store into cache
_cacheHelper.RuntimeCache.InsertCacheItem(key, () => collection,
//30 mins
new TimeSpan(0, 0, 30),
//sliding is true
true);
return collection;
}
}
}

View File

@@ -4,6 +4,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
public interface IDataTypeDefinitionRepository : IRepositoryQueryable<int, IDataTypeDefinition>
{
PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId);
string GetPreValueAsString(int preValueId);
}
}

View File

@@ -55,14 +55,15 @@ namespace Umbraco.Core.Persistence
return new ContentTypeRepository(
uow,
_disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current,
new TemplateRepository(uow, NullCacheProvider.Current));
CreateTemplateRepository(uow));
}
public virtual IDataTypeDefinitionRepository CreateDataTypeDefinitionRepository(IDatabaseUnitOfWork uow)
{
return new DataTypeDefinitionRepository(
uow,
NullCacheProvider.Current);
_disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current,
_cacheHelper);
}
public virtual IDictionaryRepository CreateDictionaryRepository(IDatabaseUnitOfWork uow)

View File

@@ -108,10 +108,11 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of string values</returns>
public IEnumerable<string> GetPreValuesByDataTypeId(int id)
{
using (var uow = _uowProvider.GetUnitOfWork())
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(_uowProvider.GetUnitOfWork()))
{
var dtos = uow.Database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = id });
var list = dtos.Select(x => x.Value).ToList();
var collection = repository.GetPreValuesCollectionByDataTypeId(id);
//now convert the collection to a string list
var list = collection.PreValuesAsArray.Select(x => x.Value).ToList();
return list;
}
}
@@ -123,12 +124,9 @@ namespace Umbraco.Core.Services
/// <returns></returns>
public PreValueCollection GetPreValuesCollectionByDataTypeId(int id)
{
using (var uow = _uowProvider.GetUnitOfWork())
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(_uowProvider.GetUnitOfWork()))
{
var dtos = uow.Database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = id });
var list = dtos.Select(x => new Tuple<PreValue, string, int>(new PreValue(x.Id, x.Value), x.Alias, x.SortOrder)).ToList();
return PreValueConverter.ConvertToPreValuesCollection(list);
return repository.GetPreValuesCollectionByDataTypeId(id);
}
}
@@ -139,10 +137,9 @@ namespace Umbraco.Core.Services
/// <returns>PreValue as a string</returns>
public string GetPreValueAsString(int id)
{
using (var uow = _uowProvider.GetUnitOfWork())
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(_uowProvider.GetUnitOfWork()))
{
var dto = uow.Database.FirstOrDefault<DataTypePreValueDto>("WHERE id = @Id", new { Id = id });
return dto != null ? dto.Value : string.Empty;
return repository.GetPreValueAsString(id);
}
}

View File

@@ -1190,5 +1190,14 @@ namespace Umbraco.Core
return false;
}
public static bool CsvContains(this string csv, string value)
{
if (string.IsNullOrEmpty(csv))
{
return false;
}
var idCheckList = csv.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
return idCheckList.Contains(value);
}
}
}

View File

@@ -3,14 +3,16 @@ using System.Data;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Caching;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Tests.TestHelpers;
using NullCacheProvider = Umbraco.Core.Persistence.Caching.NullCacheProvider;
namespace Umbraco.Tests.Persistence.Repositories
{
@@ -26,7 +28,7 @@ namespace Umbraco.Tests.Persistence.Repositories
private DataTypeDefinitionRepository CreateRepository(IDatabaseUnitOfWork unitOfWork)
{
var dataTypeDefinitionRepository = new DataTypeDefinitionRepository(unitOfWork, NullCacheProvider.Current);
var dataTypeDefinitionRepository = new DataTypeDefinitionRepository(unitOfWork, NullCacheProvider.Current, CacheHelper.CreateDisabledCacheHelper());
return dataTypeDefinitionRepository;
}
@@ -302,6 +304,135 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
[Test]
public void Can_Get_Pre_Value_Collection()
{
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
int dtid;
using (var repository = CreateRepository(unitOfWork))
{
var dataTypeDefinition = new DataTypeDefinition(-1, new Guid(Constants.PropertyEditors.RadioButtonList)) { Name = "test" };
repository.AddOrUpdate(dataTypeDefinition);
unitOfWork.Commit();
dtid = dataTypeDefinition.Id;
}
DatabaseContext.Database.Insert(new DataTypePreValueDto() { DataTypeNodeId = dtid, SortOrder = 0, Value = "test1"});
DatabaseContext.Database.Insert(new DataTypePreValueDto() { DataTypeNodeId = dtid, SortOrder = 1, Value = "test2" });
using (var repository = CreateRepository(unitOfWork))
{
var collection = repository.GetPreValuesCollectionByDataTypeId(dtid);
Assert.AreEqual(2, collection.PreValuesAsArray.Count());
}
}
[Test]
public void Can_Get_Pre_Value_As_String()
{
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
int dtid;
using (var repository = CreateRepository(unitOfWork))
{
var dataTypeDefinition = new DataTypeDefinition(-1, new Guid(Constants.PropertyEditors.RadioButtonList)) { Name = "test" };
repository.AddOrUpdate(dataTypeDefinition);
unitOfWork.Commit();
dtid = dataTypeDefinition.Id;
}
var id = DatabaseContext.Database.Insert(new DataTypePreValueDto() { DataTypeNodeId = dtid, SortOrder = 0, Value = "test1" });
DatabaseContext.Database.Insert(new DataTypePreValueDto() { DataTypeNodeId = dtid, SortOrder = 1, Value = "test2" });
using (var repository = CreateRepository(unitOfWork))
{
var val = repository.GetPreValueAsString(Convert.ToInt32(id));
Assert.AreEqual("test1", val);
}
}
[Test]
public void Can_Get_Pre_Value_Collection_With_Cache()
{
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
var cache = new CacheHelper(new ObjectCacheRuntimeCacheProvider(), new StaticCacheProvider(), new StaticCacheProvider());
Func<DataTypeDefinitionRepository> creator = () => new DataTypeDefinitionRepository(
unitOfWork,
NullCacheProvider.Current, cache);
DataTypeDefinition dtd;
using (var repository = creator())
{
dtd = new DataTypeDefinition(-1, new Guid(Constants.PropertyEditors.RadioButtonList)) { Name = "test" };
repository.AddOrUpdate(dtd);
unitOfWork.Commit();
}
DatabaseContext.Database.Insert(new DataTypePreValueDto() { DataTypeNodeId = dtd.Id, SortOrder = 0, Value = "test1" });
DatabaseContext.Database.Insert(new DataTypePreValueDto() { DataTypeNodeId = dtd.Id, SortOrder = 1, Value = "test2" });
//this will cache the result
using (var repository = creator())
{
var collection = repository.GetPreValuesCollectionByDataTypeId(dtd.Id);
}
var cached = cache.RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-");
Assert.IsNotNull(cached);
Assert.AreEqual(1, cached.Count());
Assert.AreEqual(2, cached.Single().FormatAsDictionary().Count);
}
[Test]
public void Can_Get_Pre_Value_As_String_With_Cache()
{
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
var cache = new CacheHelper(new ObjectCacheRuntimeCacheProvider(), new StaticCacheProvider(), new StaticCacheProvider());
Func<DataTypeDefinitionRepository> creator = () => new DataTypeDefinitionRepository(
unitOfWork,
NullCacheProvider.Current, cache);
DataTypeDefinition dtd;
using (var repository = creator())
{
dtd = new DataTypeDefinition(-1, new Guid(Constants.PropertyEditors.RadioButtonList)) { Name = "test" };
repository.AddOrUpdate(dtd);
unitOfWork.Commit();
}
var id = DatabaseContext.Database.Insert(new DataTypePreValueDto() { DataTypeNodeId = dtd.Id, SortOrder = 0, Value = "test1" });
DatabaseContext.Database.Insert(new DataTypePreValueDto() { DataTypeNodeId = dtd.Id, SortOrder = 1, Value = "test2" });
//this will cache the result
using (var repository = creator())
{
var val = repository.GetPreValueAsString(Convert.ToInt32(id));
}
var cached = cache.RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-");
Assert.IsNotNull(cached);
Assert.AreEqual(1, cached.Count());
Assert.AreEqual(2, cached.Single().FormatAsDictionary().Count);
using (var repository = creator())
{
//ensure it still gets resolved!
var val = repository.GetPreValueAsString(Convert.ToInt32(id));
Assert.AreEqual("test1", val);
}
}
[TearDown]
public override void TearDown()
{

View File

@@ -129,11 +129,15 @@ namespace Umbraco.Web.Cache
payloads.ForEach(payload =>
{
//clear both the Id and Unique Id cache since we cache both in the legacy classes :(
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(
string.Format("{0}{1}", CacheKeys.DataTypeCacheKey, payload.Id));
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(
string.Format("{0}{1}", CacheKeys.DataTypeCacheKey, payload.Id));
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(
string.Format("{0}{1}", CacheKeys.DataTypeCacheKey, payload.UniqueId));
//clears the prevalue cache
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(
string.Format("{0}{1}", CacheKeys.DataTypePreValuesCacheKey, payload.Id));
PublishedContentType.ClearDataType(payload.Id);
});