Merge branch 'v8/8.15' into v8/dev
This commit is contained in:
@@ -43,7 +43,8 @@
|
||||
<dependency id="Microsoft.Owin.Security.OAuth" version="[4.0.1,4.999999)" />
|
||||
<dependency id="System.Threading.Tasks.Dataflow" version="[4.9.0,4.999999)" />
|
||||
<dependency id="System.Text.Encoding.CodePages" version="[4.7.1,4.999999)" />
|
||||
<dependency id="MessagePack" version="[2.1.165,2.999999)" />
|
||||
<dependency id="MessagePack" version="[2.2.85,2.999999)" />
|
||||
<dependency id="K4os.Compression.LZ4" version="[1.1.11,1.999999)" />
|
||||
|
||||
</group>
|
||||
|
||||
|
||||
@@ -19,4 +19,4 @@ using System.Resources;
|
||||
|
||||
// these are FYI and changed automatically
|
||||
[assembly: AssemblyFileVersion("8.15.0")]
|
||||
[assembly: AssemblyInformationalVersion("8.15.0-rc")]
|
||||
[assembly: AssemblyInformationalVersion("8.15.0")]
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Umbraco.Core.Migrations.Expressions.Create.Table
|
||||
public class CreateTableOfDtoBuilder : IExecutableBuilder
|
||||
{
|
||||
private readonly IMigrationContext _context;
|
||||
|
||||
// TODO: This doesn't do anything.
|
||||
private readonly DatabaseType[] _supportedDatabaseTypes;
|
||||
|
||||
public CreateTableOfDtoBuilder(IMigrationContext context, params DatabaseType[] supportedDatabaseTypes)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
using System.Linq;
|
||||
using NPoco;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Upgrade.V_8_15_0
|
||||
{
|
||||
@@ -13,9 +18,46 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_15_0
|
||||
|
||||
public override void Migrate()
|
||||
{
|
||||
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList();
|
||||
// allow null for the `data` field
|
||||
if (DatabaseType.IsSqlCe())
|
||||
{
|
||||
// SQLCE does not support altering NTEXT, so we have to jump through some hoops to do it
|
||||
// All column ordering must remain the same as what is defined in the DTO so we need to create a temp table,
|
||||
// drop orig and then re-create/copy.
|
||||
Create.Table<ContentNuDtoTemp>(withoutKeysAndIndexes: true).Do();
|
||||
Execute.Sql($"INSERT INTO [{TempTableName}] SELECT nodeId, published, data, rv FROM [{Constants.DatabaseSchema.Tables.NodeData}]").Do();
|
||||
Delete.Table(Constants.DatabaseSchema.Tables.NodeData).Do();
|
||||
Create.Table<ContentNuDto>().Do();
|
||||
Execute.Sql($"INSERT INTO [{Constants.DatabaseSchema.Tables.NodeData}] SELECT nodeId, published, data, rv, NULL FROM [{TempTableName}]").Do();
|
||||
}
|
||||
else
|
||||
{
|
||||
AlterColumn<ContentNuDto>(Constants.DatabaseSchema.Tables.NodeData, "data");
|
||||
}
|
||||
|
||||
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList();
|
||||
AddColumnIfNotExists<ContentNuDto>(columns, "dataRaw");
|
||||
}
|
||||
|
||||
private const string TempTableName = Constants.DatabaseSchema.TableNamePrefix + "cms" + "ContentNuTEMP";
|
||||
|
||||
[TableName(TempTableName)]
|
||||
[ExplicitColumns]
|
||||
private class ContentNuDtoTemp
|
||||
{
|
||||
[Column("nodeId")]
|
||||
public int NodeId { get; set; }
|
||||
|
||||
[Column("published")]
|
||||
public bool Published { get; set; }
|
||||
|
||||
[Column("data")]
|
||||
[SpecialDbType(SpecialDbTypes.NTEXT)]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
public string Data { get; set; }
|
||||
|
||||
[Column("rv")]
|
||||
public long Rv { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,26 +62,33 @@ namespace Umbraco.Core.Persistence
|
||||
/// <returns>The number of records that were inserted.</returns>
|
||||
public static int BulkInsertRecords<T>(this IUmbracoDatabase database, IEnumerable<T> records, bool useNativeBulkInsert = true)
|
||||
{
|
||||
var recordsA = records.ToArray();
|
||||
if (recordsA.Length == 0) return 0;
|
||||
if (!records.Any()) return 0;
|
||||
|
||||
var pocoData = database.PocoDataFactory.ForType(typeof(T));
|
||||
if (pocoData == null) throw new InvalidOperationException("Could not find PocoData for " + typeof(T));
|
||||
|
||||
if (database.DatabaseType.IsSqlCe())
|
||||
{
|
||||
if (useNativeBulkInsert) return BulkInsertRecordsSqlCe(database, pocoData, recordsA);
|
||||
if (useNativeBulkInsert)
|
||||
{
|
||||
return BulkInsertRecordsSqlCe(database, pocoData, records);
|
||||
}
|
||||
|
||||
// else, no other choice
|
||||
foreach (var record in recordsA)
|
||||
var count = 0;
|
||||
foreach (var record in records)
|
||||
{
|
||||
database.Insert(record);
|
||||
return recordsA.Length;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
if (database.DatabaseType.IsSqlServer())
|
||||
{
|
||||
return useNativeBulkInsert && database.DatabaseType.IsSqlServer2008OrLater()
|
||||
? BulkInsertRecordsSqlServer(database, pocoData, recordsA)
|
||||
: BulkInsertRecordsWithCommands(database, recordsA);
|
||||
? BulkInsertRecordsSqlServer(database, pocoData, records)
|
||||
: BulkInsertRecordsWithCommands(database, records.ToArray());
|
||||
}
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
@@ -96,7 +103,9 @@ namespace Umbraco.Core.Persistence
|
||||
private static int BulkInsertRecordsWithCommands<T>(IUmbracoDatabase database, T[] records)
|
||||
{
|
||||
foreach (var command in database.GenerateBulkInsertCommands(records))
|
||||
{
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
return records.Length; // what else?
|
||||
}
|
||||
@@ -241,6 +250,10 @@ namespace Umbraco.Core.Persistence
|
||||
/// <returns>The number of records that were inserted.</returns>
|
||||
internal static int BulkInsertRecordsSqlServer<T>(IUmbracoDatabase database, PocoData pocoData, IEnumerable<T> records)
|
||||
{
|
||||
// TODO: The main reason this exists is because the NPoco InsertBulk method doesn't return the number of items.
|
||||
// It is worth investigating the performance of this vs NPoco's because we use a custom BulkDataReader
|
||||
// which in theory should be more efficient than NPocos way of building up an in-memory DataTable.
|
||||
|
||||
// create command against the original database.Connection
|
||||
using (var command = database.CreateCommand(database.Connection, CommandType.Text, string.Empty))
|
||||
{
|
||||
@@ -252,7 +265,13 @@ namespace Umbraco.Core.Persistence
|
||||
var syntax = database.SqlContext.SqlSyntax as SqlServerSyntaxProvider;
|
||||
if (syntax == null) throw new NotSupportedException("SqlSyntax must be SqlServerSyntaxProvider.");
|
||||
|
||||
using (var copy = new SqlBulkCopy(tConnection, SqlBulkCopyOptions.Default, tTransaction) { BulkCopyTimeout = 10000, DestinationTableName = tableName })
|
||||
using (var copy = new SqlBulkCopy(tConnection, SqlBulkCopyOptions.Default, tTransaction)
|
||||
{
|
||||
BulkCopyTimeout = 0, // 0 = no bulk copy timeout. If a timeout occurs it will be an connection/command timeout.
|
||||
DestinationTableName = tableName,
|
||||
// be consistent with NPoco: https://github.com/schotime/NPoco/blob/5117a55fde57547e928246c044fd40bd00b2d7d1/src/NPoco.SqlServer/SqlBulkCopyHelper.cs#L50
|
||||
BatchSize = 4096
|
||||
})
|
||||
using (var bulkReader = new PocoDataDataReader<T, SqlServerSyntaxProvider>(records, pocoData, syntax))
|
||||
{
|
||||
//we need to add column mappings here because otherwise columns will be matched by their order and if the order of them are different in the DB compared
|
||||
|
||||
@@ -40,9 +40,10 @@ namespace Umbraco.Core.Persistence
|
||||
_tableDefinition = DefinitionFactory.GetTableDefinition(pd.Type, sqlSyntaxProvider);
|
||||
if (_tableDefinition == null) throw new InvalidOperationException("No table definition found for type " + pd.Type);
|
||||
|
||||
// only real columns, exclude result columns
|
||||
// only real columns, exclude result/computed columns
|
||||
// Like NPoco does: https://github.com/schotime/NPoco/blob/5117a55fde57547e928246c044fd40bd00b2d7d1/src/NPoco.SqlServer/SqlBulkCopyHelper.cs#L59
|
||||
_readerColumns = pd.Columns
|
||||
.Where(x => x.Value.ResultColumn == false)
|
||||
.Where(x => x.Value.ResultColumn == false && x.Value.ComputedColumn == false)
|
||||
.Select(x => x.Value)
|
||||
.ToArray();
|
||||
|
||||
|
||||
@@ -352,7 +352,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
sql.Append(" ");
|
||||
sql.Append(FormatIdentity(column));
|
||||
|
||||
var isNullable = column.IsNullable;
|
||||
//var isNullable = column.IsNullable;
|
||||
|
||||
//var constraint = FormatConstraint(column)?.TrimStart("CONSTRAINT ");
|
||||
//var hasConstraint = !string.IsNullOrWhiteSpace(constraint);
|
||||
@@ -360,11 +360,14 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
//var defaultValue = FormatDefaultValue(column);
|
||||
//var hasDefaultValue = !string.IsNullOrWhiteSpace(defaultValue);
|
||||
|
||||
if (isNullable /*&& !hasConstraint && !hasDefaultValue*/)
|
||||
{
|
||||
sqls = Enumerable.Empty<string>();
|
||||
return sql.ToString();
|
||||
}
|
||||
// TODO: This used to exit if nullable but that means this would never work
|
||||
// to return SQL if the column was nullable?!? I don't get it. This was here
|
||||
// 4 years ago, I've removed it so that this works for nullable columns.
|
||||
//if (isNullable /*&& !hasConstraint && !hasDefaultValue*/)
|
||||
//{
|
||||
// sqls = Enumerable.Empty<string>();
|
||||
// return sql.ToString();
|
||||
//}
|
||||
|
||||
var msql = new List<string>();
|
||||
sqls = msql;
|
||||
|
||||
@@ -6,10 +6,16 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// Determines if a property type's value should be compressed in memory
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
///
|
||||
/// </remarks>
|
||||
public interface IPropertyCacheCompression
|
||||
{
|
||||
bool IsCompressed(IReadOnlyContentBase content, string propertyTypeAlias);
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether a property on the content is/should be compressed
|
||||
/// </summary>
|
||||
/// <param name="content">The content</param>
|
||||
/// <param name="propertyTypeAlias">The property to compress or not</param>
|
||||
/// <param name="published">Whether this content is the published version</param>
|
||||
bool IsCompressed(IReadOnlyContentBase content, string propertyTypeAlias, bool published);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,13 @@ namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
public interface IPropertyCacheCompressionOptions
|
||||
{
|
||||
bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor);
|
||||
/// <summary>
|
||||
/// Whether a property on the content is/should be compressed
|
||||
/// </summary>
|
||||
/// <param name="content">The content</param>
|
||||
/// <param name="propertyType">The property to compress or not</param>
|
||||
/// <param name="dataEditor">The datatype of the property to compress or not</param>
|
||||
/// <param name="published">Whether this content is the published version</param>
|
||||
bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor, bool published);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// </summary>
|
||||
internal class NoopPropertyCacheCompressionOptions : IPropertyCacheCompressionOptions
|
||||
{
|
||||
public bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor) => false;
|
||||
public bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor, bool published) => false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@ namespace Umbraco.Core.PropertyEditors
|
||||
private readonly IPropertyCacheCompressionOptions _compressionOptions;
|
||||
private readonly IReadOnlyDictionary<int, IContentTypeComposition> _contentTypes;
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly ConcurrentDictionary<(int contentTypeId, string propertyAlias), bool> _isCompressedCache;
|
||||
private readonly ConcurrentDictionary<(int contentTypeId, string propertyAlias, bool published), bool> _isCompressedCache;
|
||||
|
||||
public PropertyCacheCompression(
|
||||
IPropertyCacheCompressionOptions compressionOptions,
|
||||
IReadOnlyDictionary<int, IContentTypeComposition> contentTypes,
|
||||
PropertyEditorCollection propertyEditors,
|
||||
ConcurrentDictionary<(int, string), bool> compressedStoragePropertyEditorCache)
|
||||
ConcurrentDictionary<(int, string, bool), bool> compressedStoragePropertyEditorCache)
|
||||
{
|
||||
_compressionOptions = compressionOptions;
|
||||
_contentTypes = contentTypes ?? throw new System.ArgumentNullException(nameof(contentTypes));
|
||||
@@ -28,9 +28,9 @@ namespace Umbraco.Core.PropertyEditors
|
||||
_isCompressedCache = compressedStoragePropertyEditorCache;
|
||||
}
|
||||
|
||||
public bool IsCompressed(IReadOnlyContentBase content, string alias)
|
||||
public bool IsCompressed(IReadOnlyContentBase content, string alias, bool published)
|
||||
{
|
||||
var compressedStorage = _isCompressedCache.GetOrAdd((content.ContentTypeId, alias), x =>
|
||||
var compressedStorage = _isCompressedCache.GetOrAdd((content.ContentTypeId, alias, published), x =>
|
||||
{
|
||||
if (!_contentTypes.TryGetValue(x.contentTypeId, out var ct))
|
||||
return false;
|
||||
@@ -40,7 +40,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
|
||||
if (!_propertyEditors.TryGet(propertyType.PropertyEditorAlias, out var propertyEditor)) return false;
|
||||
|
||||
return _compressionOptions.IsCompressed(content, propertyType, propertyEditor);
|
||||
return _compressionOptions.IsCompressed(content, propertyType, propertyEditor, published);
|
||||
});
|
||||
|
||||
return compressedStorage;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// Compress large, non published text properties
|
||||
/// </summary>
|
||||
internal class UnPublishedContentPropertyCacheCompressionOptions : IPropertyCacheCompressionOptions
|
||||
{
|
||||
public bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor, bool published)
|
||||
{
|
||||
if (!published && propertyType.SupportsPublishing && propertyType.ValueStorageType == ValueStorageType.Ntext)
|
||||
{
|
||||
//Only compress non published content that supports publishing and the property is text
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,6 +171,7 @@
|
||||
<Compile Include="PropertyEditors\NoopPropertyCacheCompressionOptions.cs" />
|
||||
<Compile Include="PropertyEditors\PropertyCacheCompression.cs" />
|
||||
<Compile Include="PropertyEditors\IPropertyCacheCompression.cs" />
|
||||
<Compile Include="PropertyEditors\UnPublishedContentPropertyCacheCompressionOptions.cs" />
|
||||
<Compile Include="Serialization\AutoInterningStringConverter.cs" />
|
||||
<Compile Include="Serialization\AutoInterningStringKeyCaseInsensitiveDictionaryConverter.cs" />
|
||||
<Compile Include="PropertyEditors\EyeDropperColorPickerConfiguration.cs" />
|
||||
|
||||
@@ -56,14 +56,14 @@ namespace Umbraco.Tests.PublishedContent
|
||||
|
||||
var content = Mock.Of<IReadOnlyContentBase>(x => x.ContentTypeId == 1);
|
||||
|
||||
var json = jsonSerializer.Serialize(content, cacheModel).StringData;
|
||||
var msgPack = msgPackSerializer.Serialize(content, cacheModel).ByteData;
|
||||
var json = jsonSerializer.Serialize(content, cacheModel, false).StringData;
|
||||
var msgPack = msgPackSerializer.Serialize(content, cacheModel, false).ByteData;
|
||||
|
||||
Console.WriteLine(json);
|
||||
Console.WriteLine(msgPackSerializer.ToJson(msgPack));
|
||||
|
||||
var jsonContent = jsonSerializer.Deserialize(content, json, null);
|
||||
var msgPackContent = msgPackSerializer.Deserialize(content, null, msgPack);
|
||||
var jsonContent = jsonSerializer.Deserialize(content, json, null, false);
|
||||
var msgPackContent = msgPackSerializer.Deserialize(content, null, msgPack, false);
|
||||
|
||||
|
||||
CollectionAssert.AreEqual(jsonContent.CultureData.Keys, msgPackContent.CultureData.Keys);
|
||||
|
||||
@@ -351,9 +351,6 @@
|
||||
<DevelopmentServerPort>8150</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:8150</IISUrl>
|
||||
<DevelopmentServerPort>8131</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:8131</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
|
||||
@@ -393,12 +393,13 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
}
|
||||
else
|
||||
{
|
||||
var deserializedContent = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw);
|
||||
bool published = false;
|
||||
var deserializedContent = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published);
|
||||
|
||||
d = new ContentData
|
||||
{
|
||||
Name = dto.EditName,
|
||||
Published = false,
|
||||
Published = published,
|
||||
TemplateId = dto.EditTemplateId,
|
||||
VersionId = dto.VersionId,
|
||||
VersionDate = dto.EditVersionDate,
|
||||
@@ -420,13 +421,14 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
}
|
||||
else
|
||||
{
|
||||
var deserializedContent = serializer.Deserialize(dto, dto.PubData, dto.PubDataRaw);
|
||||
bool published = true;
|
||||
var deserializedContent = serializer.Deserialize(dto, dto.PubData, dto.PubDataRaw, published);
|
||||
|
||||
p = new ContentData
|
||||
{
|
||||
Name = dto.PubName,
|
||||
UrlSegment = deserializedContent.UrlSegment,
|
||||
Published = true,
|
||||
Published = published,
|
||||
TemplateId = dto.PubTemplateId,
|
||||
VersionId = dto.VersionId,
|
||||
VersionDate = dto.PubVersionDate,
|
||||
@@ -456,12 +458,13 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
if (dto.EditData == null && dto.EditDataRaw == null)
|
||||
throw new InvalidOperationException("No data for media " + dto.Id);
|
||||
|
||||
var deserializedMedia = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw);
|
||||
bool published = true;
|
||||
var deserializedMedia = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published);
|
||||
|
||||
var p = new ContentData
|
||||
{
|
||||
Name = dto.EditName,
|
||||
Published = true,
|
||||
Published = published,
|
||||
TemplateId = -1,
|
||||
VersionId = dto.VersionId,
|
||||
VersionDate = dto.EditVersionDate,
|
||||
|
||||
@@ -14,12 +14,12 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
/// <summary>
|
||||
/// Deserialize the data into a <see cref="ContentCacheDataModel"/>
|
||||
/// </summary>
|
||||
ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData);
|
||||
ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the <see cref="ContentCacheDataModel"/>
|
||||
/// Serializes the <see cref="ContentCacheDataModel"/>
|
||||
/// </summary>
|
||||
ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model);
|
||||
ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
DateFormatString = "o"
|
||||
};
|
||||
private readonly JsonNameTable _propertyNameTable = new DefaultJsonNameTable();
|
||||
public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData)
|
||||
public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published)
|
||||
{
|
||||
if (stringData == null && byteData != null)
|
||||
throw new NotSupportedException($"{typeof(JsonContentNestedDataSerializer)} does not support byte[] serialization");
|
||||
@@ -39,7 +39,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
}
|
||||
}
|
||||
|
||||
public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model)
|
||||
public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published)
|
||||
{
|
||||
// note that numeric values (which are Int32) are serialized without their
|
||||
// type (eg "value":1234) and JsonConvert by default deserializes them as Int64
|
||||
|
||||
@@ -39,7 +39,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
|
||||
_options = defaultOptions
|
||||
.WithResolver(resolver)
|
||||
.WithCompression(MessagePackCompression.Lz4BlockArray);
|
||||
.WithCompression(MessagePackCompression.Lz4BlockArray)
|
||||
.WithSecurity(MessagePackSecurity.UntrustedData);
|
||||
}
|
||||
|
||||
public string ToJson(byte[] bin)
|
||||
@@ -48,12 +49,12 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
return json;
|
||||
}
|
||||
|
||||
public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData)
|
||||
public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published)
|
||||
{
|
||||
if (byteData != null)
|
||||
{
|
||||
var cacheModel = MessagePackSerializer.Deserialize<ContentCacheDataModel>(byteData, _options);
|
||||
Expand(content, cacheModel);
|
||||
Expand(content, cacheModel, published);
|
||||
return cacheModel;
|
||||
}
|
||||
else if (stringData != null)
|
||||
@@ -61,7 +62,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
// NOTE: We don't really support strings but it's possible if manually used (i.e. tests)
|
||||
var bin = Convert.FromBase64String(stringData);
|
||||
var cacheModel = MessagePackSerializer.Deserialize<ContentCacheDataModel>(bin, _options);
|
||||
Expand(content, cacheModel);
|
||||
Expand(content, cacheModel, published);
|
||||
return cacheModel;
|
||||
}
|
||||
else
|
||||
@@ -70,9 +71,9 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
}
|
||||
}
|
||||
|
||||
public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model)
|
||||
public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published)
|
||||
{
|
||||
Compress(content, model);
|
||||
Compress(content, model, published);
|
||||
var bytes = MessagePackSerializer.Serialize(model, _options);
|
||||
return new ContentCacheDataSerializationResult(null, bytes);
|
||||
}
|
||||
@@ -80,7 +81,9 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
/// <summary>
|
||||
/// Used during serialization to compress properties
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="published"></param>
|
||||
/// <remarks>
|
||||
/// This will essentially 'double compress' property data. The MsgPack data as a whole will already be compressed
|
||||
/// but this will go a step further and double compress property data so that it is stored in the nucache file
|
||||
@@ -88,11 +91,11 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
/// read/decompressed as a string to be displayed on the front-end. This allows for potentially a significant
|
||||
/// memory savings but could also affect performance of first rendering pages while decompression occurs.
|
||||
/// </remarks>
|
||||
private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model)
|
||||
private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model, bool published)
|
||||
{
|
||||
foreach(var propertyAliasToData in model.PropertyData)
|
||||
{
|
||||
if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key))
|
||||
if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key, published))
|
||||
{
|
||||
foreach(var property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is string))
|
||||
{
|
||||
@@ -105,12 +108,14 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
/// <summary>
|
||||
/// Used during deserialization to map the property data as lazy or expand the value
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="nestedData"></param>
|
||||
private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData)
|
||||
/// <param name="published"></param>
|
||||
private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData, bool published)
|
||||
{
|
||||
foreach (var propertyAliasToData in nestedData.PropertyData)
|
||||
{
|
||||
if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key))
|
||||
if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key,published))
|
||||
{
|
||||
foreach (var property in propertyAliasToData.Value.Where(x => x.Value != null))
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly IPropertyCacheCompressionOptions _compressionOptions;
|
||||
private readonly ConcurrentDictionary<(int, string), bool> _isCompressedCache = new ConcurrentDictionary<(int, string), bool>();
|
||||
private readonly ConcurrentDictionary<(int, string, bool), bool> _isCompressedCache = new ConcurrentDictionary<(int, string, bool), bool>();
|
||||
|
||||
public MsgPackContentNestedDataSerializerFactory(
|
||||
IContentTypeService contentTypeService,
|
||||
|
||||
@@ -24,10 +24,18 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
}
|
||||
else
|
||||
{
|
||||
composition.RegisterUnique<IContentCacheDataSerializerFactory, MsgPackContentNestedDataSerializerFactory>();
|
||||
composition.RegisterUnique<IContentCacheDataSerializerFactory, MsgPackContentNestedDataSerializerFactory>();
|
||||
}
|
||||
var unPublishedContentCompression = ConfigurationManager.AppSettings[NuCacheSerializerComponent.Nucache_UnPublishedContentCompression_Key];
|
||||
if (serializer == "MsgPack" && unPublishedContentCompression == "true")
|
||||
{
|
||||
composition.RegisterUnique<IPropertyCacheCompressionOptions, UnPublishedContentPropertyCacheCompressionOptions>();
|
||||
}
|
||||
else
|
||||
{
|
||||
composition.RegisterUnique<IPropertyCacheCompressionOptions, NoopPropertyCacheCompressionOptions>();
|
||||
}
|
||||
|
||||
composition.RegisterUnique<IPropertyCacheCompressionOptions, NoopPropertyCacheCompressionOptions>();
|
||||
|
||||
composition.RegisterUnique(factory => new ContentDataSerializer(new DictionaryOfPropertyDataSerializer()));
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
public class NuCacheSerializerComponent : IComponent
|
||||
{
|
||||
internal const string Nucache_Serializer_Key = "Umbraco.Web.PublishedCache.NuCache.Serializer";
|
||||
internal const string Nucache_UnPublishedContentCompression_Key = "Umbraco.Web.PublishedCache.NuCache.CompressUnPublishedContent";
|
||||
private const string JSON_SERIALIZER_VALUE = "JSON";
|
||||
private readonly Lazy<IPublishedSnapshotService> _service;
|
||||
private readonly IKeyValueService _keyValueService;
|
||||
@@ -54,7 +55,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
{
|
||||
_profilingLogger.Warn<NuCacheSerializerComponent>($"Database NuCache was serialized using {currentSerializer}. Currently configured NuCache serializer {serializer}. Rebuilding Nucache");
|
||||
|
||||
using (_profilingLogger.TraceDuration<NuCacheSerializerComponent>($"Rebuilding NuCache database with {currentSerializer} serializer"))
|
||||
using (_profilingLogger.TraceDuration<NuCacheSerializerComponent>($"Rebuilding NuCache database with {serializer} serializer"))
|
||||
{
|
||||
_service.Value.Rebuild();
|
||||
_keyValueService.SetValue(Nucache_Serializer_Key, serializer);
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
IPublishedModelFactory publishedModelFactory,
|
||||
UrlSegmentProviderCollection urlSegmentProviders,
|
||||
ISyncBootStateAccessor syncBootStateAccessor,
|
||||
IContentCacheDataSerializerFactory contentCacheDataSerializerFactory,
|
||||
IContentCacheDataSerializerFactory contentCacheDataSerializerFactory,
|
||||
ContentDataSerializer contentDataSerializer = null)
|
||||
: base(publishedSnapshotAccessor, variationContextAccessor)
|
||||
{
|
||||
@@ -177,7 +177,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
_localContentDb = BTree.GetTree(localContentDbPath, _localContentDbExists, _contentDataSerializer);
|
||||
_localMediaDb = BTree.GetTree(localMediaDbPath, _localMediaDbExists, _contentDataSerializer);
|
||||
|
||||
_logger.Info<PublishedSnapshotService,bool,bool>("Registered with MainDom, localContentDbExists? {LocalContentDbExists}, localMediaDbExists? {LocalMediaDbExists}", _localContentDbExists, _localMediaDbExists);
|
||||
_logger.Info<PublishedSnapshotService, bool, bool>("Registered with MainDom, localContentDbExists? {LocalContentDbExists}, localMediaDbExists? {LocalMediaDbExists}", _localContentDbExists, _localMediaDbExists);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -262,7 +262,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
if (!okMedia)
|
||||
_logger.Warn<PublishedSnapshotService>("Loading media from local db raised warnings, will reload from database.");
|
||||
}
|
||||
|
||||
|
||||
if (!okContent)
|
||||
LockAndLoadContent(scope => LoadContentFromDatabaseLocked(scope, true));
|
||||
|
||||
@@ -1168,7 +1168,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
if (Volatile.Read(ref _isReady) == false)
|
||||
{
|
||||
throw new InvalidOperationException("The published snapshot service has not properly initialized.");
|
||||
}
|
||||
}
|
||||
|
||||
var preview = previewToken.IsNullOrWhiteSpace() == false;
|
||||
return new PublishedSnapshot(this, preview);
|
||||
@@ -1491,7 +1491,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
UrlSegment = content.GetUrlSegment(_urlSegmentProviders)
|
||||
};
|
||||
|
||||
var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData);
|
||||
var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData, published);
|
||||
|
||||
var dto = new ContentNuDto
|
||||
{
|
||||
|
||||
@@ -281,14 +281,15 @@ namespace Umbraco.Web
|
||||
return CreateHtmlString(url, htmlEncode);
|
||||
}
|
||||
|
||||
public static IHtmlString GetCropUrl(this UrlHelper urlHelper, ImageCropperValue imageCropperValue, string cropAlias, bool htmlEncode = true)
|
||||
{
|
||||
if (imageCropperValue == null || string.IsNullOrEmpty(imageCropperValue.Src)) return EmptyHtmlString;
|
||||
|
||||
var url = imageCropperValue.Src.GetCropUrl(imageCropperValue, cropAlias: cropAlias, useCropDimensions: true);
|
||||
|
||||
return CreateHtmlString(url, htmlEncode);
|
||||
}
|
||||
// TODO: enable again in v9 and make sure to document that `@Url.GetCropUrl(Model.Property, cropAlias: "Featured")` needs to be updated - see https://github.com/umbraco/Umbraco-CMS/pull/10527 for alternatives
|
||||
// public static IHtmlString GetCropUrl(this UrlHelper urlHelper, ImageCropperValue imageCropperValue, string cropAlias, bool htmlEncode = true)
|
||||
// {
|
||||
// if (imageCropperValue == null || string.IsNullOrEmpty(imageCropperValue.Src)) return EmptyHtmlString;
|
||||
//
|
||||
// var url = imageCropperValue.Src.GetCropUrl(imageCropperValue, cropAlias: cropAlias, useCropDimensions: true);
|
||||
//
|
||||
// return CreateHtmlString(url, htmlEncode);
|
||||
// }
|
||||
|
||||
public static IHtmlString GetCropUrl(this UrlHelper urlHelper,
|
||||
ImageCropperValue imageCropperValue,
|
||||
|
||||
Reference in New Issue
Block a user