Clean up extension methods (#17051)

Co-authored-by: Andy Butland <abutland73@gmail.com>
This commit is contained in:
Ronald Barendse
2025-05-05 14:53:26 +02:00
committed by GitHub
parent 44d9f5ac2e
commit 2dced37117
28 changed files with 187 additions and 1157 deletions

View File

@@ -86,8 +86,8 @@ public class AppCaches : IDisposable
{
if (disposing)
{
RuntimeCache.DisposeIfDisposable();
RequestCache.DisposeIfDisposable();
(RuntimeCache as IDisposable)?.Dispose();
(RequestCache as IDisposable)?.Dispose();
IsolatedCaches.Dispose();
}

View File

@@ -81,9 +81,9 @@ public abstract class AppPolicedCacheDictionary<TKey> : IDisposable
{
if (disposing)
{
foreach (IAppPolicyCache value in _caches.Values)
foreach (IDisposable value in _caches.Values.OfType<IDisposable>())
{
value.DisposeIfDisposable();
value.Dispose();
}
}

View File

@@ -135,7 +135,7 @@ public class DeepCloneAppCache : IAppPolicyCache, IDisposable
{
if (disposing)
{
InnerCache.DisposeIfDisposable();
(InnerCache as IDisposable)?.Dispose();
}
_disposedValue = true;

View File

@@ -59,6 +59,7 @@ public class ComponentCollection : BuilderCollectionBase<IAsyncComponent>
try
{
await component.TerminateAsync(isRestarting, cancellationToken);
(component as IDisposable)?.Dispose();
}
catch (Exception ex)
{

View File

@@ -1,6 +1,4 @@
using Umbraco.Cms.Core.Extensions;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.DynamicRoot.QuerySteps;
@@ -30,7 +28,9 @@ public class FurthestAncestorOrSelfDynamicRootQueryStep : IDynamicRootQueryStep
}
using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
var result = (await _nodeFilterRepository.FurthestAncestorOrSelfAsync(origins, filter))?.ToSingleItemCollection() ?? Array.Empty<Guid>();
var result = (await _nodeFilterRepository.FurthestAncestorOrSelfAsync(origins, filter)) is Guid key
? [key]
: Array.Empty<Guid>();
return Attempt<ICollection<Guid>>.Succeed(result);
}

View File

@@ -1,6 +1,4 @@
using Umbraco.Cms.Core.Extensions;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.DynamicRoot.QuerySteps;
@@ -30,7 +28,9 @@ public class NearestAncestorOrSelfDynamicRootQueryStep : IDynamicRootQueryStep
}
using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
var result = (await _nodeFilterRepository.NearestAncestorOrSelfAsync(origins, filter))?.ToSingleItemCollection() ?? Array.Empty<Guid>();
var result = (await _nodeFilterRepository.NearestAncestorOrSelfAsync(origins, filter)) is Guid key
? [key]
: Array.Empty<Guid>();
return Attempt<ICollection<Guid>>.Succeed(result);
}

View File

@@ -42,20 +42,6 @@ public static class AssemblyExtensions
return _rootDir;
}
/// <summary>
/// Returns the file used to load the assembly
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
[Obsolete("This extension method is no longer used and will be removed in Umbraco 17.")]
public static FileInfo GetAssemblyFile(this Assembly assembly)
{
var codeBase = assembly.Location;
var uri = new Uri(codeBase);
var path = uri.LocalPath;
return new FileInfo(path);
}
/// <summary>
/// Returns true if the assembly is the App_Code assembly
/// </summary>
@@ -90,25 +76,6 @@ public static class AssemblyExtensions
// only way I can figure out how to test is by the name
assembly.FullName!.StartsWith("App_global.asax");
/// <summary>
/// Returns the file used to load the assembly
/// </summary>
/// <param name="assemblyName"></param>
/// <returns></returns>
[Obsolete("This extension method is no longer used and will be removed in Umbraco 17.")]
public static FileInfo? GetAssemblyFile(this AssemblyName assemblyName)
{
var codeBase = assemblyName.CodeBase;
if (!string.IsNullOrEmpty(codeBase))
{
var uri = new Uri(codeBase);
var path = uri.LocalPath;
return new FileInfo(path);
}
return null;
}
/// <summary>
/// Gets the assembly informational version for the specified <paramref name="assembly" />.
/// </summary>

View File

@@ -1,8 +0,0 @@
namespace Umbraco.Cms.Core.Extensions;
public static class CollectionExtensions
{
[Obsolete("Please replace uses of this extension method with collection expression. This method will be removed in Umbraco 17.")]
public static ICollection<T> ToSingleItemCollection<T>(this T item) =>
new T[] { item };
}

View File

@@ -1,115 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Data;
namespace Umbraco.Extensions;
/// <summary>
/// Static and extension methods for the DataTable object
/// </summary>
public static class DataTableExtensions
{
/// <summary>
/// Creates a DataTable with the specified alias and columns and uses a callback to populate the headers.
/// </summary>
/// <param name="tableAlias"></param>
/// <param name="getHeaders"></param>
/// <param name="rowData"> </param>
/// <returns></returns>
/// <remarks>
/// This has been migrated from the Node class and uses proper locking now. It is now used by the Node class and the
/// DynamicPublishedContent extensions for legacy reasons.
/// </remarks>
[Obsolete("This no longer has a use in Umbraco and so will be removed in Umbraco 17.")]
public static DataTable GenerateDataTable(
string tableAlias,
Func<string, IEnumerable<KeyValuePair<string, string>>> getHeaders,
Func<IEnumerable<Tuple<IEnumerable<KeyValuePair<string, object?>>, IEnumerable<KeyValuePair<string, object?>>>>>
rowData)
{
var dt = new DataTable(tableAlias);
// get all row data
Tuple<IEnumerable<KeyValuePair<string, object?>>, IEnumerable<KeyValuePair<string, object?>>>[] tableData =
rowData().ToArray();
// get all headers
IDictionary<string, string> propertyHeaders = GetPropertyHeaders(tableAlias, getHeaders);
foreach (KeyValuePair<string, string> h in propertyHeaders)
{
dt.Columns.Add(new DataColumn(h.Value));
}
// add row data
foreach (Tuple<IEnumerable<KeyValuePair<string, object?>>, IEnumerable<KeyValuePair<string, object?>>> r in
tableData)
{
dt.PopulateRow(
propertyHeaders,
r.Item1,
r.Item2);
}
return dt;
}
/// <summary>
/// Helper method to return this ugly object
/// </summary>
/// <returns></returns>
/// <remarks>
/// This is for legacy code, I didn't want to go creating custom classes for these
/// </remarks>
[Obsolete("This no longer has a use in Umbraco and so will be removed in Umbraco 17.")]
public static List<Tuple<IEnumerable<KeyValuePair<string, object?>>, IEnumerable<KeyValuePair<string, object?>>>>
CreateTableData() =>
new List<Tuple<IEnumerable<KeyValuePair<string, object?>>, IEnumerable<KeyValuePair<string, object?>>>>();
/// <summary>
/// Helper method to deal with these ugly objects
/// </summary>
/// <param name="rowData"></param>
/// <param name="standardVals"></param>
/// <param name="userVals"></param>
/// <remarks>
/// This is for legacy code, I didn't want to go creating custom classes for these
/// </remarks>
[Obsolete("This no longer has a use in Umbraco and so will be removed in Umbraco 17.")]
public static void AddRowData(
List<Tuple<IEnumerable<KeyValuePair<string, object?>>, IEnumerable<KeyValuePair<string, object?>>>> rowData,
IEnumerable<KeyValuePair<string, object?>> standardVals,
IEnumerable<KeyValuePair<string, object?>> userVals) =>
rowData.Add(new Tuple<IEnumerable<KeyValuePair<string, object?>>, IEnumerable<KeyValuePair<string, object?>>>(
standardVals,
userVals));
private static IDictionary<string, string> GetPropertyHeaders(
string alias,
Func<string, IEnumerable<KeyValuePair<string, string>>> getHeaders)
{
IEnumerable<KeyValuePair<string, string>> headers = getHeaders(alias);
var def = headers.ToDictionary(pt => pt.Key, pt => pt.Value);
return def;
}
private static void PopulateRow(
this DataTable dt,
IDictionary<string, string> aliasesToNames,
IEnumerable<KeyValuePair<string, object?>> standardVals,
IEnumerable<KeyValuePair<string, object?>> userPropertyVals)
{
DataRow dr = dt.NewRow();
foreach (KeyValuePair<string, object?> r in standardVals)
{
dr[r.Key] = r.Value;
}
foreach (KeyValuePair<string, object?> p in userPropertyVals.Where(p => p.Value != null))
{
dr[aliasesToNames[p.Key]] = p.Value;
}
dt.Rows.Add(dr);
}
}

