Introduce IContentNestedDataSerializer to allow injecting a custom serializer for nucache

This commit is contained in:
nzdev
2020-07-01 17:19:56 +12:00
parent 90a9ce4c5d
commit 578e1317a0
14 changed files with 245 additions and 28 deletions

View File

@@ -37,6 +37,7 @@ namespace Umbraco.Tests.PublishedContent
private ContentType _contentTypeInvariant;
private ContentType _contentTypeVariant;
private TestDataSource _source;
private IContentNestedDataSerializer _contentNestedDataSerializer;
[TearDown]
public void Teardown()
@@ -134,6 +135,7 @@ namespace Umbraco.Tests.PublishedContent
// create a data source for NuCache
_source = new TestDataSource(kits());
_contentNestedDataSerializer = new JsonContentNestedDataSerializer();
// at last, create the complete NuCache snapshot service!
var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true };
@@ -155,7 +157,8 @@ namespace Umbraco.Tests.PublishedContent
globalSettings,
Mock.Of<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>(),
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }));
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }),
_contentNestedDataSerializer);
// invariant is the current default
_variationAccesor.VariationContext = new VariationContext();

View File

@@ -33,6 +33,7 @@ namespace Umbraco.Tests.PublishedContent
{
private IPublishedSnapshotService _snapshotService;
private IVariationContextAccessor _variationAccesor;
private IContentNestedDataSerializer _contentNestedDataSerializer;
private ContentType _contentType;
private PropertyType _propertyType;
@@ -114,6 +115,7 @@ namespace Umbraco.Tests.PublishedContent
// create a data source for NuCache
var dataSource = new TestDataSource(kit);
_contentNestedDataSerializer = new JsonContentNestedDataSerializer();
var runtime = Mock.Of<IRuntimeState>();
Mock.Get(runtime).Setup(x => x.Level).Returns(RuntimeLevel.Run);
@@ -201,7 +203,8 @@ namespace Umbraco.Tests.PublishedContent
globalSettings,
Mock.Of<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>(),
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }));
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }),
_contentNestedDataSerializer);
// invariant is the current default
_variationAccesor.VariationContext = new VariationContext();

View File

@@ -82,6 +82,7 @@ namespace Umbraco.Tests.Scoping
var mediaRepository = Mock.Of<IMediaRepository>();
var memberRepository = Mock.Of<IMemberRepository>();
var nestedContentDataSerializer = new JsonContentNestedDataSerializer();
return new PublishedSnapshotService(
options,
null,
@@ -95,11 +96,12 @@ namespace Umbraco.Tests.Scoping
ScopeProvider,
documentRepository, mediaRepository, memberRepository,
DefaultCultureAccessor,
new DatabaseDataSource(),
new DatabaseDataSource(nestedContentDataSerializer),
Factory.GetInstance<IGlobalSettings>(),
Factory.GetInstance<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>(),
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }));
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }),
nestedContentDataSerializer);
}
protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable<IUrlProvider> urlProviders = null)

View File

@@ -53,6 +53,8 @@ namespace Umbraco.Tests.Services
var mediaRepository = Mock.Of<IMediaRepository>();
var memberRepository = Mock.Of<IMemberRepository>();
var nestedContentDataSerializer = new JsonContentNestedDataSerializer();
return new PublishedSnapshotService(
options,
null,
@@ -66,11 +68,12 @@ namespace Umbraco.Tests.Services
ScopeProvider,
documentRepository, mediaRepository, memberRepository,
DefaultCultureAccessor,
new DatabaseDataSource(),
new DatabaseDataSource(nestedContentDataSerializer),
Factory.GetInstance<IGlobalSettings>(),
Factory.GetInstance<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>(),
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }));
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }),
nestedContentDataSerializer);
}
public class LocalServerMessenger : ServerMessengerBase

View File

