diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs index 6a1befaad8..9754cb7943 100644 --- a/src/Umbraco.Core/ObjectExtensions.cs +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.CompilerServices; using System.Xml; namespace Umbraco.Core @@ -13,22 +14,32 @@ namespace Umbraco.Core /// Provides object extension methods. /// public static class ObjectExtensions - { - //private static readonly ConcurrentDictionary> ObjectFactoryCache = new ConcurrentDictionary>(); + { + // Used for caching the various type lookups + private static readonly Dictionary NullableGenericCache = new Dictionary(); + private static readonly Dictionary InputTypeConverterCache = new Dictionary(); + private static readonly Dictionary DestinationTypeConverterCache = new Dictionary(); + private static readonly Dictionary AssignableTypeCache = new Dictionary(); + private static readonly Dictionary BoolConvertCache = new Dictionary(); - /// - /// - /// - /// - /// - /// - public static IEnumerable AsEnumerableOfOne(this T input) + private static readonly char[] NumberDecimalSeparatorsToNormalize = { '.', ',' }; + private static readonly CustomBooleanTypeConverter CustomBooleanTypeConverter = new CustomBooleanTypeConverter(); + + //private static readonly ConcurrentDictionary> ObjectFactoryCache = new ConcurrentDictionary>(); + + /// + /// + /// + /// + /// + /// + public static IEnumerable AsEnumerableOfOne(this T input) { return Enumerable.Repeat(input, 1); } /// - /// + /// /// /// public static void DisposeIfDisposable(this object input) @@ -51,21 +62,22 @@ namespace Umbraco.Core return default(T); } - /// - /// Tries to convert the input object to the output type using TypeConverters - /// - /// - /// - /// - public static Attempt TryConvertTo(this object input) - { - var result = TryConvertTo(input, typeof(T)); - if (result.Success == false) + /// + /// Attempts to convert the input object to the output type. + /// + /// This code is an optimized version of the original Umbraco method + /// The type to convert to + /// The input. + /// The + public static Attempt TryConvertTo(this object input) + { + Attempt result = TryConvertTo(input, typeof(T)); + if (!result.Success) { - //just try a straight up conversion + // Just try a straight up conversion try { - var converted = (T) input; + var converted = (T)input; return Attempt.Succeed(converted); } catch (Exception e) @@ -73,320 +85,298 @@ namespace Umbraco.Core return Attempt.Fail(e); } } - return result.Success == false ? Attempt.Fail() : Attempt.Succeed((T)result.Result); - } - /// - /// Tries to convert the input object to the output type using TypeConverters. If the destination - /// type is a superclass of the input type, if will use . - /// - /// The input. - /// Type of the destination. - /// - public static Attempt TryConvertTo(this object input, Type destinationType) - { - // if null... - if (input == null) - { - // nullable is ok - if (destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof(Nullable<>)) - return Attempt.Succeed(null); + return Attempt.Succeed((T)result.Result); + } + + /// + /// Attempts to convert the input object to the output type. + /// + /// This code is an optimized version of the original Umbraco method + /// The input. + /// The type to convert to + /// The + public static Attempt TryConvertTo(this object input, Type destinationType) + { + try + { + if (destinationType == null) + { + return Attempt.Fail(); + } - // value type is nok, else can be null, so is ok - return Attempt.SucceedIf(destinationType.IsValueType == false, null); + if (input == null) + { + // Nullable is ok + if (destinationType.IsGenericType && GetCachedGenericNullableType(destinationType) != null) + { + return Attempt.Succeed(null); + } + + // Reference types are ok + return Attempt.SucceedIf(!destinationType.IsValueType, null); + } + + Type inputType = input.GetType(); + + // Easy + if (destinationType == typeof(object) || inputType == destinationType) + { + return Attempt.Succeed(input); + } + + // Check for string so that overloaders of ToString() can take advantage of the conversion. + if (destinationType == typeof(string)) + { + return Attempt.Succeed(input.ToString()); + } + + // If we've got a nullable of something, we try to convert directly to that thing. + // We cache the destination type and underlying nullable types + // Any other generic types need to fall through + if (destinationType.IsGenericType) + { + Type underlyingGenericType = GetCachedGenericNullableType(destinationType); + if (underlyingGenericType != null) + { + // Special case for empty strings for bools/dates which should return null if an empty string. + if (input is string asString) + { + if (string.IsNullOrEmpty(asString) && (underlyingGenericType == typeof(DateTime) || underlyingGenericType == typeof(bool))) + { + return Attempt.Succeed(null); + } + } + + // Recursively call into this method with the inner (not-nullable) type and handle the outcome + Attempt nonNullable = input.TryConvertTo(underlyingGenericType); + + // And if sucessful, fall on through to rewrap in a nullable; if failed, pass on the exception + if (nonNullable.Success) + { + input = nonNullable.Result; // Now fall on through... + } + else + { + return Attempt.Fail(nonNullable.Exception); + } + } + } + else + { + if (input is string asString) + { + // Try convert from string, returns an Attempt if the string could be + // processed (either succeeded or failed), else null if we need to try + // other methods + Attempt? result = TryConvertToFromString(asString, destinationType); + if (result.HasValue) + { + return result.Value; + } + } + + // TODO: Do a check for destination type being IEnumerable and source type implementing IEnumerable with + // the same 'T', then we'd have to find the extension method for the type AsEnumerable() and execute it. + IConvertible convertible = GetCachedAssignableConvertibleResult(input, inputType, destinationType); + if (convertible != null) + { + return Attempt.Succeed(Convert.ChangeType(convertible, destinationType)); + } + } + + if (destinationType == typeof(bool)) + { + if (GetCanConvertToBooleanResult(inputType)) + { + return Attempt.Succeed(CustomBooleanTypeConverter.ConvertFrom(input)); + } + } + + TypeConverter inputConverter = GetCachedInputTypeConverter(inputType, destinationType); + if (inputConverter != null) + { + return Attempt.Succeed(inputConverter.ConvertTo(input, destinationType)); + } + + TypeConverter outputConverter = GetCachedDestinationTypeConverter(inputType, destinationType); + if (outputConverter != null) + { + return Attempt.Succeed(outputConverter.ConvertFrom(input)); + } + + // Re-check convertables since we altered the input through recursion + if (input is IConvertible convertible2) + { + return Attempt.Succeed(Convert.ChangeType(convertible2, destinationType)); + } + } + catch (Exception e) + { + return Attempt.Fail(e); } - // easy - if (destinationType == typeof(object)) return Attempt.Succeed(input); - if (input.GetType() == destinationType) return Attempt.Succeed(input); + return Attempt.Fail(); + } + + /// + /// Attempts to convert the input string to the output type. + /// + /// This code is an optimized version of the original Umbraco method + /// The input. + /// The type to convert to + /// The + private static Attempt? TryConvertToFromString(this string input, Type destinationType) + { + // Easy + if (destinationType == typeof(string)) + { + return Attempt.Succeed(input); + } - // check for string so that overloaders of ToString() can take advantage of the conversion. - if (destinationType == typeof(string)) return Attempt.Succeed(input.ToString()); - - // if we've got a nullable of something, we try to convert directly to that thing. - if (destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - var underlyingType = Nullable.GetUnderlyingType(destinationType); - - //special case for empty strings for bools/dates which should return null if an empty string - var asString = input as string; - if (asString != null && string.IsNullOrEmpty(asString) && (underlyingType == typeof(DateTime) || underlyingType == typeof(bool))) + // Null, empty, whitespaces + if (string.IsNullOrWhiteSpace(input)) + { + if (destinationType == typeof(bool)) { - return Attempt.Succeed(null); + // null/empty = bool false + return Attempt.Succeed(false); } - // recursively call into myself with the inner (not-nullable) type and handle the outcome - var nonNullable = input.TryConvertTo(underlyingType); - - // and if sucessful, fall on through to rewrap in a nullable; if failed, pass on the exception - if (nonNullable.Success) - input = nonNullable.Result; // now fall on through... - else - return Attempt.Fail(nonNullable.Exception); - } - - // we've already dealed with nullables, so any other generic types need to fall through - if (destinationType.IsGenericType == false) - { - if (input is string) - { - // try convert from string, returns an Attempt if the string could be - // processed (either succeeded or failed), else null if we need to try - // other methods - var result = TryConvertToFromString(input as string, destinationType); - if (result.HasValue) return result.Value; - } - - //TODO: Do a check for destination type being IEnumerable and source type implementing IEnumerable with - // the same 'T', then we'd have to find the extension method for the type AsEnumerable() and execute it. - - if (TypeHelper.IsTypeAssignableFrom(destinationType, input.GetType()) - && TypeHelper.IsTypeAssignableFrom(input)) - { - try - { - var casted = Convert.ChangeType(input, destinationType); - return Attempt.Succeed(casted); - } - catch (Exception e) - { - return Attempt.Fail(e); - } - } - } - - var inputConverter = TypeDescriptor.GetConverter(input); - if (inputConverter.CanConvertTo(destinationType)) - { - try - { - var converted = inputConverter.ConvertTo(input, destinationType); - return Attempt.Succeed(converted); - } - catch (Exception e) - { - return Attempt.Fail(e); - } - } - - if (destinationType == typeof(bool)) - { - var boolConverter = new CustomBooleanTypeConverter(); - if (boolConverter.CanConvertFrom(input.GetType())) - { - try - { - var converted = boolConverter.ConvertFrom(input); - return Attempt.Succeed(converted); - } - catch (Exception e) - { - return Attempt.Fail(e); - } - } - } - - var outputConverter = TypeDescriptor.GetConverter(destinationType); - if (outputConverter.CanConvertFrom(input.GetType())) - { - try - { - var converted = outputConverter.ConvertFrom(input); - return Attempt.Succeed(converted); - } - catch (Exception e) - { - return Attempt.Fail(e); - } - } - - if (TypeHelper.IsTypeAssignableFrom(input)) - { - try - { - var casted = Convert.ChangeType(input, destinationType); - return Attempt.Succeed(casted); - } - catch (Exception e) - { - return Attempt.Fail(e); - } - } - - return Attempt.Fail(); - } - - // returns an attempt if the string has been processed (either succeeded or failed) - // returns null if we need to try other methods - private static Attempt? TryConvertToFromString(this string input, Type destinationType) - { - // easy - if (destinationType == typeof(string)) - return Attempt.Succeed(input); - - // null, empty, whitespaces - if (string.IsNullOrWhiteSpace(input)) - { - if (destinationType == typeof(bool)) // null/empty = bool false - return Attempt.Succeed(false); - if (destinationType == typeof(DateTime)) // null/empty = min DateTime value + if (destinationType == typeof(DateTime)) + { + // null/empty = min DateTime value return Attempt.Succeed(DateTime.MinValue); + } - // cannot decide here, - // any of the types below will fail parsing and will return a failed attempt + // Cannot decide here, + // Any of the types below will fail parsing and will return a failed attempt // but anything else will not be processed and will return null - // so even though the string is null/empty we have to proceed - } + // so even though the string is null/empty we have to proceed. + } - // look for type conversions in the expected order of frequency of use... - if (destinationType.IsPrimitive) - { - if (destinationType == typeof(int)) // aka Int32 - { - int value; - if (int.TryParse(input, out value)) return Attempt.Succeed(value); + // Look for type conversions in the expected order of frequency of use. + // + // By using a mixture of ordered if statements and switches we can optimize both for + // fast conditional checking for most frequently used types and the branching + // that does not depend on previous values available to switch statements. + if (destinationType.IsPrimitive) + { + if (destinationType == typeof(int)) + { + if (int.TryParse(input, out int value)) + { + return Attempt.Succeed(value); + } - // because decimal 100.01m will happily convert to integer 100, it + // Because decimal 100.01m will happily convert to integer 100, it // makes sense that string "100.01" *also* converts to integer 100. - decimal value2; - var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt32(value2)); + string input2 = NormalizeNumberDecimalSeparator(input); + return Attempt.SucceedIf(decimal.TryParse(input2, out decimal value2), Convert.ToInt32(value2)); } - if (destinationType == typeof(long)) // aka Int64 - { - long value; - if (long.TryParse(input, out value)) return Attempt.Succeed(value); + if (destinationType == typeof(long)) + { + if (long.TryParse(input, out long value)) + { + return Attempt.Succeed(value); + } - // same as int - decimal value2; - var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt64(value2)); - } + // Same as int + string input2 = NormalizeNumberDecimalSeparator(input); + return Attempt.SucceedIf(decimal.TryParse(input2, out decimal value2), Convert.ToInt64(value2)); + } - // fixme - should we do the decimal trick for short, byte, unsigned? + // TODO: Should we do the decimal trick for short, byte, unsigned? + if (destinationType == typeof(bool)) + { + if (bool.TryParse(input, out bool value)) + { + return Attempt.Succeed(value); + } - if (destinationType == typeof(bool)) // aka Boolean - { - bool value; - if (bool.TryParse(input, out value)) return Attempt.Succeed(value); - // don't declare failure so the CustomBooleanTypeConverter can try + // Don't declare failure so the CustomBooleanTypeConverter can try return null; - } - - if (destinationType == typeof(short)) // aka Int16 - { - short value; - return Attempt.SucceedIf(short.TryParse(input, out value), value); - } - - if (destinationType == typeof(double)) // aka Double - { - double value; - var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.SucceedIf(double.TryParse(input2, out value), value); - } - - if (destinationType == typeof(float)) // aka Single - { - float value; - var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.SucceedIf(float.TryParse(input2, out value), value); - } - - if (destinationType == typeof(char)) // aka Char - { - char value; - return Attempt.SucceedIf(char.TryParse(input, out value), value); - } - - if (destinationType == typeof(byte)) // aka Byte - { - byte value; - return Attempt.SucceedIf(byte.TryParse(input, out value), value); - } - - if (destinationType == typeof(sbyte)) // aka SByte - { - sbyte value; - return Attempt.SucceedIf(sbyte.TryParse(input, out value), value); } - if (destinationType == typeof(uint)) // aka UInt32 + // Calling this method directly is faster than any attempt to cache it. + switch (Type.GetTypeCode(destinationType)) { - uint value; - return Attempt.SucceedIf(uint.TryParse(input, out value), value); - } + case TypeCode.Int16: + return Attempt.SucceedIf(short.TryParse(input, out short value), value); - if (destinationType == typeof(ushort)) // aka UInt16 - { - ushort value; - return Attempt.SucceedIf(ushort.TryParse(input, out value), value); - } + case TypeCode.Double: + string input2 = NormalizeNumberDecimalSeparator(input); + return Attempt.SucceedIf(double.TryParse(input2, out double valueD), valueD); - if (destinationType == typeof(ulong)) // aka UInt64 + case TypeCode.Single: + string input3 = NormalizeNumberDecimalSeparator(input); + return Attempt.SucceedIf(float.TryParse(input3, out float valueF), valueF); + + case TypeCode.Char: + return Attempt.SucceedIf(char.TryParse(input, out char valueC), valueC); + + case TypeCode.Byte: + return Attempt.SucceedIf(byte.TryParse(input, out byte valueB), valueB); + + case TypeCode.SByte: + return Attempt.SucceedIf(sbyte.TryParse(input, out sbyte valueSb), valueSb); + + case TypeCode.UInt32: + return Attempt.SucceedIf(uint.TryParse(input, out uint valueU), valueU); + + case TypeCode.UInt16: + return Attempt.SucceedIf(ushort.TryParse(input, out ushort valueUs), valueUs); + + case TypeCode.UInt64: + return Attempt.SucceedIf(ulong.TryParse(input, out ulong valueUl), valueUl); + } + } + else if (destinationType == typeof(Guid)) + { + return Attempt.SucceedIf(Guid.TryParse(input, out Guid value), value); + } + else if (destinationType == typeof(DateTime)) + { + if (DateTime.TryParse(input, out DateTime value)) { - ulong value; - return Attempt.SucceedIf(ulong.TryParse(input, out value), value); - } - } - else if (destinationType == typeof(Guid)) - { - Guid value; - return Attempt.SucceedIf(Guid.TryParse(input, out value), value); - } - else if (destinationType == typeof(DateTime)) - { - DateTime value; - if (DateTime.TryParse(input, out value)) - { - switch (value.Kind) - { - case DateTimeKind.Unspecified: - case DateTimeKind.Utc: + switch (value.Kind) + { + case DateTimeKind.Unspecified: + case DateTimeKind.Utc: return Attempt.Succeed(value); - case DateTimeKind.Local: - return Attempt.Succeed(value.ToUniversalTime()); - default: - throw new ArgumentOutOfRangeException(); - } - } - return Attempt.Fail(); - } - else if (destinationType == typeof(DateTimeOffset)) - { - DateTimeOffset value; - return Attempt.SucceedIf(DateTimeOffset.TryParse(input, out value), value); - } - else if (destinationType == typeof(TimeSpan)) - { - TimeSpan value; - return Attempt.SucceedIf(TimeSpan.TryParse(input, out value), value); - } - else if (destinationType == typeof(decimal)) // aka Decimal - { - decimal value; - var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.SucceedIf(decimal.TryParse(input2, out value), value); - } - else if (destinationType == typeof(Version)) - { - Version value; - return Attempt.SucceedIf(Version.TryParse(input, out value), value); - } - // E_NOTIMPL IPAddress, BigInteger - return null; // we can't decide... - } + case DateTimeKind.Local: + return Attempt.Succeed(value.ToUniversalTime()); - private static readonly char[] NumberDecimalSeparatorsToNormalize = {'.', ','}; + default: + throw new ArgumentOutOfRangeException(); + } + } - private static string NormalizeNumberDecimalSeparator(string s) - { - var normalized = System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]; - return s.ReplaceMany(NumberDecimalSeparatorsToNormalize, normalized); - } + return Attempt.Fail(); + } + else if (destinationType == typeof(DateTimeOffset)) + { + return Attempt.SucceedIf(DateTimeOffset.TryParse(input, out DateTimeOffset value), value); + } + else if (destinationType == typeof(TimeSpan)) + { + return Attempt.SucceedIf(TimeSpan.TryParse(input, out TimeSpan value), value); + } + else if (destinationType == typeof(decimal)) + { + string input2 = NormalizeNumberDecimalSeparator(input); + return Attempt.SucceedIf(decimal.TryParse(input2, out decimal value), value); + } + else if (input != null && destinationType == typeof(Version)) + { + return Attempt.SucceedIf(Version.TryParse(input, out Version value), value); + } + // E_NOTIMPL IPAddress, BigInteger + return null; // We can't decide... + } internal static void CheckThrowObjectDisposed(this IDisposable disposable, bool isDisposed, string objectname) { //TODO: Localise this exception @@ -651,6 +641,115 @@ namespace Umbraco.Core internal static Guid AsGuid(this object value) { return value is Guid ? (Guid) value : Guid.Empty; - } - } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static string NormalizeNumberDecimalSeparator(string s) + { + var normalized = System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]; + return s.ReplaceMany(NumberDecimalSeparatorsToNormalize, normalized); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TypeConverter GetCachedInputTypeConverter(Type inputType, Type destinationType) + { + var key = new TypePair(inputType, destinationType); + if (!InputTypeConverterCache.ContainsKey(key)) + { + TypeConverter converter = TypeDescriptor.GetConverter(inputType); + if (converter.CanConvertTo(destinationType)) + { + InputTypeConverterCache[key] = converter; + return converter; + } + else + { + InputTypeConverterCache[key] = null; + return null; + } + } + + return InputTypeConverterCache[key]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TypeConverter GetCachedDestinationTypeConverter(Type inputType, Type destinationType) + { + var key = new TypePair(inputType, destinationType); + if (!DestinationTypeConverterCache.ContainsKey(key)) + { + TypeConverter converter = TypeDescriptor.GetConverter(destinationType); + if (converter.CanConvertFrom(inputType)) + { + DestinationTypeConverterCache[key] = converter; + return converter; + } + else + { + DestinationTypeConverterCache[key] = null; + return null; + } + } + + return DestinationTypeConverterCache[key]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Type GetCachedGenericNullableType(Type destinationType) + { + if (!NullableGenericCache.ContainsKey(destinationType)) + { + if (destinationType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + Type underlying = Nullable.GetUnderlyingType(destinationType); + NullableGenericCache[destinationType] = underlying; + return underlying; + } + else + { + NullableGenericCache[destinationType] = null; + return null; + } + } + + return NullableGenericCache[destinationType]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static IConvertible GetCachedAssignableConvertibleResult(object input, Type inputType, Type destinationType) + { + var key = new TypePair(inputType, destinationType); + if (!AssignableTypeCache.ContainsKey(key)) + { + if (destinationType.IsAssignableFrom(inputType)) + { + if (input is IConvertible convertable) + { + AssignableTypeCache[key] = convertable; + return convertable; + } + } + else + { + AssignableTypeCache[key] = null; + return null; + } + } + + return AssignableTypeCache[key]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool GetCanConvertToBooleanResult(Type inputType) + { + if (!BoolConvertCache.ContainsKey(inputType)) + { + bool canConvert = CustomBooleanTypeConverter.CanConvertFrom(inputType); + BoolConvertCache[inputType] = canConvert; + return canConvert; + } + + return BoolConvertCache[inputType]; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index 40a0c71778..f74a8c0a59 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -70,7 +70,7 @@ namespace Umbraco.Core.Strings { foreach (var node in _umbracoSettings.RequestHandler.CharCollection) { - if(string.IsNullOrEmpty(node.Char) == false) + if (string.IsNullOrEmpty(node.Char) == false) _urlReplaceCharacters[node.Char] = node.Replacement; } } @@ -109,7 +109,7 @@ namespace Umbraco.Core.Strings private void EnsureNotFrozen() { if (_frozen) - throw new InvalidOperationException("Cannot configure the helper once it is frozen."); + throw new InvalidOperationException("Cannot configure the helper once it is frozen."); } /// @@ -176,7 +176,7 @@ namespace Umbraco.Core.Strings }).WithConfig(CleanStringType.Alias, new Config { PreFilter = ApplyUrlReplaceCharacters, - IsTerm = (c, leading) => leading + IsTerm = (c, leading) => leading ? char.IsLetter(c) // only letters : (char.IsLetterOrDigit(c) || c == '_'), // letter, digit or underscore StringType = CleanStringType.Ascii | CleanStringType.UmbracoCase, @@ -215,12 +215,12 @@ namespace Umbraco.Core.Strings return new Config { PreFilter = PreFilter, - PostFilter = PostFilter, + PostFilter = PostFilter, IsTerm = IsTerm, StringType = StringType, BreakTermsOnUpper = BreakTermsOnUpper, - CutAcronymOnNonUpper = CutAcronymOnNonUpper, - GreedyAcronyms = GreedyAcronyms, + CutAcronymOnNonUpper = CutAcronymOnNonUpper, + GreedyAcronyms = GreedyAcronyms, Separator = Separator }; } @@ -338,9 +338,9 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// Gets the JavaScript code defining client-side short string services. /// public string GetShortStringServicesJavaScript(string controllerPath) - { - return string.Format(SssjsFormat, - _umbracoSettings.Content.ForceSafeAliases ? "true" : "false", controllerPath); + { + return string.Format(SssjsFormat, + _umbracoSettings.Content.ForceSafeAliases ? "true" : "false", controllerPath); } #endregion @@ -467,7 +467,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// Cleans a string. /// /// The text to clean. - /// A flag indicating the target casing and encoding of the string. By default, + /// A flag indicating the target casing and encoding of the string. By default, /// strings are cleaned up to camelCase and Ascii. /// The clean string. /// The string is cleaned in the context of the default culture. @@ -480,7 +480,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// Cleans a string, using a specified separator. /// /// The text to clean. - /// A flag indicating the target casing and encoding of the string. By default, + /// A flag indicating the target casing and encoding of the string. By default, /// strings are cleaned up to camelCase and Ascii. /// The separator. /// The clean string. @@ -494,7 +494,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// Cleans a string in the context of a specified culture. /// /// The text to clean. - /// A flag indicating the target casing and encoding of the string. By default, + /// A flag indicating the target casing and encoding of the string. By default, /// strings are cleaned up to camelCase and Ascii. /// The culture. /// The clean string. @@ -507,7 +507,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// Cleans a string in the context of a specified culture, using a specified separator. /// /// The text to clean. - /// A flag indicating the target casing and encoding of the string. By default, + /// A flag indicating the target casing and encoding of the string. By default, /// strings are cleaned up to camelCase and Ascii. /// The separator. /// The culture. @@ -554,7 +554,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ text = Utf8ToAsciiConverter.ToAsciiString(text); break; case CleanStringType.TryAscii: - const char ESC = (char) 27; + const char ESC = (char)27; var ctext = Utf8ToAsciiConverter.ToAsciiString(text, ESC); if (ctext.Contains(ESC) == false) text = ctext; break; @@ -568,8 +568,8 @@ function validateSafeAlias(input, value, immediate, callback) {{ // apply post-filter if (config.PostFilter != null) - text = config.PostFilter(text); - + text = config.PostFilter(text); + return text; } @@ -839,7 +839,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ { term = term.Substring(i); term.CopyTo(0, output, opos, term.Length); - opos += term.Length; + opos += term.Length; } break; @@ -914,21 +914,22 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// The filtered string. public virtual string ReplaceMany(string text, IDictionary replacements) { - // be safe if (text == null) + { throw new ArgumentNullException("text"); + } + if (replacements == null) + { throw new ArgumentNullException("replacements"); + } - // Have done various tests, implementing my own "super fast" state machine to handle - // replacement of many items, or via regexes, but on short strings and not too - // many replacements (which prob. is going to be our case) nothing can beat this... - // (at least with safe and checked code -- we don't want unsafe/unchecked here) + foreach (KeyValuePair item in replacements) + { + text = text.Replace(item.Key, item.Value); + } - // Note that it will do chained-replacements ie replaced items can be replaced - // in turn by another replacement (ie the order of replacements is important) - - return replacements.Aggregate(text, (current, kvp) => current.Replace(kvp.Key, kvp.Value)); + return text; } /// @@ -940,15 +941,22 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// The filtered string. public virtual string ReplaceMany(string text, char[] chars, char replacement) { - // be safe if (text == null) + { throw new ArgumentNullException("text"); + } + if (chars == null) + { throw new ArgumentNullException("chars"); + } - // see note above + for (int i = 0; i < chars.Length; i++) + { + text = text.Replace(chars[i], replacement); + } - return chars.Aggregate(text, (current, c) => current.Replace(c, replacement)); + return text; } #endregion diff --git a/src/Umbraco.Core/TypePair.cs b/src/Umbraco.Core/TypePair.cs new file mode 100644 index 0000000000..48568c1f94 --- /dev/null +++ b/src/Umbraco.Core/TypePair.cs @@ -0,0 +1,54 @@ +using System; + +namespace Umbraco.Core +{ + /// + /// A lightweight struct for storing a pair of types for caching. + /// + internal struct TypePair : IEquatable + { + /// + /// Initializes a new instance of the struct. + /// + /// The first type + /// The second type + public TypePair(Type type1, Type type2) + { + this.Type1 = type1; + this.Type2 = type2; + } + + /// + /// Gets the first type + /// + public Type Type1 { get; } + + /// + /// Gets the second type + /// + public Type Type2 { get; } + + /// + public bool Equals(TypePair other) + { + return this.Type1 == other.Type1 && this.Type2 == other.Type2; + } + + /// + public override bool Equals(object obj) + { + return (obj is TypePair) && this.Equals((TypePair)obj); + } + + /// + public override int GetHashCode() + { + unchecked + { + int hashCode = this.Type1.GetHashCode(); + hashCode = (hashCode * 397) ^ this.Type2.GetHashCode(); + return hashCode; + } + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 69d4a5cb3f..548788965e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -1531,6 +1531,7 @@ + diff --git a/src/Umbraco.Tests.Benchmarks/App.config b/src/Umbraco.Tests.Benchmarks/App.config deleted file mode 100644 index e29f2348e4..0000000000 --- a/src/Umbraco.Tests.Benchmarks/App.config +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs index 4896a6570a..2503931600 100644 --- a/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs @@ -3,43 +3,24 @@ using System.Collections.Generic; using System.Data.SqlServerCe; using System.IO; using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Diagnostics.Windows; -using BenchmarkDotNet.Jobs; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Migrations.Initial; using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Tests.Benchmarks.Config; using Umbraco.Tests.TestHelpers; using ILogger = Umbraco.Core.Logging.ILogger; namespace Umbraco.Tests.Benchmarks { - [Config(typeof(Config))] + [QuickRunWithMemoryDiagnoser] public class BulkInsertBenchmarks { - private class Config : ManualConfig - { - public Config() - { - Add(new MemoryDiagnoser()); - //Add(ExecutionValidator.FailOnError); - - //The 'quick and dirty' settings, so it runs a little quicker - // see benchmarkdotnet FAQ - Add(Job.Default - .WithLaunchCount(1) // benchmark process will be launched only once - .WithIterationTime(100) // 100ms per iteration - .WithWarmupCount(3) // 3 warmup iteration - .WithTargetCount(3)); // 3 target iteration - } - } - private static byte[] _initDbBytes = null; - [Setup] + [GlobalSetup] public void Setup() { var logger = new DebugDiagnosticsLogger(); @@ -51,8 +32,8 @@ namespace Umbraco.Tests.Benchmarks SetupSqlCe(path, logger); SetupSqlServer(logger); - - } + + } private void SetupSqlServer(ILogger logger) { @@ -75,7 +56,7 @@ namespace Umbraco.Tests.Benchmarks [lastNotifiedDate] [datetime] NOT NULL, [isActive] [bit] NOT NULL, [isMaster] [bit] NOT NULL, - CONSTRAINT [PK_umbracoServer] PRIMARY KEY CLUSTERED + CONSTRAINT [PK_umbracoServer] PRIMARY KEY CLUSTERED ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] @@ -87,7 +68,7 @@ namespace Umbraco.Tests.Benchmarks var dbName = string.Concat("Umb", Guid.NewGuid(), ".sdf"); AppDomain.CurrentDomain.SetData("DataDirectory", path); var sqlCeConnectionString = $"Datasource=|DataDirectory|\\{dbName};Flush Interval=1;"; - + _dbFile = Path.Combine(path, dbName); //only create the db one time @@ -107,12 +88,12 @@ namespace Umbraco.Tests.Benchmarks var creation = new DatabaseSchemaCreation(_dbSqlCe, logger, _sqlCeSyntax); creation.InitializeDatabaseSchema(); } - _initDbBytes = File.ReadAllBytes(_dbFile); + _initDbBytes = File.ReadAllBytes(_dbFile); } else { File.WriteAllBytes(_dbFile, _initDbBytes); - } + } //create the db _dbSqlCe = new UmbracoDatabase( @@ -138,7 +119,7 @@ namespace Umbraco.Tests.Benchmarks return data; } - [Cleanup] + [GlobalCleanup] public void Cleanup() { _dbSqlCe.Dispose(); diff --git a/src/Umbraco.Tests.Benchmarks/Config/QuickRunAttribute.cs b/src/Umbraco.Tests.Benchmarks/Config/QuickRunAttribute.cs new file mode 100644 index 0000000000..c06932aa2e --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/Config/QuickRunAttribute.cs @@ -0,0 +1,25 @@ +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Horology; +using BenchmarkDotNet.Jobs; +using System; + +namespace Umbraco.Tests.Benchmarks.Config +{ + /// + /// Configures the benchmark to run with less warmup and a shorter iteration time than the standard benchmark. + /// + public class QuickRunAttribute : Attribute, IConfigSource + { + public QuickRunAttribute() + { + Config = ManualConfig.CreateEmpty() + .With(Job.Default.WithLaunchCount(1) // benchmark process will be launched only once + .WithIterationTime(new TimeInterval(100, TimeUnit.Millisecond)) // 100ms per iteration + .WithWarmupCount(3) // 3 warmup iteration + .WithTargetCount(3)); // 3 target iteration + } + + /// + public IConfig Config { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/Config/QuickRunWithMemoryDiagnoserAttribute.cs b/src/Umbraco.Tests.Benchmarks/Config/QuickRunWithMemoryDiagnoserAttribute.cs new file mode 100644 index 0000000000..12e3c9164e --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/Config/QuickRunWithMemoryDiagnoserAttribute.cs @@ -0,0 +1,17 @@ +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; + +namespace Umbraco.Tests.Benchmarks.Config +{ + /// + /// Configures the benchmark to run with less warmup and a shorter iteration time than the standard benchmark. + /// Memory usage diagnosis is included in the benchmark + /// + public class QuickRunWithMemoryDiagnoserAttribute : QuickRunAttribute + { + public QuickRunWithMemoryDiagnoserAttribute() + { + ((ManualConfig)this.Config).Add(new MemoryDiagnoser()); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/LinqCastBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/LinqCastBenchmarks.cs index 4118620515..0c5b3ccd61 100644 --- a/src/Umbraco.Tests.Benchmarks/LinqCastBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/LinqCastBenchmarks.cs @@ -1,25 +1,16 @@ using System.Collections.Generic; using System.Linq; using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Diagnostics.Windows; +using BenchmarkDotNet.Diagnosers; namespace Umbraco.Tests.Benchmarks { /// /// Want to check what is faster OfType or Cast when a enurable all has the same items /// - [Config(typeof(Config))] + [MemoryDiagnoser] public class LinqCastBenchmarks { - private class Config : ManualConfig - { - public Config() - { - Add(new MemoryDiagnoser()); - } - } - public LinqCastBenchmarks() { _array = new List(); diff --git a/src/Umbraco.Tests.Benchmarks/ModelToSqlExpressionHelperBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/ModelToSqlExpressionHelperBenchmarks.cs index 5fa395d2f1..ce6db4c3e2 100644 --- a/src/Umbraco.Tests.Benchmarks/ModelToSqlExpressionHelperBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/ModelToSqlExpressionHelperBenchmarks.cs @@ -1,47 +1,19 @@ using System; -using System.Collections.Generic; -using System.Data.SqlServerCe; -using System.Diagnostics; -using System.IO; using System.Linq.Expressions; -using System.Threading; -using System.Xml; using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Columns; -using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Diagnostics.Windows; -using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Loggers; -using BenchmarkDotNet.Reports; using BenchmarkDotNet.Running; -using BenchmarkDotNet.Validators; using Moq; -using Umbraco.Core; -using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; -using Umbraco.Core.Persistence.Migrations.Initial; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Tests.TestHelpers; -using ILogger = Umbraco.Core.Logging.ILogger; namespace Umbraco.Tests.Benchmarks { - [Config(typeof(Config))] + [MemoryDiagnoser] public class ModelToSqlExpressionHelperBenchmarks { - private class Config : ManualConfig - { - public Config() - { - Add(new MemoryDiagnoser()); - } - } - public ModelToSqlExpressionHelperBenchmarks() { var contentMapper = new ContentMapper(_syntaxProvider); @@ -51,7 +23,7 @@ namespace Umbraco.Tests.Benchmarks mappingResolver.Setup(resolver => resolver.ResolveMapperByType(It.IsAny())).Returns(contentMapper); _mappingResolver = mappingResolver.Object; } - + private readonly ISqlSyntaxProvider _syntaxProvider = new SqlCeSyntaxProvider(); private readonly CachedExpression _cachedExpression; private readonly MappingResolver _mappingResolver; @@ -62,18 +34,15 @@ namespace Umbraco.Tests.Benchmarks for (int i = 0; i < 100; i++) { var a = i; - var b = i*10; + var b = i * 10; Expression> predicate = content => content.Path.StartsWith("-1") && content.Published && (content.ContentTypeId == a || content.ContentTypeId == b); var modelToSqlExpressionHelper = new ModelToSqlExpressionVisitor(_syntaxProvider, _mappingResolver); var result = modelToSqlExpressionHelper.Visit(predicate); } - } - - [Benchmark()] public void WithCachedExpression() { @@ -83,7 +52,7 @@ namespace Umbraco.Tests.Benchmarks var b = i * 10; Expression> predicate = content => content.Path.StartsWith("-1") && content.Published && (content.ContentTypeId == a || content.ContentTypeId == b); - + var modelToSqlExpressionHelper = new ModelToSqlExpressionVisitor(_syntaxProvider, _mappingResolver); //wrap it! @@ -92,6 +61,5 @@ namespace Umbraco.Tests.Benchmarks var result = modelToSqlExpressionHelper.Visit(_cachedExpression); } } - } } \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/Program.cs b/src/Umbraco.Tests.Benchmarks/Program.cs index 2f41642bbf..c9332e7fa3 100644 --- a/src/Umbraco.Tests.Benchmarks/Program.cs +++ b/src/Umbraco.Tests.Benchmarks/Program.cs @@ -6,17 +6,7 @@ namespace Umbraco.Tests.Benchmarks { public static void Main(string[] args) { - var switcher = new BenchmarkSwitcher(new[] - { - typeof(BulkInsertBenchmarks), - typeof(ModelToSqlExpressionHelperBenchmarks), - typeof(XmlBenchmarks), - typeof(LinqCastBenchmarks), - //typeof(DeepCloneBenchmarks), - typeof(XmlPublishedContentInitBenchmarks), - - }); - switcher.Run(args); + new BenchmarkSwitcher(typeof(Program).Assembly).Run(args); } } } diff --git a/src/Umbraco.Tests.Benchmarks/StringReplaceManyBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/StringReplaceManyBenchmarks.cs new file mode 100644 index 0000000000..12f2ffe7b7 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/StringReplaceManyBenchmarks.cs @@ -0,0 +1,103 @@ +using BenchmarkDotNet.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Tests.Benchmarks +{ + [MemoryDiagnoser] + public class StringReplaceManyBenchmarks + { + private static string text = "1,2.3:4&5#6"; + + private static char[] chars = new[] { ',', '.', ':', '&', '#' }; + + private static char replacement = '*'; + + private static Dictionary replacements = new Dictionary(5) + { + {",", "*" }, + {".", "*" }, + {":", "*" }, + {"&", "*" }, + {"#", "*" }, + }; + + [Benchmark(Description = "String.ReplaceMany with Aggregate", Baseline = true)] + public string ReplaceManyAggregate() + { + if (text == null) + { + throw new ArgumentNullException("text"); + } + + if (chars == null) + { + throw new ArgumentNullException("chars"); + } + + string result = text; + return chars.Aggregate(result, (current, c) => current.Replace(c, replacement)); + } + + [Benchmark(Description = "String.ReplaceMany with For Loop")] + public string ReplaceManyForLoop() + { + if (text == null) + { + throw new ArgumentNullException("text"); + } + + if (chars == null) + { + throw new ArgumentNullException("chars"); + } + + string result = text; + for (int i = 0; i < chars.Length; i++) + { + result = result.Replace(chars[i], replacement); + } + + return result; + } + + [Benchmark(Description = "String.ReplaceMany Dictionary with Aggregate")] + public string ReplaceManyDictionaryAggregate() + { + if (text == null) + { + throw new ArgumentNullException("text"); + } + + if (replacements == null) + { + throw new ArgumentNullException("replacements"); + } + + return replacements.Aggregate(text, (current, kvp) => current.Replace(kvp.Key, kvp.Value)); + } + + [Benchmark(Description = "String.ReplaceMany Dictionary with For Each")] + public string ReplaceManyDictionaryForEach() + { + if (text == null) + { + throw new ArgumentNullException("text"); + } + + if (replacements == null) + { + throw new ArgumentNullException("replacements"); + } + + string result = text; + foreach (KeyValuePair item in replacements) + { + result = result.Replace(item.Key, item.Value); + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/TryConvertToBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TryConvertToBenchmarks.cs new file mode 100644 index 0000000000..57b47dc1d0 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/TryConvertToBenchmarks.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using BenchmarkDotNet.Attributes; +using Umbraco.Core; + +namespace Umbraco.Tests.Benchmarks +{ + [MemoryDiagnoser] + public class TryConvertToBenchmarks + { + private static readonly List List = new List() { "hello", "world", "awesome" }; + private static readonly string Date = "Saturday 10, November 2012"; + + [Benchmark(Description = "List to IEnumerable")] + public IEnumerable TryConvertToEnumerable() + { + return List.TryConvertTo>().Result; + } + + [Benchmark(Description = "Int to Double")] + public double TryConvertToDouble() + { + return 1.TryConvertTo().Result; + } + + [Benchmark(Description = "Float to Decimal")] + public decimal TryConvertToDecimal() + { + return 1F.TryConvertTo().Result; + } + + [Benchmark(Description = "String to Boolean")] + public bool TryConvertToBoolean() + { + return "1".TryConvertTo().Result; + } + + [Benchmark(Description = "String to DateTime")] + public DateTime TryConvertToDateTime() + { + return Date.TryConvertTo().Result; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 6cad6893f5..11507d825a 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -9,11 +9,12 @@ Properties Umbraco.Tests.Benchmarks Umbraco.Tests.Benchmarks - v4.5 + v4.6 512 + true AnyCPU @@ -38,38 +39,55 @@ Always - - ..\packages\BenchmarkDotNet.0.9.9\lib\net45\BenchmarkDotNet.dll + + ..\packages\BenchmarkDotNet.0.10.12\lib\net46\BenchmarkDotNet.dll - - ..\packages\BenchmarkDotNet.Core.0.9.9\lib\net45\BenchmarkDotNet.Core.dll + + ..\packages\BenchmarkDotNet.Core.0.10.12\lib\net46\BenchmarkDotNet.Core.dll ..\packages\BenchmarkDotNet.Diagnostics.Windows.0.9.9\lib\net45\BenchmarkDotNet.Diagnostics.Windows.dll - - ..\packages\BenchmarkDotNet.Toolchains.Roslyn.0.9.9\lib\net45\BenchmarkDotNet.Toolchains.Roslyn.dll + + ..\packages\BenchmarkDotNet.Toolchains.Roslyn.0.10.12\lib\net46\BenchmarkDotNet.Toolchains.Roslyn.dll ..\packages\Castle.Core.4.0.0\lib\net45\Castle.Core.dll - - ..\packages\Microsoft.CodeAnalysis.Common.1.3.2\lib\net45\Microsoft.CodeAnalysis.dll + + ..\packages\Microsoft.CodeAnalysis.Common.2.4.0\lib\netstandard1.3\Microsoft.CodeAnalysis.dll - - ..\packages\Microsoft.CodeAnalysis.CSharp.1.3.2\lib\net45\Microsoft.CodeAnalysis.CSharp.dll + + ..\packages\Microsoft.CodeAnalysis.CSharp.2.4.0\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.dll ..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.1.0.41\lib\net40\Microsoft.Diagnostics.Tracing.TraceEvent.dll + + ..\packages\Microsoft.DotNet.InternalAbstractions.1.0.0\lib\net451\Microsoft.DotNet.InternalAbstractions.dll + + + ..\packages\Microsoft.DotNet.PlatformAbstractions.1.1.1\lib\net451\Microsoft.DotNet.PlatformAbstractions.dll + + + ..\packages\Microsoft.Win32.Registry.4.3.0\lib\net46\Microsoft.Win32.Registry.dll + ..\packages\Moq.4.1.1309.0919\lib\net40\Moq.dll - - ..\packages\System.Collections.Immutable.1.2.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll + True + + + ..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + True + + ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll + ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll @@ -77,13 +95,52 @@ ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll + + ..\packages\System.Diagnostics.FileVersionInfo.4.3.0\lib\net46\System.Diagnostics.FileVersionInfo.dll + + + ..\packages\System.Diagnostics.StackTrace.4.3.0\lib\net46\System.Diagnostics.StackTrace.dll + + + ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll + True + + + ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll + + + ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll + - - ..\packages\System.Reflection.Metadata.1.3.0\lib\portable-net45+win8\System.Reflection.Metadata.dll + + ..\packages\System.Reflection.Metadata.1.4.2\lib\portable-net45+win8\System.Reflection.Metadata.dll - - ..\packages\System.Threading.Tasks.Extensions.4.0.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll + + ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net46\System.Security.Cryptography.Algorithms.dll + True + + + ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll + + + ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll + True + + + ..\packages\System.Text.Encoding.CodePages.4.3.0\lib\net46\System.Text.Encoding.CodePages.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.3.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.Threading.Thread.4.3.0\lib\net46\System.Threading.Thread.dll + + + ..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll @@ -91,20 +148,34 @@ + + ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll + + + ..\packages\System.Xml.XmlDocument.4.3.0\lib\net46\System.Xml.XmlDocument.dll + + + ..\packages\System.Xml.XPath.4.3.0\lib\net46\System.Xml.XPath.dll + + + ..\packages\System.Xml.XPath.XDocument.4.3.0\lib\net46\System.Xml.XPath.XDocument.dll + + + + + - - @@ -120,10 +191,6 @@ Umbraco.Web - - - - diff --git a/src/Umbraco.Tests.Benchmarks/XmlBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/XmlBenchmarks.cs index c12545ec94..c95af1d64a 100644 --- a/src/Umbraco.Tests.Benchmarks/XmlBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/XmlBenchmarks.cs @@ -1,42 +1,19 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Xml; using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Diagnostics.Windows; -using BenchmarkDotNet.Jobs; +using Umbraco.Tests.Benchmarks.Config; namespace Umbraco.Tests.Benchmarks { - [Config(typeof(Config))] + [QuickRunWithMemoryDiagnoser] public class XmlBenchmarks { - private class Config : ManualConfig - { - public Config() - { - Add(new MemoryDiagnoser()); - //Add(ExecutionValidator.FailOnError); - - //The 'quick and dirty' settings, so it runs a little quicker - // see benchmarkdotnet FAQ - Add(Job.Default - .WithLaunchCount(1) // benchmark process will be launched only once - .WithIterationTime(100) // 100ms per iteration - .WithWarmupCount(3) // 3 warmup iteration - .WithTargetCount(3)); // 3 target iteration - } - } - - [Setup] + [GlobalSetup] public void Setup() { var templateId = 0; var xmlText = @" - @@ -49,7 +26,7 @@ namespace Umbraco.Tests.Benchmarks 1 This is some content]]> - + @@ -70,7 +47,7 @@ namespace Umbraco.Tests.Benchmarks _xml.LoadXml(xmlText); } - [Cleanup] + [GlobalCleanup] public void Cleanup() { _xml = null; @@ -90,7 +67,7 @@ namespace Umbraco.Tests.Benchmarks public void XmlWithNavigation() { var elt = _xml.DocumentElement; - var id = NavigateElementRoute(elt, new[] {"home", "sub1", "sub2"}); + var id = NavigateElementRoute(elt, new[] { "home", "sub1", "sub2" }); if (id <= 0) Console.WriteLine("ERR"); } @@ -120,4 +97,4 @@ namespace Umbraco.Tests.Benchmarks return found ? int.Parse(elt.GetAttribute("id")) : -1; } } -} +} \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs index e26022d8e7..9113c6e6f4 100644 --- a/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs @@ -3,37 +3,17 @@ using System.Collections.Generic; using System.Linq; using System.Xml; using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Diagnostics.Windows; -using BenchmarkDotNet.Jobs; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Tests.Benchmarks.Config; using Umbraco.Web.PublishedCache.XmlPublishedCache; namespace Umbraco.Tests.Benchmarks { - [Config(typeof(Config))] + [QuickRunWithMemoryDiagnoser] public class XmlPublishedContentInitBenchmarks { - private class Config : ManualConfig - { - public Config() - { - Add(new MemoryDiagnoser()); - - //The 'quick and dirty' settings, so it runs a little quicker - // see benchmarkdotnet FAQ - Add(Job.Default - .WithLaunchCount(1) // benchmark process will be launched only once - .WithIterationTime(100) // 100ms per iteration - .WithWarmupCount(3) // 3 warmup iteration - .WithTargetCount(3)); // 3 target iteration - } - } - public XmlPublishedContentInitBenchmarks() { _xml10 = Build(10); @@ -281,7 +261,7 @@ namespace Umbraco.Tests.Benchmarks var nodes = xmlNode.SelectNodes(dataXPath); contentType = GetPublishedContentType(PublishedItemType.Content, docTypeAlias); - + var propertyNodes = new Dictionary(); if (nodes != null) foreach (XmlNode n in nodes) @@ -307,8 +287,8 @@ namespace Umbraco.Tests.Benchmarks private static PublishedContentType GetPublishedContentType(PublishedItemType type, string alias) { - return new PublishedContentType(alias, new string[] {}, - new List(Enumerable.Range(0, 10).Select(x => new PublishedPropertyType("prop" + x, 0, "test", initConverters:false)))); + return new PublishedContentType(alias, new string[] { }, + new List(Enumerable.Range(0, 10).Select(x => new PublishedPropertyType("prop" + x, 0, "test", initConverters: false)))); } } diff --git a/src/Umbraco.Tests.Benchmarks/packages.config b/src/Umbraco.Tests.Benchmarks/packages.config index ae552b8cd5..82ed3b4bf2 100644 --- a/src/Umbraco.Tests.Benchmarks/packages.config +++ b/src/Umbraco.Tests.Benchmarks/packages.config @@ -1,40 +1,61 @@  - - + + - + - - + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Tests/ObjectExtensionsTests.cs b/src/Umbraco.Tests/ObjectExtensionsTests.cs index c9d90e342b..a2d27b4181 100644 --- a/src/Umbraco.Tests/ObjectExtensionsTests.cs +++ b/src/Umbraco.Tests/ObjectExtensionsTests.cs @@ -161,9 +161,9 @@ namespace Umbraco.Tests var obj = new MyTestObject(); var result = obj.TryConvertTo(); - Assert.AreEqual(obj, result.Result); + Assert.AreEqual(obj, result.Result); } - + private CultureInfo savedCulture; ///