View File

@@ -1,59 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Diagnostics;
using Umbraco.Cms.Core;
namespace Umbraco.Extensions;
public static class DelegateExtensions
{
[Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")]
public static Attempt<T?> RetryUntilSuccessOrTimeout<T>(this Func<Attempt<T?>> task, TimeSpan timeout, TimeSpan pause)
{
if (pause.TotalMilliseconds < 0)
{
throw new ArgumentException("pause must be >= 0 milliseconds");
}
var stopwatch = Stopwatch.StartNew();
do
{
Attempt<T?> result = task();
if (result.Success)
{
return result;
}
Thread.Sleep((int)pause.TotalMilliseconds);
}
while (stopwatch.Elapsed < timeout);
return Attempt<T?>.Fail();
}
[Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")]
public static Attempt<T?> RetryUntilSuccessOrMaxAttempts<T>(this Func<int, Attempt<T?>> task, int totalAttempts, TimeSpan pause)
{
if (pause.TotalMilliseconds < 0)
{
throw new ArgumentException("pause must be >= 0 milliseconds");
}
var attempts = 0;
do
{
attempts++;
Attempt<T?> result = task(attempts);
if (result.Success)
{
return result;
}
Thread.Sleep((int)pause.TotalMilliseconds);
}
while (attempts < totalAttempts);
return Attempt<T?>.Fail();
}
}

View File

@@ -260,7 +260,7 @@ public static class DictionaryExtensions
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
public static string ToQueryString(this IDictionary<string, object> d)
public static string ToQueryString(this IDictionary<string, object?> d)
{
if (!d.Any())
{
@@ -268,7 +268,7 @@ public static class DictionaryExtensions
}
var builder = new StringBuilder();
foreach (KeyValuePair<string, object> i in d)
foreach (KeyValuePair<string, object?> i in d)
{
builder.Append(WebUtility.UrlEncode(i.Key));
builder.Append('=');

View File

@@ -1,30 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
namespace Umbraco.Extensions
{
/// <summary>
/// Provides extension methods to <see cref="Enum"/>.
/// </summary>
public static class EnumExtensions
{
/// <summary>
/// Determines whether any of the flags/bits are set within the enum value.
/// </summary>
/// <typeparam name="T">The enum type.</typeparam>
/// <param name="value">The value.</param>
/// <param name="flags">The flags.</param>
/// <returns>
/// <c>true</c> if any of the flags/bits are set within the enum value; otherwise, <c>false</c>.
/// </returns>
[Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")]
public static bool HasFlagAny<T>(this T value, T flags)
where T : Enum
{
var v = Convert.ToUInt64(value);
var f = Convert.ToUInt64(flags);
return (v & f) > 0;
}
}
}

View File

@@ -1,21 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
namespace Umbraco.Extensions;
/// <summary>
/// Provides extension methods for the <see cref="KeyValuePair{TKey,TValue}" /> struct.
/// </summary>
public static class KeyValuePairExtensions
{
/// <summary>
/// Implements key/value pair deconstruction.
/// </summary>
/// <remarks>Allows for <c>foreach ((var k, var v) in ...)</c>.</remarks>
[Obsolete("Please replace uses of this extension method with native language features. This method will be removed in Umbraco 17.")]
public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value)
{
key = kvp.Key;
value = kvp.Value;
}
}

View File

@@ -1,42 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Collections.Specialized;
using Umbraco.Cms.Core;
namespace Umbraco.Extensions;
public static class NameValueCollectionExtensions
{
[Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")]
public static IEnumerable<KeyValuePair<string?, string?>> AsEnumerable(this NameValueCollection nvc)
{
foreach (var key in nvc.AllKeys)
{
yield return new KeyValuePair<string?, string?>(key, nvc[key]);
}
}
[Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")]
public static bool ContainsKey(this NameValueCollection collection, string key) =>
collection.Keys.Cast<object>().Any(k => (string)k == key);
[Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")]
public static T? GetValue<T>(this NameValueCollection collection, string key, T defaultIfNotFound)
{
if (collection.ContainsKey(key) == false)
{
return defaultIfNotFound;
}
var val = collection[key];
if (val == null)
{
return defaultIfNotFound;
}
Attempt<T> result = val.TryConvertTo<T>();
return result.Success ? result.Result : defaultIfNotFound;
}
}

View File

@@ -4,7 +4,6 @@
using System.Collections;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Xml;
@@ -15,81 +14,148 @@ using Umbraco.Cms.Core.Collections;
namespace Umbraco.Extensions;
/// <summary>
/// Provides object extension methods.
/// Provides object extension methods.
/// </summary>
public static class ObjectExtensions
{
private static readonly ConcurrentDictionary<Type, Type?> NullableGenericCache = new();
private static readonly ConcurrentDictionary<CompositeTypeTypeKey, TypeConverter?> InputTypeConverterCache = new();
private static readonly ConcurrentDictionary<CompositeTypeTypeKey, TypeConverter?> DestinationTypeConverterCache =
new();
private static readonly ConcurrentDictionary<CompositeTypeTypeKey, bool> AssignableTypeCache = new();
private static readonly ConcurrentDictionary<Type, bool> BoolConvertCache = new();
private static readonly char[] NumberDecimalSeparatorsToNormalize = { '.', ',' };
private static readonly CustomBooleanTypeConverter CustomBooleanTypeConverter = new();
// private static readonly ConcurrentDictionary<Type, Func<object>> ObjectFactoryCache = new ConcurrentDictionary<Type, Func<object>>();
private static readonly ConcurrentDictionary<Type, Type?> _nullableGenericCache = new();
private static readonly ConcurrentDictionary<CompositeTypeTypeKey, TypeConverter?> _inputTypeConverterCache = new();
private static readonly ConcurrentDictionary<CompositeTypeTypeKey, TypeConverter?> _destinationTypeConverterCache = new();
private static readonly ConcurrentDictionary<CompositeTypeTypeKey, bool> _assignableTypeCache = new();
private static readonly ConcurrentDictionary<Type, bool> _boolConvertCache = new();
private static readonly char[] _numberDecimalSeparatorsToNormalize = ['.', ','];
private static readonly CustomBooleanTypeConverter _customBooleanTypeConverter = new();
/// <summary>
/// Returns an XML serialized safe string representation for the value and type.
/// </summary>
/// <param name="input"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> AsEnumerableOfOne<T>(this T input) => Enumerable.Repeat(input, 1);
/// <typeparam name="T">The type of the value.</typeparam>
/// <param name="value">The value.</param>
/// <returns>
/// The XML serialized safe string representation.
/// </returns>
public static string ToXmlString<T>(this object value) => value.ToXmlString(typeof(T));
/// <summary>
/// Disposes the object if it implements <see cref="IDisposable" />.
/// Returns an XML serialized safe string representation for the value and type.
/// </summary>
/// <param name="input">The object.</param>
[Obsolete("Please replace uses of this extension method with (input as IDisposable)?.Dispose(). This extension method will be removed in Umbraco 17.")]
public static void DisposeIfDisposable(this object input)
/// <param name="value">The value.</param>
/// <param name="type">The type of the value. This can only be a primitive type or <see cref="Guid" /> and <see cref="T:byte[]" />, otherwise an exception is thrown.</param>
/// <returns>
/// The XML serialized safe string representation.
/// </returns>
public static string ToXmlString(this object value, Type type)
{
if (input is IDisposable disposable)
if (value == null)
{
try
{
disposable.Dispose();
}
catch (ObjectDisposedException)
{
// ignore if it is already disposed
}
return string.Empty;
}
if (type == typeof(string))
{
return value.ToString().OrIfNullOrWhiteSpace(string.Empty);
}
if (type == typeof(bool))
{
return XmlConvert.ToString((bool)value);
}
if (type == typeof(byte))
{
return XmlConvert.ToString((byte)value);
}
if (type == typeof(char))
{
return XmlConvert.ToString((char)value);
}
if (type == typeof(DateTime))
{
return XmlConvert.ToString((DateTime)value, XmlDateTimeSerializationMode.Unspecified);
}
if (type == typeof(DateTimeOffset))
{
return XmlConvert.ToString((DateTimeOffset)value);
}
if (type == typeof(decimal))
{
return XmlConvert.ToString((decimal)value);
}
if (type == typeof(double))
{
return XmlConvert.ToString((double)value);
}
if (type == typeof(float))
{
return XmlConvert.ToString((float)value);
}
if (type == typeof(Guid))
{
return XmlConvert.ToString((Guid)value);
}
if (type == typeof(int))
{
return XmlConvert.ToString((int)value);
}
if (type == typeof(long))
{
return XmlConvert.ToString((long)value);
}
if (type == typeof(sbyte))
{
return XmlConvert.ToString((sbyte)value);
}
if (type == typeof(short))
{
return XmlConvert.ToString((short)value);
}
if (type == typeof(TimeSpan))
{
return XmlConvert.ToString((TimeSpan)value);
}
if (type == typeof(uint))
{
return XmlConvert.ToString((uint)value);
}
if (type == typeof(ulong))
{
return XmlConvert.ToString((ulong)value);
}
if (type == typeof(ushort))
{
return XmlConvert.ToString((ushort)value);
}
throw new NotSupportedException($"Cannot convert type {type.FullName} to a string using ToXmlString, as it is not supported by XmlConvert.");
}
/// <summary>
/// Provides a shortcut way of safely casting an input when you cannot guarantee the <typeparamref name="T" /> is
/// an instance type (i.e., when the C# AS keyword is not applicable).
/// Attempts to convert the input object to the output type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="T">The type to convert to.</typeparam>
/// <param name="input">The input.</param>
/// <returns></returns>
[Obsolete("This extension method is not longer used and will be removed in Umbraco 17.")]
public static T? SafeCast<T>(this object input)
{
if (ReferenceEquals(null, input) || ReferenceEquals(default(T), input))
{
return default;
}
if (input is T variable)
{
return variable;
}
return default;
}
/// <summary>
/// Attempts to convert the input object to the output type.
/// </summary>
/// <remarks>This code is an optimized version of the original Umbraco method</remarks>
/// <typeparam name="T">The type to convert to</typeparam>
/// <param name="input">The input.</param>
/// <returns>The <see cref="Attempt{T}" /></returns>
/// <returns>
/// The <see cref="Attempt{T}" />.
/// </returns>
/// <remarks>
/// This code is an optimized version of the original Umbraco method.
/// </remarks>
public static Attempt<T> TryConvertTo<T>(this object? input)
{
Attempt<object?> result = TryConvertTo(input, typeof(T));
@@ -116,19 +182,23 @@ public static class ObjectExtensions
{
return Attempt<T>.Succeed((T)input);
}
catch (Exception e)
catch (Exception ex)
{
return Attempt<T>.Fail(e);
return Attempt<T>.Fail(ex);
}
}
/// <summary>
/// Attempts to convert the input object to the output type.
/// Attempts to convert the input object to the output type.
/// </summary>
/// <remarks>This code is an optimized version of the original Umbraco method</remarks>
/// <param name="input">The input.</param>
/// <param name="target">The type to convert to</param>
/// <returns>The <see cref="Attempt{Object}" /></returns>
/// <param name="target">The type to convert to.</param>
/// <returns>
/// The <see cref="T:Attempt{object?}" />.
/// </returns>
/// <remarks>
/// This code is an optimized version of the original Umbraco method.
/// </remarks>
public static Attempt<object?> TryConvertTo(this object? input, Type target)
{
if (target == null)
@@ -158,7 +228,7 @@ public static class ObjectExtensions
return Attempt.Succeed(input);
}
// Check for string so that overloaders of ToString() can take advantage of the conversion.
// Check for string so that overloads of ToString() can take advantage of the conversion.
if (target == typeof(string))
{
return Attempt<object?>.Succeed(input.ToString());
@@ -225,13 +295,13 @@ public static class ObjectExtensions
{
if (GetCachedCanConvertToBoolean(inputType))
{
return Attempt.Succeed(CustomBooleanTypeConverter.ConvertFrom(input!));
return Attempt.Succeed(_customBooleanTypeConverter.ConvertFrom(input!));
}
}
if (target == typeof(DateTime) && input is DateTimeOffset dateTimeOffset)
{
// IMPORTANT: for compatability with various editors, we must discard any Offset information and assume UTC time here
// IMPORTANT: for compatibility with various editors, we must discard any Offset information and assume UTC time here
return Attempt.Succeed((object?)new DateTime(
new DateOnly(dateTimeOffset.Year, dateTimeOffset.Month, dateTimeOffset.Day),
new TimeOnly(dateTimeOffset.Hour, dateTimeOffset.Minute, dateTimeOffset.Second, dateTimeOffset.Millisecond, dateTimeOffset.Microsecond),
@@ -240,7 +310,7 @@ public static class ObjectExtensions
if (target == typeof(DateTimeOffset) && input is DateTime dateTime)
{
// IMPORTANT: for compatability with various editors, we must discard any DateTimeKind information and assume UTC time here
// IMPORTANT: for compatibility with various editors, we must discard any DateTimeKind information and assume UTC time here
return Attempt.Succeed((object?)new DateTimeOffset(
new DateOnly(dateTime.Year, dateTime.Month, dateTime.Day),
new TimeOnly(dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond, dateTime.Microsecond),
@@ -281,102 +351,18 @@ public static class ObjectExtensions
return Attempt<object?>.Fail();
}
// public enum PropertyNamesCaseType
// {
// CamelCase,
// CaseInsensitive
// }
///// <summary>
///// Convert an object to a JSON string with camelCase formatting
///// </summary>
///// <param name="obj"></param>
///// <returns></returns>
// public static string ToJsonString(this object obj)
// {
// return obj.ToJsonString(PropertyNamesCaseType.CamelCase);
// }
///// <summary>
///// Convert an object to a JSON string with the specified formatting
///// </summary>
///// <param name="obj">The obj.</param>
///// <param name="propertyNamesCaseType">Type of the property names case.</param>
///// <returns></returns>
// public static string ToJsonString(this object obj, PropertyNamesCaseType propertyNamesCaseType)
// {
// var type = obj.GetType();
// var dateTimeStyle = "yyyy-MM-dd HH:mm:ss";
// if (type.IsPrimitive || typeof(string).IsAssignableFrom(type))
// {
// return obj.ToString();
// }
// if (typeof(DateTime).IsAssignableFrom(type) || typeof(DateTimeOffset).IsAssignableFrom(type))
// {
// return Convert.ToDateTime(obj).ToString(dateTimeStyle);
// }
// var serializer = new JsonSerializer();
// switch (propertyNamesCaseType)
// {
// case PropertyNamesCaseType.CamelCase:
// serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
// break;
// }
// var dateTimeConverter = new IsoDateTimeConverter
// {
// DateTimeStyles = System.Globalization.DateTimeStyles.None,
// DateTimeFormat = dateTimeStyle
// };
// if (typeof(IDictionary).IsAssignableFrom(type))
// {
// return JObject.FromObject(obj, serializer).ToString(Formatting.None, dateTimeConverter);
// }
// if (type.IsArray || (typeof(IEnumerable).IsAssignableFrom(type)))
// {
// return JArray.FromObject(obj, serializer).ToString(Formatting.None, dateTimeConverter);
// }
// return JObject.FromObject(obj, serializer).ToString(Formatting.None, dateTimeConverter);
// }
/// <summary>
/// Converts an object into a dictionary
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TProperty"></typeparam>
/// <typeparam name="TVal"> </typeparam>
/// <param name="o"></param>
/// <param name="ignoreProperties"></param>
/// <returns></returns>
[Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")]
public static IDictionary<string, TVal>? ToDictionary<T, TProperty, TVal>(
this T o,
params Expression<Func<T, TProperty>>[] ignoreProperties) => o?.ToDictionary<TVal>(ignoreProperties
.Select(e => o.GetPropertyInfo(e)).Select(propInfo => propInfo.Name).ToArray());
internal static void CheckThrowObjectDisposed(this IDisposable disposable, bool isDisposed, string objectname)
{
// TODO: Localize this exception
if (isDisposed)
{
throw new ObjectDisposedException(objectname);
}
}
/// <summary>
/// Attempts to convert the input string to the output type.
/// Attempts to convert the input string to the output type.
/// </summary>
/// <remarks>This code is an optimized version of the original Umbraco method</remarks>
/// <param name="input">The input.</param>
/// <param name="target">The type to convert to</param>
/// <returns>The <see cref="Nullable{Attempt}" /></returns>
/// <returns>
/// The <see cref="T:Attempt{object?}" />
/// </returns>
/// <remarks>
/// This code is an optimized version of the original Umbraco method.
/// </remarks>
private static Attempt<object?>? TryConvertToFromString(this string input, Type target)
{
// Easy
@@ -529,143 +515,7 @@ public static class ObjectExtensions
return null; // we can't decide...
}
/// <summary>
/// Turns object into dictionary
/// </summary>
/// <param name="o"></param>
/// <param name="ignoreProperties">Properties to ignore</param>
/// <returns></returns>
[Obsolete("Use of this can be replaced with RouteValueDictionary or HtmlHelper.AnonymousObjectToHtmlAttributes(). The method will be removed in Umbraco 17.")]
public static IDictionary<string, TVal> ToDictionary<TVal>(this object o, params string[] ignoreProperties)
{
if (o != null)
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(o);
var d = new Dictionary<string, TVal>();
foreach (PropertyDescriptor prop in props.Cast<PropertyDescriptor>()
.Where(x => ignoreProperties.Contains(x.Name) == false))
{
var val = prop.GetValue(o);
if (val != null)
{
d.Add(prop.Name, (TVal)val);
}
}
return d;
}
return new Dictionary<string, TVal>();
}
/// <summary>
/// Returns an XmlSerialized safe string representation for the value
/// </summary>
/// <param name="value"></param>
/// <param name="type">The Type can only be a primitive type or Guid and byte[] otherwise an exception is thrown</param>
/// <returns></returns>
public static string ToXmlString(this object value, Type type)
{
if (value == null)
{
return string.Empty;
}
if (type == typeof(string))
{
return value.ToString().IsNullOrWhiteSpace() ? string.Empty : value.ToString()!;
}
if (type == typeof(bool))
{
return XmlConvert.ToString((bool)value);
}
if (type == typeof(byte))
{
return XmlConvert.ToString((byte)value);
}
if (type == typeof(char))
{
return XmlConvert.ToString((char)value);
}
if (type == typeof(DateTime))
{
return XmlConvert.ToString((DateTime)value, XmlDateTimeSerializationMode.Unspecified);
}
if (type == typeof(DateTimeOffset))
{
return XmlConvert.ToString((DateTimeOffset)value);
}
if (type == typeof(decimal))
{
return XmlConvert.ToString((decimal)value);
}
if (type == typeof(double))
{
return XmlConvert.ToString((double)value);
}
if (type == typeof(float))
{
return XmlConvert.ToString((float)value);
}
if (type == typeof(Guid))
{
return XmlConvert.ToString((Guid)value);
}
if (type == typeof(int))
{
return XmlConvert.ToString((int)value);
}
if (type == typeof(long))
{
return XmlConvert.ToString((long)value);
}
if (type == typeof(sbyte))
{
return XmlConvert.ToString((sbyte)value);
}
if (type == typeof(short))
{
return XmlConvert.ToString((short)value);
}
if (type == typeof(TimeSpan))
{
return XmlConvert.ToString((TimeSpan)value);
}
if (type == typeof(uint))
{
return XmlConvert.ToString((uint)value);
}
if (type == typeof(ulong))
{
return XmlConvert.ToString((ulong)value);
}
if (type == typeof(ushort))
{
return XmlConvert.ToString((ushort)value);
}
throw new NotSupportedException("Cannot convert type " + type.FullName +
" to a string using ToXmlString as it is not supported by XmlConvert");
}
internal static string? ToDebugString(this object? obj, int levels = 0)
private static string? ToDebugString(this object? obj, int levels = 0)
{
if (obj == null)
{
@@ -738,35 +588,6 @@ public static class ObjectExtensions
return null;
}
/// <summary>
/// Attempts to serialize the value to an XmlString using ToXmlString
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
internal static Attempt<string?> TryConvertToXmlString(this object value, Type type)
{
try
{
var output = value.ToXmlString(type);
return Attempt.Succeed(output);
}
catch (NotSupportedException ex)
{
return Attempt<string?>.Fail(ex);
}
}
/// <summary>
/// Returns an XmlSerialized safe string representation for the value and type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static string ToXmlString<T>(this object value) => value.ToXmlString(typeof(T));
public static Guid AsGuid(this object value) => value is Guid guid ? guid : Guid.Empty;
private static string? GetEnumPropertyDebugString(object enumItem, int levels)
{
try
@@ -795,7 +616,7 @@ public static class ObjectExtensions
private static string NormalizeNumberDecimalSeparator(string s)
{
var normalized = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator[0];
return s.ReplaceMany(NumberDecimalSeparatorsToNormalize, normalized);
return s.ReplaceMany(_numberDecimalSeparatorsToNormalize, normalized);
}
// gets a converter for source, that can convert to target, or null if none exists
@@ -804,7 +625,7 @@ public static class ObjectExtensions
{
var key = new CompositeTypeTypeKey(source, target);
if (InputTypeConverterCache.TryGetValue(key, out TypeConverter? typeConverter))
if (_inputTypeConverterCache.TryGetValue(key, out TypeConverter? typeConverter))
{
return typeConverter;
}
@@ -812,10 +633,10 @@ public static class ObjectExtensions
TypeConverter converter = TypeDescriptor.GetConverter(source);
if (converter.CanConvertTo(target))
{
return InputTypeConverterCache[key] = converter;
return _inputTypeConverterCache[key] = converter;
}
InputTypeConverterCache[key] = null;
_inputTypeConverterCache[key] = null;
return null;
}
@@ -825,7 +646,7 @@ public static class ObjectExtensions
{
var key = new CompositeTypeTypeKey(source, target);
if (DestinationTypeConverterCache.TryGetValue(key, out TypeConverter? typeConverter))
if (_destinationTypeConverterCache.TryGetValue(key, out TypeConverter? typeConverter))
{
return typeConverter;
}
@@ -833,10 +654,10 @@ public static class ObjectExtensions
TypeConverter converter = TypeDescriptor.GetConverter(target);
if (converter.CanConvertFrom(source))
{
return DestinationTypeConverterCache[key] = converter;
return _destinationTypeConverterCache[key] = converter;
}
DestinationTypeConverterCache[key] = null;
_destinationTypeConverterCache[key] = null;
return null;
}
@@ -844,7 +665,7 @@ public static class ObjectExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Type? GetCachedGenericNullableType(Type type)
{
if (NullableGenericCache.TryGetValue(type, out Type? underlyingType))
if (_nullableGenericCache.TryGetValue(type, out Type? underlyingType))
{
return underlyingType;
}
@@ -852,10 +673,10 @@ public static class ObjectExtensions
if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
Type? underlying = Nullable.GetUnderlyingType(type);
return NullableGenericCache[type] = underlying;
return _nullableGenericCache[type] = underlying;
}
NullableGenericCache[type] = null;
_nullableGenericCache[type] = null;
return null;
}
@@ -864,7 +685,7 @@ public static class ObjectExtensions
private static bool GetCachedCanAssign(object input, Type source, Type target)
{
var key = new CompositeTypeTypeKey(source, target);
if (AssignableTypeCache.TryGetValue(key, out var canConvert))
if (_assignableTypeCache.TryGetValue(key, out var canConvert))
{
return canConvert;
}
@@ -873,26 +694,26 @@ public static class ObjectExtensions
// We can use it to very quickly determine whether true/false
if (input is IConvertible && target.IsAssignableFrom(source))
{
return AssignableTypeCache[key] = true;
return _assignableTypeCache[key] = true;
}
return AssignableTypeCache[key] = false;
return _assignableTypeCache[key] = false;
}
// determines whether a type can be converted to boolean
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool GetCachedCanConvertToBoolean(Type type)
{
if (BoolConvertCache.TryGetValue(type, out var result))
if (_boolConvertCache.TryGetValue(type, out var result))
{
return result;
}
if (CustomBooleanTypeConverter.CanConvertFrom(type))
if (_customBooleanTypeConverter.CanConvertFrom(type))
{
return BoolConvertCache[type] = true;
return _boolConvertCache[type] = true;
}
return BoolConvertCache[type] = false;
return _boolConvertCache[type] = false;
}
}

View File

@@ -2832,137 +2832,6 @@ public static class PublishedContentExtensions
#endregion
#region Axes: children
/// <summary>
/// Gets the children of the content in a DataTable.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="navigationQueryService"></param>
/// <param name="publishedStatusFilteringService"></param>
/// <param name="contentTypeService">The content type service.</param>
/// <param name="mediaTypeService">The media type service.</param>
/// <param name="memberTypeService">The member type service.</param>
/// <param name="publishedUrlProvider">The published url provider.</param>
/// <param name="contentTypeAliasFilter">An optional content type alias.</param>
/// <param name="culture">
/// The specific culture to filter for. If null is used the current culture is used. (Default is
/// null)
/// </param>
/// <returns>The children of the content.</returns>
[Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")]
public static DataTable ChildrenAsTable(
this IPublishedContent content,
INavigationQueryService navigationQueryService,
IPublishedStatusFilteringService publishedStatusFilteringService,
IContentTypeService contentTypeService,
IMediaTypeService mediaTypeService,
IMemberTypeService memberTypeService,
IPublishedUrlProvider publishedUrlProvider,
string contentTypeAliasFilter = "",
string? culture = null)
=> content.GenerateDataTable(navigationQueryService, publishedStatusFilteringService, contentTypeService, mediaTypeService, memberTypeService, publishedUrlProvider, contentTypeAliasFilter, culture);
[Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")]
public static DataTable ChildrenAsTable(
this IPublishedContent content,
IVariationContextAccessor variationContextAccessor,
IPublishedCache publishedCache,
INavigationQueryService navigationQueryService,
IContentTypeService contentTypeService,
IMediaTypeService mediaTypeService,
IMemberTypeService memberTypeService,
IPublishedUrlProvider publishedUrlProvider,
string contentTypeAliasFilter = "",
string? culture = null)
=> content.GenerateDataTable(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeService, mediaTypeService, memberTypeService, publishedUrlProvider, contentTypeAliasFilter, culture);
private static DataTable GenerateDataTable(
this IPublishedContent content,
INavigationQueryService navigationQueryService,
IPublishedStatusFilteringService publishedStatusFilteringService,
IContentTypeService contentTypeService,
IMediaTypeService mediaTypeService,
IMemberTypeService memberTypeService,
IPublishedUrlProvider publishedUrlProvider,
string contentTypeAliasFilter = "",
string? culture = null)
{
IPublishedContent[] children = content.Children(navigationQueryService, publishedStatusFilteringService, culture).ToArray();
IPublishedContent? firstNode = contentTypeAliasFilter.IsNullOrWhiteSpace()
? children.Length > 0
? children.ElementAt(0)
: null
: children.FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAliasFilter));
if (firstNode == null)
{
// No children found
return new DataTable();
}
// use new utility class to create table so that we don't have to maintain code in many places, just one
DataTable dt = DataTableExtensions.GenerateDataTable(
// pass in the alias of the first child node since this is the node type we're rendering headers for
firstNode.ContentType.Alias,
// pass in the callback to extract the Dictionary<string, string> of all defined aliases to their names
alias => GetPropertyAliasesAndNames(contentTypeService, mediaTypeService, memberTypeService, alias),
() =>
{
// here we pass in a callback to populate the datatable, yup its a bit ugly but it's already legacy and we just want to maintain code in one place.
// create all row data
List<Tuple<IEnumerable<KeyValuePair<string, object?>>, IEnumerable<KeyValuePair<string, object?>>>>
tableData = DataTableExtensions.CreateTableData();
IOrderedEnumerable<IPublishedContent>? children =
content.Children(navigationQueryService, publishedStatusFilteringService).OrderBy(x => x.SortOrder);
if (children is not null)
{
// loop through each child and create row data for it
foreach (IPublishedContent n in children)
{
if (contentTypeAliasFilter.IsNullOrWhiteSpace() == false)
{
if (n.ContentType.Alias.InvariantEquals(contentTypeAliasFilter) == false)
{
continue; // skip this one, it doesn't match the filter
}
}
var standardVals = new Dictionary<string, object?>
{
{ "Id", n.Id },
{ "NodeName", n.Name(null, culture) },
{ "NodeTypeAlias", n.ContentType.Alias },
{ "CreateDate", n.CreateDate },
{ "UpdateDate", n.UpdateDate },
{ "CreatorId", n.CreatorId },
{ "WriterId", n.WriterId },
{ "Url", n.Url(publishedUrlProvider) },
};
var userVals = new Dictionary<string, object?>();
IEnumerable<IPublishedProperty> properties =
n.Properties?.Where(p => p.GetSourceValue() is not null) ??
Array.Empty<IPublishedProperty>();
foreach (IPublishedProperty p in properties)
{
// probably want the "object value" of the property here...
userVals[p.Alias] = p.GetValue();
}
// Add the row data
DataTableExtensions.AddRowData(tableData, standardVals, userVals);
}
}
return tableData;
});
return dt;
}
#endregion
#region PropertyAliasesAndNames
private static Func<IContentTypeService, IMediaTypeService, IMemberTypeService, string, Dictionary<string, string>>? _getPropertyAliasesAndNames;
@@ -3142,17 +3011,6 @@ public static class PublishedContentExtensions
where T : class, IPublishedContent
=> content.Children<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture);
[Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")]
public static DataTable ChildrenAsTable(
this IPublishedContent content,
IVariationContextAccessor variationContextAccessor,
IContentTypeService contentTypeService,
IMediaTypeService mediaTypeService,
IMemberTypeService memberTypeService,
IPublishedUrlProvider publishedUrlProvider,
string contentTypeAliasFilter = "",
string? culture = null)
=> content.GenerateDataTable(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeService, mediaTypeService, memberTypeService, publishedUrlProvider, contentTypeAliasFilter, culture);
public static IEnumerable<IPublishedContent> DescendantsOrSelfOfType(
this IEnumerable<IPublishedContent> parentNodes,

View File

@@ -912,7 +912,8 @@ public static class StringExtensions
return text;
}
public static string OrIfNullOrWhiteSpace(this string input, string alternative) =>
[return: NotNullIfNotNull(nameof(alternative))]
public static string? OrIfNullOrWhiteSpace(this string? input, string? alternative) =>
!string.IsNullOrWhiteSpace(input)
? input
: alternative;

View File

@@ -1,55 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Globalization;
namespace Umbraco.Extensions;
public static class ThreadExtensions
{
[Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")]
public static void SanitizeThreadCulture(this Thread thread)
{
// get the current culture
CultureInfo currentCulture = CultureInfo.CurrentCulture;
// at the top of any culture should be the invariant culture - find it
// doing an .Equals comparison ensure that we *will* find it and not loop
// endlessly
CultureInfo invariantCulture = currentCulture;
while (invariantCulture.Equals(CultureInfo.InvariantCulture) == false)
{
invariantCulture = invariantCulture.Parent;
}
// now that invariant culture should be the same object as CultureInfo.InvariantCulture
// yet for some reasons, sometimes it is not - and this breaks anything that loops on
// culture.Parent until a reference equality to CultureInfo.InvariantCulture. See, for
// example, the following code in PerformanceCounterLib.IsCustomCategory:
//
// CultureInfo culture = CultureInfo.CurrentCulture;
// while (culture != CultureInfo.InvariantCulture)
// {
// library = GetPerformanceCounterLib(machine, culture);
// if (library.IsCustomCategory(category))
// return true;
// culture = culture.Parent;
// }
//
// The reference comparisons never succeeds, hence the loop never ends, and the
// application hangs.
//
// granted, that comparison should probably be a .Equals comparison, but who knows
// how many times the framework assumes that it can do a reference comparison? So,
// better fix the cultures.
if (ReferenceEquals(invariantCulture, CultureInfo.InvariantCulture))
{
return;
}
// if we do not have equality, fix cultures by replacing them with a culture with
// the same name, but obtained here and now, with a proper invariant top culture
thread.CurrentCulture = CultureInfo.GetCultureInfo(thread.CurrentCulture.Name);
thread.CurrentUICulture = CultureInfo.GetCultureInfo(thread.CurrentUICulture.Name);
}
}

View File

@@ -26,7 +26,7 @@ public class SerilogLogger : IDisposable
IConfiguration configuration) =>
CreateWithDefaultConfiguration(hostingEnvironment, loggingConfiguration, configuration, out _);
public void Dispose() => SerilogLog.DisposeIfDisposable();
public void Dispose() => (SerilogLog as IDisposable)?.Dispose();
/// <summary>
/// Creates a logger with some pre-defined configuration and remainder from config file

View File

@@ -123,7 +123,7 @@ internal static class UserFactory
if (entity.HasIdentity)
{
dto.Id = entity.Id.SafeCast<int>();
dto.Id = entity.Id;
}
return dto;

View File

@@ -675,28 +675,6 @@ public static class FriendlyPublishedContentExtensions
public static string Url(this IPublishedContent content, string? culture = null, UrlMode mode = UrlMode.Default)
=> content.Url(PublishedUrlProvider, culture, mode);
/// <summary>
/// Gets the children of the content in a DataTable.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="contentTypeAliasFilter">An optional content type alias.</param>
/// <param name="culture">
/// The specific culture to filter for. If null is used the current culture is used. (Default is
/// null)
/// </param>
/// <returns>The children of the content.</returns>
[Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")]
public static DataTable ChildrenAsTable(this IPublishedContent content, string contentTypeAliasFilter = "", string? culture = null)
=> content.ChildrenAsTable(
GetNavigationQueryService(content),
GetPublishedStatusFilteringService(content),
ContentTypeService,
MediaTypeService,
MemberTypeService,
PublishedUrlProvider,
contentTypeAliasFilter,
culture);
/// <summary>
/// Gets the url for a media.
/// </summary>

View File

@@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Web;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core;
using Umbraco.Cms.Web.Common.Constants;
@@ -65,14 +66,7 @@ public class EncryptionHelper
string? additionalRouteValsAsQuery;
if (additionalRouteVals != null)
{
if (additionalRouteVals is Dictionary<string, object> additionalRouteValsAsDictionary)
{
additionalRouteValsAsQuery = additionalRouteValsAsDictionary.ToQueryString();
}
else
{
additionalRouteValsAsQuery = additionalRouteVals.ToDictionary<object>().ToQueryString();
}
additionalRouteValsAsQuery = new RouteValueDictionary(additionalRouteVals).ToQueryString();
}
else
{

View File

@@ -88,7 +88,7 @@ public abstract class UmbracoIntegrationTest : UmbracoIntegrationTestBase
public void TearDownAsync()
{
_host.StopAsync();
Services.DisposeIfDisposable();
(Services as IDisposable)?.Dispose();
}
/// <summary>

View File

@@ -43,7 +43,7 @@ internal sealed class BackOfficeExamineSearcherTests : ExamineBaseTest
// When disposing examine, it does a final write, which ends up locking the file if the indexing is not done yet. So we have this wait to circumvent that.
Thread.Sleep(1500);
// Sometimes we do not dispose all services in time and the test fails because the log file is locked. Resulting in all other tests failing as well
Services.DisposeIfDisposable();
(Services as IDisposable)?.Dispose();
}
private IDocumentUrlService DocumentUrlService => GetRequiredService<IDocumentUrlService>();

View File

@@ -47,7 +47,7 @@ internal sealed class ExamineExternalIndexTests : ExamineBaseTest
// When disposing examine, it does a final write, which ends up locking the file if the indexing is not done yet. So we have this wait to circumvent that.
Thread.Sleep(1500);
// Sometimes we do not dispose all services in time and the test fails because the log file is locked. Resulting in all other tests failing as well
Services.DisposeIfDisposable();
(Services as IDisposable)?.Dispose();
}

View File

@@ -40,24 +40,6 @@ public class ObjectExtensionsTests
Assert.AreEqual(3, result.Result.Count());
}
[Test]
public void ObjectExtensions_Object_To_Dictionary()
{
// Arrange
var obj = new { Key1 = "value1", Key2 = "value2", Key3 = "value3" };
// Act
var d = obj.ToDictionary<string>();
// Assert
Assert.IsTrue(d.Keys.Contains("Key1"));
Assert.IsTrue(d.Keys.Contains("Key2"));
Assert.IsTrue(d.Keys.Contains("Key3"));
Assert.AreEqual(d["Key1"], "value1");
Assert.AreEqual(d["Key2"], "value2");
Assert.AreEqual(d["Key3"], "value3");
}
[Test]
public void CanConvertIntToNullableInt()
{

View File

@@ -1,46 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using Lucene.Net.Index;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Extensions;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core;
[TestFixture]
public class DelegateExtensionsTests
{
[Test]
public void Only_Executes_Specific_Count()
{
const int maxTries = 5;
var totalTries = 0;
DelegateExtensions.RetryUntilSuccessOrMaxAttempts(
currentTry =>
{
totalTries = currentTry;
return Attempt<IndexWriter>.Fail();
},
5,
TimeSpan.FromMilliseconds(10));
Assert.AreEqual(maxTries, totalTries);
}
[Test]
public void Quits_On_Success_Count()
{
var totalTries = 0;
DelegateExtensions.RetryUntilSuccessOrMaxAttempts(
currentTry =>
{
totalTries = currentTry;
return totalTries == 2 ? Attempt<string>.Succeed() : Attempt<string>.Fail();
},
5,
TimeSpan.FromMilliseconds(10));
Assert.AreEqual(2, totalTries);
}
}

View File

@@ -1,196 +0,0 @@
// using System.Collections.Generic;
// using System.Linq;
// using Moq;
// using NUnit.Framework;
// using Umbraco.Cms.Core.Models;
// using Umbraco.Cms.Core.Routing;
// using Umbraco.Cms.Core.Services;
// using Umbraco.Cms.Infrastructure.PublishedCache;
// using Umbraco.Cms.Tests.Common.Builders;
// using Umbraco.Cms.Tests.Common.Builders.Extensions;
// using Umbraco.Cms.Tests.UnitTests.TestHelpers;
// using Umbraco.Extensions;
//
// namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache;
//
// FIXME: Reintroduce if relevant
// /// <summary>
// /// Unit tests for IPublishedContent and extensions
// /// </summary>
// [TestFixture]
// public class PublishedContentDataTableTests : PublishedSnapshotServiceTestBase
// {
// private readonly DataType[] _dataTypes = GetDefaultDataTypes();
//
// private static ContentType CreateContentType(string name, IDataType dataType, IReadOnlyDictionary<string, string> propertyAliasesAndNames)
// {
// var contentType = new ContentType(TestHelper.ShortStringHelper, -1)
// {
// Alias = name,
// Name = name,
// Key = Guid.NewGuid(),
// Id = name.GetHashCode(),
// };
// foreach (var prop in propertyAliasesAndNames)
// {
// contentType.AddPropertyType(new PropertyType(TestHelper.ShortStringHelper, dataType, prop.Key)
// {
// Name = prop.Value,
// });
// }
//
// return contentType;
// }
//
// private IEnumerable<ContentNodeKit> CreateCache(
// bool createChildren,
// IDataType dataType,
// out ContentType[] contentTypes)
// {
// var result = new List<ContentNodeKit>();
// var valueCounter = 1;
// var parentId = 3;
//
// var properties = new Dictionary<string, string> { ["property1"] = "Property 1", ["property2"] = "Property 2" };
//
// var parentContentType = CreateContentType(
// "Parent",
// dataType,
// new Dictionary<string, string>(properties) { ["property3"] = "Property 3" });
// var childContentType = CreateContentType(
// "Child",
// dataType,
// new Dictionary<string, string>(properties) { ["property4"] = "Property 4" });
// var child2ContentType = CreateContentType(
// "Child2",
// dataType,
// new Dictionary<string, string>(properties) { ["property4"] = "Property 4" });
//
// contentTypes = new[] { parentContentType, childContentType, child2ContentType };
//
// var parentData = new ContentDataBuilder()
// .WithName("Page" + Guid.NewGuid())
// .WithProperties(new PropertyDataBuilder()
// .WithPropertyData("property1", "value" + valueCounter)
// .WithPropertyData("property2", "value" + (valueCounter + 1))
// .WithPropertyData("property3", "value" + (valueCounter + 2))
// .Build())
// .Build();
//
// var parent = ContentNodeKitBuilder.CreateWithContent(
// parentContentType.Id,
// parentId,
// $"-1,{parentId}",
// draftData: parentData,
// publishedData: parentData);
//
// result.Add(parent);
//
// if (createChildren)
// {
// for (var i = 0; i < 3; i++)
// {
// valueCounter += 3;
// var childId = parentId + i + 1;
//
// var childData = new ContentDataBuilder()
// .WithName("Page" + Guid.NewGuid())
// .WithProperties(new PropertyDataBuilder()
// .WithPropertyData("property1", "value" + valueCounter)
// .WithPropertyData("property2", "value" + (valueCounter + 1))
// .WithPropertyData("property4", "value" + (valueCounter + 2))
// .Build())
// .Build();
//
// var child = ContentNodeKitBuilder.CreateWithContent(
// i > 0 ? childContentType.Id : child2ContentType.Id,
// childId,
// $"-1,{parentId},{childId}",
// i,
// draftData: childData,
// publishedData: childData);
//
// result.Add(child);
// }
// }
//
// return result;
// }
//
// [Test]
// public void To_DataTable()
// {
// var cache = CreateCache(true, _dataTypes[0], out var contentTypes);
// InitializedCache(cache, contentTypes, _dataTypes);
//
// var snapshot = GetPublishedSnapshot();
// var root = snapshot.Content.GetAtRoot().First();
//
// var dt = root.ChildrenAsTable(
// VariationContextAccessor,
// ContentTypeService,
// MediaTypeService,
// Mock.Of<IMemberTypeService>(),
// Mock.Of<IPublishedUrlProvider>());
//
// Assert.AreEqual(11, dt.Columns.Count);
// Assert.AreEqual(3, dt.Rows.Count);
// Assert.AreEqual("value4", dt.Rows[0]["Property 1"]);
// Assert.AreEqual("value5", dt.Rows[0]["Property 2"]);
// Assert.AreEqual("value6", dt.Rows[0]["Property 4"]);
// Assert.AreEqual("value7", dt.Rows[1]["Property 1"]);
// Assert.AreEqual("value8", dt.Rows[1]["Property 2"]);
// Assert.AreEqual("value9", dt.Rows[1]["Property 4"]);
// Assert.AreEqual("value10", dt.Rows[2]["Property 1"]);
// Assert.AreEqual("value11", dt.Rows[2]["Property 2"]);
// Assert.AreEqual("value12", dt.Rows[2]["Property 4"]);
// }
//
// [Test]
// public void To_DataTable_With_Filter()
// {
// var cache = CreateCache(true, _dataTypes[0], out var contentTypes);
// InitializedCache(cache, contentTypes, _dataTypes);
//
// var snapshot = GetPublishedSnapshot();
// var root = snapshot.Content.GetAtRoot().First();
//
// var dt = root.ChildrenAsTable(
// VariationContextAccessor,
// ContentTypeService,
// MediaTypeService,
// Mock.Of<IMemberTypeService>(),
// Mock.Of<IPublishedUrlProvider>(),
// "Child");
//
// Assert.AreEqual(11, dt.Columns.Count);
// Assert.AreEqual(2, dt.Rows.Count);
// Assert.AreEqual("value7", dt.Rows[0]["Property 1"]);
// Assert.AreEqual("value8", dt.Rows[0]["Property 2"]);
// Assert.AreEqual("value9", dt.Rows[0]["Property 4"]);
// Assert.AreEqual("value10", dt.Rows[1]["Property 1"]);
// Assert.AreEqual("value11", dt.Rows[1]["Property 2"]);
// Assert.AreEqual("value12", dt.Rows[1]["Property 4"]);
// }
//
// [Test]
// public void To_DataTable_No_Rows()
// {
// var cache = CreateCache(false, _dataTypes[0], out var contentTypes);
// InitializedCache(cache, contentTypes, _dataTypes);
//
// var snapshot = GetPublishedSnapshot();
// var root = snapshot.Content.GetAtRoot().First();
//
// var dt = root.ChildrenAsTable(
// VariationContextAccessor,
// ContentTypeService,
// MediaTypeService,
// Mock.Of<IMemberTypeService>(),
// Mock.Of<IPublishedUrlProvider>());
//
// // will return an empty data table
// Assert.AreEqual(0, dt.Columns.Count);
// Assert.AreEqual(0, dt.Rows.Count);
// }
// }