@@ -7,7 +7,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
/// <summary>
/// The content item 1:M data that is serialized to JSON
/// </summary>
internal class ContentNestedData
public class ContentNestedData
{
//dont serialize empty properties
[JsonProperty("pd")]

View File

@@ -6,7 +6,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
/// <summary>
/// Represents the culture variation information on a content item
/// </summary>
internal class CultureVariation
public class CultureVariation
{
[JsonProperty("nm")]
public string Name { get; set; }

View File

@@ -20,7 +20,15 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
// provides efficient database access for NuCache
internal class DatabaseDataSource : IDataSource
{
private const int PageSize = 500;
private readonly IContentNestedDataSerializer _contentNestedDataSerializer;
internal DatabaseDataSource(IContentNestedDataSerializer contentNestedDataSerializer)
{
_contentNestedDataSerializer = contentNestedDataSerializer;
}
// we want arrays, we want them all loaded, not an enumerable
@@ -198,7 +206,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
yield return CreateMediaNodeKit(row);
}
private static ContentNodeKit CreateContentNodeKit(ContentSourceDto dto)
private ContentNodeKit CreateContentNodeKit(ContentSourceDto dto)
{
ContentData d = null;
ContentData p = null;
@@ -213,7 +221,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
}
else
{
var nested = DeserializeNestedData(dto.EditData);
var nested = _contentNestedDataSerializer.Deserialize(dto.EditData);
d = new ContentData
{
@@ -240,7 +248,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
}
else
{
var nested = DeserializeNestedData(dto.PubData);
var nested = _contentNestedDataSerializer.Deserialize(dto.PubData);
p = new ContentData
{
@@ -271,12 +279,12 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
return s;
}
private static ContentNodeKit CreateMediaNodeKit(ContentSourceDto dto)
private ContentNodeKit CreateMediaNodeKit(ContentSourceDto dto)
{
if (dto.EditData == null)
throw new Exception("No data for media " + dto.Id);
var nested = DeserializeNestedData(dto.EditData);
var nested = _contentNestedDataSerializer.Deserialize(dto.EditData);
var p = new ContentData
{
@@ -303,17 +311,6 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
return s;
}
private static ContentNestedData DeserializeNestedData(string data)
{
// by default JsonConvert will deserialize our numeric values as Int64
// which is bad, because they were Int32 in the database - take care
var settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new ForceInt32Converter() }
};
return JsonConvert.DeserializeObject<ContentNestedData>(data, settings);
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
{
public interface IContentNestedDataSerializer
{
ContentNestedData Deserialize(string data);
string Serialize(ContentNestedData nestedData);
}
}

View File

@@ -0,0 +1,31 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Serialization;
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
{
internal class JsonContentNestedDataSerializer : IContentNestedDataSerializer
{
public ContentNestedData Deserialize(string data)
{
// by default JsonConvert will deserialize our numeric values as Int64
// which is bad, because they were Int32 in the database - take care
var settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new ForceInt32Converter() }
};
return JsonConvert.DeserializeObject<ContentNestedData>(data, settings);
}
public string Serialize(ContentNestedData nestedData)
{
return JsonConvert.SerializeObject(nestedData);
}
}
}

View File

@@ -0,0 +1,157 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Serialization;
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
{
public class MappedPropertiesJsonContentNestedDataSerializer : IContentNestedDataSerializer
{
private readonly IDictionary<string, PropertyMap> _serializeMap;
private readonly IDictionary<string, PropertyMap> _deserializeMap;
/// <summary>
/// Constructor
/// </summary>
/// <param name="serializeMap">Map for PropertData properties</param>
/// <param name="deserializeMap"></param>
public MappedPropertiesJsonContentNestedDataSerializer(IDictionary<string, PropertyMap> serializeMap, IDictionary<string, PropertyMap> deserializeMap)
{
_serializeMap = serializeMap;
_deserializeMap = deserializeMap;
}
public (string mappedName, bool isCompressed) ToSerializedProperty(string name)
{
if(_serializeMap.TryGetValue(name,out PropertyMap map))
{
return (map.To,map.IsCompressed);
}
return (name,false);
}
public (string mappedName, bool isCompressed) ToDeserializedProperty(string name)
{
if (_deserializeMap.TryGetValue(name, out PropertyMap map))
{
return (map.To, map.IsCompressed);
}
return (name, false);
}
public ContentNestedData Deserialize(string data)
{
// by default JsonConvert will deserialize our numeric values as Int64
// which is bad, because they were Int32 in the database - take care
var settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new ForceInt32Converter() },
};
return JsonConvert.DeserializeObject<ContentNestedData>(data, settings);
}
public string Serialize(ContentNestedData nestedData)
{
return JsonConvert.SerializeObject(nestedData);
}
}
public class MappedPropertyDataContractResolver : DefaultContractResolver
{
private readonly IDictionary<string, PropertyMap> _serializeMap;
private readonly IDictionary<string, PropertyMap> _deserializeMap;
/// <summary>
/// Constructor
/// </summary>
/// <param name="serializeMap">Map for PropertData properties</param>
/// <param name="deserializeMap"></param>
public MappedPropertyDataContractResolver(IDictionary<string, PropertyMap> serializeMap, IDictionary<string, PropertyMap> deserializeMap)
{
_serializeMap = serializeMap;
_deserializeMap = deserializeMap;
}
public (string mappedName, bool isCompressed) ToSerializedProperty(string name)
{
if (_serializeMap.TryGetValue(name, out PropertyMap map))
{
return (map.To, map.IsCompressed);
}
return (name, false);
}
public (string mappedName, bool isCompressed) ToDeserializedProperty(string name)
{
if (_deserializeMap.TryGetValue(name, out PropertyMap map))
{
return (map.To, map.IsCompressed);
}
return (name, false);
}
private readonly Type _type = typeof(PropertyData);
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == _type)
{
if (property.PropertyName.Equals("LongPropertyName", StringComparison.OrdinalIgnoreCase))
{
property.PropertyName = "Short";
}
}
return property;
}
protected override string ResolvePropertyName(string propertyName)
{
return base.ResolvePropertyName(propertyName);
}
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
{
JsonDictionaryContract contract = base.CreateDictionaryContract(objectType);
contract.DictionaryKeyResolver = propertyName => propertyName;
return contract;
}
}
public class MappedNamingStrategy : NamingStrategy
{
public MappedNamingStrategy()
{
ProcessDictionaryKeys = true;
}
public override string GetDictionaryKey(string key)
{
return key;
}
protected override string ResolvePropertyName(string name)
{
return name;
}
}
public class PropertyMap
{
/// <summary>
/// PropertyName
/// </summary>
public string To { get; set; }
/// <summary>
/// Whether the property is compressed
/// </summary>
public bool IsCompressed { get; set; }
}
}

View File

@@ -4,7 +4,7 @@ using Newtonsoft.Json;
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
{
internal class PropertyData
public class PropertyData
{
private string _culture;
private string _segment;

View File

@@ -10,6 +10,9 @@ namespace Umbraco.Web.PublishedCache.NuCache
{
base.Compose(composition);
// register the NuCache NestedContentData serializer
composition.Register<IContentNestedDataSerializer, JsonContentNestedDataSerializer>();
// register the NuCache database data source
composition.Register<IDataSource, DatabaseDataSource>();

View File

@@ -48,6 +48,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
private readonly IPublishedModelFactory _publishedModelFactory;
private readonly IDefaultCultureAccessor _defaultCultureAccessor;
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
private readonly IContentNestedDataSerializer _contentNestedDataSerializer;
// volatile because we read it with no lock
private volatile bool _isReady;
@@ -81,7 +82,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
IDataSource dataSource, IGlobalSettings globalSettings,
IEntityXmlSerializer entitySerializer,
IPublishedModelFactory publishedModelFactory,
UrlSegmentProviderCollection urlSegmentProviders)
UrlSegmentProviderCollection urlSegmentProviders, IContentNestedDataSerializer contentNestedDataSerializer)
: base(publishedSnapshotAccessor, variationContextAccessor)
{
//if (Interlocked.Increment(ref _singletonCheck) > 1)
@@ -98,6 +99,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
_defaultCultureAccessor = defaultCultureAccessor;
_globalSettings = globalSettings;
_urlSegmentProviders = urlSegmentProviders;
_contentNestedDataSerializer = contentNestedDataSerializer;
// we need an Xml serializer here so that the member cache can support XPath,
// for members this is done by navigating the serialized-to-xml member
@@ -1457,7 +1459,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
// note that numeric values (which are Int32) are serialized without their
// type (eg "value":1234) and JsonConvert by default deserializes them as Int64
Data = JsonConvert.SerializeObject(nestedData)
Data = _contentNestedDataSerializer.Serialize(nestedData)
};
//Core.Composing.Current.Logger.Debug<PublishedSnapshotService>(dto.Data);

View File

@@ -245,6 +245,8 @@
<Compile Include="Compose\NestedContentPropertyComposer.cs" />
<Compile Include="PropertyEditors\ParameterEditors\MultipleMediaPickerParameterEditor.cs" />
<Compile Include="PropertyEditors\RichTextEditorPastedImages.cs" />
<Compile Include="PublishedCache\NuCache\DataSource\IContentNestedDataSerializer.cs" />
<Compile Include="PublishedCache\NuCache\DataSource\JsonContentNestedDataSerializer.cs" />
<Compile Include="PublishedCache\NuCache\PublishedSnapshotServiceOptions.cs" />
<Compile Include="PublishedCache\NuCache\Snap\GenObj.cs" />
<Compile Include="PublishedCache\NuCache\Snap\GenRef.cs" />